pwnable.tw CTF "Start"

This post is a solution to pwnable.tw‘s CTF “Start” challenge. If you’re planning to solve it yourself, please don’t cheat.

How the challenge works

  • After solving a challenge, the flag is submitted.
  • Flags can usually be found in /home/<challenge_name>/flag.
  • Most challenges run on Ubuntu 16.04 docker containers.
  • After analysis of the binary, you connect to chall.pwnable.tw on a port the changes per challenge - and run the exploit to get the flag.

“Start”

This challenge is a small (elf) executable, it’s length is only 564 bytes.

Here is it’s disassembly:

start.asm

As you can see, there are 5 pushes of constant numbers to the stack (0x0804806e-0x08048082). Each number contains bytes that can be ascii characters. After decoding, we get Let's start the CTF:.

The string is written to stdout directly using syscalls (syscalls are int 0x80 for Linux OSs).

A bit about syscalls

Syscalls in Linux take eax as the syscall number, ebx, ecx, edx, etc… as parameters. The return code is stored in eax, all other registers remain the same.

Syscalls in this binary:

eax (syscall #)nameebxecxedx
4writefdbuffercount
3readfdbuffersize

Immediatly after writing “Let’s start the CTF:", the program wait for 0x3C (=60) bytes of input and writes it to $esp - At this point $esp is pointing to the beginning of the printed string.

Since the stack itself is 20 bytes long, we can control the return address!

Since ASLR is enabled, we must first leak the stack’s address.

Leaking the stack’s address

In order to print the string the code used $esp as a parameter for write(1, $esp, 4). This is great, because it means we can leak $esp. Leaking $esp is done by setting the return address to 0x08048087. This will cause the program to write 20 bytes of which the first 4 will contain $esp’s value.

c.send('x' * 20 + pack('<I', 0x08048087))
esp = unpack('<I', c.recv(0x100)[:4])[0]

After sending the above payload, we will get $esp and the program will be expecting another (up to) 60 byte input.

Now that we have $esp, we can use ragg2 -i exec -b32 to generate a shellcode. The shellcode’s input follows the shellcode.

Full solution