$ cat writeup.md…
$ cat writeup.md…
sekai2026
Task: attacker controls the AFC device side for libimobiledevice's afc_list client, exposing a real 0day-style heap overflow from inconsistent AFC lengths. Solution: shape tcache, overflow a freed small chunk, poison malloc to free@GOT, write system, and trigger /readflag via free(command).
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
ppp — insert your typical "you might need a 0day for this" description
We are given a pwn challenge with a remote service at:
nc ppp.chals.sekai.team 1337
The provided archive is pwn_ppp.tar.gz, extracted under pwn_ppp/. The interesting binary is afc_list, built from the libimobiledevice stack inside the supplied Docker environment. The challenge gives us the device side of the AFC protocol, while afc_list is the host-side client.
The final exploit abuses a real 0day-style bug in libimobiledevice's AFC receive path. This writeup focuses on the technical issue and the CTF exploit chain.
afc_list is a dynamically linked amd64 ELF with the following relevant properties:
The Dockerfile builds libimobiledevice from commit:
fa0f79190142bc309307967c058f89c1b36eb6b8
and compiles src/afc_list.c. Because we control the device side of the protocol, the attack surface is not command-line parsing in afc_list, but the library code that receives and parses AFC responses from the device.
The bug is in libimobiledevice src/afc.c, in afc_receive_data().
The receive logic reads an AFC header containing both entire_length and this_length. It then computes payload lengths approximately as:
entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket); this_len = (uint32_t)header.this_length - sizeof(AFCPacket); buf = malloc(entire_len); if (this_len > 0) { service_receive(..., buf, this_len, ...); }
The missing check is:
this_len <= entire_len
As a malicious device, we can send a header where entire_length is small enough to allocate a small heap buffer, but this_length is larger. The subsequent service_receive() writes this_len bytes into a malloc(entire_len) allocation, producing a heap overflow.
...
$ grep --similar