This weekend, I participated in the Hack the Vote (pwn.voting) CTF event. Unfortunately, I was only able to solve two challenges in the time allocated (and it looks like these were the easy ones – by the end of the event, many people had solved these).
Before I begin, I would like to extend a special thanks to sy (mewy.pw – now with 100% more epic writeup of Web300) and grc for an epic DPS carry through this CTF. I did sweet fuck all, while these guys got all the hard web and crypto challenges.
As usual, I will present the writeups below:
Pwn100 – IRS
This first binary was presented as a zip file, which you can download here: irs. A quick “file” reveals this to be a32-bit Linux executable.
My first step was to inspect the executable in IDA. This analysis quickly revealed a menu-based interface, which we can confirm by running the executable:
At this point, we can spot a few “tells” about the challenge:
- The ability to “create, edit, delete” often indicates a heap metadata overflow (or simply a structure overflow from one to the next). Typically, this will be in a variable-length field like tax return name in this case.
- Checksec indicates that NX is on: this may require ROP-based exploitation, if we identify a stack overflow.
A little bit of digging through the disassembly reveals our vulnerability:
Quite clearly, we can see a gets() function call saving user-controlled data to the stack. My next step was to craft a rop chain (and by craft a rop chain, I meant run ropper.py and fix by hand): unfortunately, the tool sadly reported a lack of usable gadgets:
Typically, this would indicate that one might have to rely on another technique, such as returning to libc – but this is not necessary in this case. If we continue browsing through the executable, we can quite clearly see that a flag is contained within the executable itself:
From here, it is quite simple to re-use one of the printf calls with a “%s” specifier as a giant rop gadget, returning to the printf call but putting the location of the flag in memory (0x08408AC2) as the argument:
When this gets called, printf pops the format specifier off the stack, realises there’s one parameter, pops the address of the flag off the stack, and prints it to the user. The program probably crashes after that, but we’ve got the flag so who cares. A quick Python script later, and we’re 100 points better off:
Rev 100: consul
Once again, this is presented as an executable: consul. “File” reveals this to be a 64-bit Linux executable, and inspection in IDA reveals a stack of unhelpful function names:
Dynamic analysis at this point isn’t too fruitful: however, we can use GDB to call functions directly. First, we put a breakpoint in the main function (or at _start, or wherever), and from there, we can use call() directly:
However, many other functions don’t work, attempting to jump to zero (the below screenshot is from calling help()). A bit of analysis shows us why:
Back in IDA, if we inspect 0x4006e8, we can see that IDA is trying to call an uninitialized variable. If we look at the cross-references, this appears to be a malloc call, which is initialized by the c55() function. Executing the c55() function, however, does nothing:
Quite clearly, this is a troll function, with no way of increasing var_4. We can patch this out in a hex editor, and try again:
We can see that the other functions all return sensible values, with the exception of c8():
A bit of further investigation shows that c8 modifies a buffer (‘b’ @ 0x601280) in-place, and then prints it. The cipher appears to be addition-based: this means that if we simply take these values and add every number from 0 to 255 to them, one of them should (presumably) print out the flag.
Alternatively, as c8() saves the output in place, you can repeatedly call it for the answer (the way I solved it during the CTF):
Note: web100 (Sanders Fan Club)
During this CTF, we also solved the “web100” challenge. I will present the solution here for posterity, as I think this is interesting. This challenge is presented as a web page:
When we inspect the response in Burp Suite, something immediately stands out:
Typically, retrieving flag2.jpg will simply return a normal JPEG. However, modifying our Accept: header will reveal a stylesheet:
Following the rabbit-hole gets us the flag:
As usual, I would like to thank the RPISEC team for putting together this fantastic event. It is always a humbling experience to get schooled in a CTF, whether it be through lack of devotion or lack of skill: but each crushing defeat also serves as a beacon of progress, showing that there are ever greater heights to aspire to.
See you all next week (hopefully!) in RuCTFE.