← ALL WRITEUPS

RESEARCHNODE / WRITEUP

legacy dctf 2026 writeup

DefCamp D-CTF Legacy writeup covering a localhost-only root note service, unsafe raw note uploads, a Python 2 JSON parsing DoS, cron-triggered gcore memory dumps, root password recovery from process memory, and final privilege escalation via SUID su.

privesclinux

legacy writeup

recon

low-priv ssh (`sys4dmin:adminpass`), and a legacy note-taking app listening only on `127.0.0.1:8080`. goal is privesc to root.

no sudo on the box (not even installed), but `ps aux` shows the app running as root:

python2 + BaseHTTPServer, runnin as root. working dir /var/run/s3cr3t_py_d1r/ holds three root-owned files: webapp.py (the note app), backup.py (backup helper), ‘backup.sh’ (wrapper around backup.py)

the webapp

it’s a markdown notes app, notes stored as json in /app/notes. two things matter.

single-session lock: a global _active_token gives the first client a cookie, everyone else gets 403. so step zero is grabbing the session first

the bug: /save validates that the body parses to a dict, but /upload writes the raw request body to /app/notes/id.json with no validation. we control the exact bytes of a note file that root’s backup will later read.

the backup chain

cron runs the backup every 2 minutes as root, wrapped in timeout:

exact line from cron : */2 * * * * root cd /tmp && /usr/bin/timeout --signal=QUIT --kill-after=5s 10s /var/run/s3cr3t_py_d1r/backup.sh ...

after 10s timeout sends SIGQUIT to backup.sh, which traps it and runs gcore on the live python process, then leaves the core at 0644 - readable.

meanwhile backup.py loads the root password from /root/.env into memory at the top of main() and holds it for the whole run, then does json.loads() on each note.

the main idea

the box is python2, where parsing a huge decimal string into an int/long is O(n²) with no digit cap. json.loads() over one giant integer hangs for a long time. so we exploit exactly that .

so i upload a note that’s just millions of digits. that’s the whole point

exploitation

grab the session cookie mdsession against 127.0.0.1:8080.

so we write a ton of 9s (3 million) as an exploit:

python -c “open(‘/tmp/payload’,‘w’).write(‘9’*3000000)” curl -s -b /tmp/.mc -X POST --data-binary @/tmp/payload http://127.0.0.1:8080/upload

it gets a low hex id (“5d300476.json”), so it sorts and parses first → instant hang.

wait for the next tick. at t+10s backup.log shows SIGQUIT caught and the dump written, and the core lands at 0644 and gives us the dump:

/tmp/memdump_314.core

we grep the whole file ,no sudo yet, but /bin/su is SUID, so we easily escalate

os.execvp(“su”, [“su”, “-c”, “id; cat /root/flag.txt”, “root”])

uid=0(root) gid=0(root) groups=0(root)

flag drops out. gg

END OF NOTE / dencent444MORE WRITEUPS ↗