Flowise: Remote Code Execution Vulnerability in CSVAgent
A critical RCE vulnerability in Flowise (<= 3.1.2) in the CSVAgent node. The node allows users to supply a customReadCSVFunc for pandas CSV processing, executed inside Pyodide. A denylist blocks dangerous Python constructs (imports, eval, os, subprocess, etc.) but misses pandas.read_pickle(), which deserializes pickle payloads and can execute arbitrary OS commands during unpickling via __reduce__ gadgets. An attacker supplies a customReadCSVFunc that invokes read_pickle() on a base64-encoded pickle payload through a custom MiniBytesIO class (no imports needed), bypassing the denylist entirely. The PoC demonstrates OS command execution via a reverse shell. The prediction API endpoint does not require server-side authentication to trigger (per SecureLayer7 analysis; GHSA CVSS assigns PR:L). Patched in Flowise 3.1.3 via PR #6257, which adds read_pickle and class to forbidden patterns and restricts the field to read_csv() calls only. Discovered by jia-elttam / Raul (Snyk Security Labs).
Research notes
- Root causeThe CSVAgent node's Python denylist (FORBIDDEN_PATTERNS) blocks dangerous builtins, modules, and reflection attributes but does not block pandas.read_pickle(). Python's pickle deserialization can execute arbitrary code during unpickling via __reduce__ gadgets, and pandas.read_pickle() exposes this primitive without hitting any denied patterns.
- Exploit chainThe exploit chain: (1) generate a pickled payload using __reduce__ to call os.system with a reverse shell command, (2) base64-encode the pickle, (3) supply a customReadCSVFunc that defines a MiniBytesIO class and invokes pd.read_pickle(MiniBytesIO(base64.b64decode(payload))) — the custom MiniBytesIO class provides the file-like object interface needed since import and open() are blocked, (4) pickle deserialization executes the OS command, (5) trigger via unauthenticated POST to the prediction API endpoint.
- Technical detailThe denylist includes patterns covering imports, dangerous builtins (eval, exec, compile, open, breakpoint, input, globals, locals, getattr, setattr, delattr, reload, file, execfile), dangerous modules (os, subprocess, sys, socket, urllib, requests), and reflection attributes (__builtins__, __class__, __subclasses__, __bases__, __mro__, __globals__, __code__, __closure__, __dict__, __module__, __loader__, __spec__). None of these match pandas.read_pickle or the pickle module path.