legacy

Python 2 parses a 2 MB integer literal in quadratic time, so the backup overruns its timeout and the SIGQUIT trap gcores the root password into /tmp.

2026.09.19 DefCamp CTF 2026 Quals Misc
FLAG CTF{L3g4cy_Syst3ms_H4v3_Fun_Att4cks_D0s_on_python2}

SSH in as sys4dmin/adminpass. No sudo, nothing weird in the suid list, /app/notes is root and I can’t write there. So I went looking at cron and found /etc/cron.d/backup, which runs backup.sh every 2 min under timeout --signal=QUIT --kill-after=5s 10s.

backup.sh is the whole challenge honestly. It launches backup.py in the background and traps SIGQUIT, and the trap runs gcore on the python pid, then chmod 0644 the core into /tmp. And backup.py is python2 that reads /root/.env at the top of main and keeps root_password sitting in a local for the entire run.

So if the backup ever takes more than 10 seconds, timeout fires QUIT and root hands me a world-readable dump with the password in it. No memory corruption needed, just make it slow.

Making it slow

The only thing I control is the json files it parses. And this is python2, where strlong is O(n²), and json will read an integer literal of any length. Tested it on the box:

200k digits  0.77s
400k digits  3.1s
800k digits  12.3s

python3 wouldn’t do this, hence the name.

Getting the file in

Can’t write to /app/notes myself, but the root webapp on 127.0.0.1:8080 can. Its /upload just takes the request body and writes it to disk, no json validation at all, 5 MB cap. One session at a time, so grab the cookie on the first GET.

(/save also writes but then calls _write_config(), which isn’t defined anywhere in the file, so it NameErrors. /upload is fine.)

python -c 'open("/tmp/big.json","w").write("1"*2000000)'
curl -c cookies ...                                    # grab the session cookie
curl -b cookies -X POST --data-binary @/tmp/big.json .../upload

It landed as 0af51c5c.json and since backup.py sorts the filenames the 0 goes first, so the slow parse starts right away.

Result

Waited for the next tick. backup.log said SIGQUIT caught at :11 and dumped at :12, and /tmp/memdump_176.core showed up 0644. No strings binary on the box, so:

grep -a -o -E 'ROOT_PASSWORD=[0-9a-f]+' /tmp/memdump_176.core

and there it is: 3219b1e0c668294cafd1eaf592b3fe2d. su root, cat /root/flag.txt.

#python2#dos#cron#core-dump