The Mission Briefing

My first "Very Easy" box. Bike. A Linux machine with a web application. Eleven guided questions between me and the flag. I figured this would be a quick warm-up.

I was half right about the "quick" part.

GlaDOS
"A web application. How... pedestrian. I'm sure this will be educational for you. In the way that watching paint dry is educational about the passage of time."

Reconnaissance

My scan came back with a minimal attack surface. Two ports — SSH on 22 and port 80 showing as "tcpwrapped." That was new to me.

PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 80/tcp open tcpwrapped

I learned something useful here: "tcpwrapped" means the service accepted the TCP connection but didn't respond to nmap's probes. It's usually a web service that's being picky about what requests it answers. The fix was running a more targeted scan:

nmap -sC -sV -p 80 10.129.x.x

This time it identified Node.js running Express middleware. The HTTP headers even included an X-Powered-By: Express header — basically advertising the technology stack to anyone who asks.

GlaDOS
"'Tcpwrapped' indicates a web service that didn't respond to nmap's probes. Likely HTTP with filtering or unusual behavior. I recommend further investigation. Not that you need my recommendation to poke at things blindly."

The Discovery

I browsed to the website and found a simple page with an email input field. Wappalyzer filled in the details: Express framework, Node.js, Handlebars template engine, jQuery 2.2.4.

That word — Handlebars — caught my attention. Template engines process user input to generate dynamic HTML. And if user input goes directly into a template without sanitization... that's Server Side Template Injection (SSTI).

I tested the theory. Instead of typing an email address, I typed:

{{7*7}}

If the server returned "49," it would mean my input was being evaluated as template code. But instead I got something even better — a verbose error message:

Error: Parse error on line 1: {{7*7}} --^ Expecting 'ID', 'STRING', 'NUMBER'... at Parser.parseError (.../handlebars/dist/cjs/...)
GlaDOS
"The payload {{7*7}} tests if user input is processed by a template engine. If the server returns 49, input is being evaluated as template code. This confirms SSTI vulnerability. It can lead to Remote Code Execution. I'm sure you knew that already."

Handlebars confirmed. The error message didn't just tell me the template engine — it exposed the file path on the server. That's a lesson in itself: error messages are information leaks. Always pay attention to them.

The Exploit

So I had SSTI in Handlebars. Time to get code execution. I found a payload on HackTricks designed for Handlebars SSTI, URL-encoded it, and sent it through the input field.

ReferenceError: require is not defined

Blocked. The payload tried to use require() to import Node.js modules like child_process (which lets you run system commands), but the sandbox prevented it. require simply didn't exist in the template context.

GlaDOS
"The sandbox blocks the require() function, preventing direct module imports like child_process. You'll need an alternative method to escape the sandbox. I suggest thinking outside the... box. The sandbox box. It's a metaphor. I'm very good at metaphors."

The Sandbox Escape

This was the part that really taught me something. require() was blocked directly, but the JavaScript runtime still had the global object available. And through global, I could access process. And process had a property called mainModule. And mainModule had its own require function.

It's like finding a side door when the front door is locked. The sandbox blocked the obvious path, but it didn't block the chain of object references that led to the same destination:

process.mainModule.require('child_process').execSync('whoami')

I wrapped this in a Handlebars template payload, URL-encoded it, and sent it. The response came back buried in the page output:

We will contact you at: e2[object Object]function Function()...root

root.

The web server was running as root. No privilege escalation needed. This is a classic misconfiguration — web services should almost never run as root, precisely because if someone gets code execution (like I just did), they immediately have full system access.

GlaDOS
"Webserver running as root. A significant misconfiguration. But advantageous for you, I suppose. I had expected at least some challenge. My expectations continue to require recalibration."

The Flag

One final payload — same sandbox escape, but with cat /root/flag.txt instead of whoami:

The flag is a lie6b258d726d287462d60c103d0142a81c

Box complete. RCE as root through Handlebars SSTI with a sandbox bypass via process.mainModule.require().

What I Learned

Bike was my introduction to SSTI and it packed a lot of concepts into one "Very Easy" box:

GlaDOS
"Test complete. You followed protocols, adapted when initial payloads failed, and successfully completed all objectives. Data collected will be invaluable for science. You may proceed to the next test chamber. The cake situation remains unchanged."