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.
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 str → long 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.