Brooklynt Overflow Recently Competed in HackIM CTF. This is a writeup of the vuln3 service.
Vuln3 is a service exposed to the Internet via xinetd or something similar. It accepts input from you writes it to the stack, parses it and performs actions based on it. It does this until you disconnect. The important parts of user input are dwords at offset 0x28 and 0x50. The dword at offset 0x28 tell the service what operation to perform. The dword at offset 0x50 is used only when you ask the service to allocate some memory for you, it is then used as the argument to
.text:08048583 lea eax, [esp+230h+safe_stack_buffer_512] .text:08048587 mov eax, [eax+50h] .text:0804858A mov [esp+230h+my_size_50], eax .text:08048591 lea eax, [esp+230h+safe_stack_buffer_512] .text:08048595 mov eax, [eax+28h] .text:08048598 mov [esp+230h+my_verb_28], eax
The service supports four functions that you can control. Before you can control any of the application’s state an allocation of size 0x100 bytes is made with
Allocate a block of size 8 and put two function pointers inside it. The first function pointer points to a function that calls
puts("Hello world") The section is a pointer to a function that calls
puts("Bye bye World").
Allocate a block of a size specified by the dword at offset 50. Call
fgets to populate it. Create a block of arbitrary size who’s contents we control.
Call function pointers and free blocks. Check to make sure that we have allocated the block of size 8 with function pointers inside. If we have, call the function pointers and free the block. Also check to see if we have allocated a block with an arbitrary size and free that too.
Copy the contents as a string from the block of arbitrary size to the block of fixed size that was allocated at the beginning of the program. This will cause a buffer overflow on the heap if the block that we allocate is larger than size 0x100 and doesn’t contain any null bytes.
The vulnerability here is a heap buffer overflow. What we need to do is manipulate the heap state such that the block of size 8 immediately follows the block allocated in the beginning of the program. Then we can allocate a large block to overflow the block with a static size. That overflow will corrupt the function pointers. We can then ask the application to call the function pointers thereby escaping intended control flow.
Choosing a suitable trampoline
Astute hackers will note that we do not control the stack however stack layout is predictable. Right before the function is called the value on the top of the stack is a pointer to a buffer that we control. The pointer is pointing at the stack buffer and because the organizers are nice folks, the stack and heap are executable. That means we have 0x28 bytes to work with for writing shellcode. That’s nice but I was having trouble opening a shell during the competition so I opted for more space. I wrote a small trampoline that punts execution to the heap block that we have arbitrary control over.
The flow of exploitation
Allocate the small block. It will be allocated directly after the block of size 0x100
Allocate a large block with pointers to
pop; retto overwrite the function pointers in the small block.
Copy the large block into the block of size 0x100. If it is large enough it will corrupt the small block.
Next we create another large block that will contain our shellcode. Now we have no shellcode length constraints.
Issue the command to call the pointers and free the block, naturally divert control before the free. Before the padding to the command dword, put the trampoline (call [esp+0x228]) At the time of the call, [esp+0x228] is the pointer to our heap block.
You’re now executing arbitrary code from the heap.
You can find this exploit and many others in the CTF-Solutions repo on github.