When "validate code" runs the code: unauthenticated RCE in Langflow
An endpoint whose whole job was to check a snippet of Python turned out to run it, and it never asked who was asking. This is CVE-2025-3248, found live on an internet-exposed instance, and the short walk from a validator to a root shell.
Some of the scariest bugs hide behind the most reassuring names. This one lived behind a button called validate code. You would read that and assume it does the safe thing: take a snippet, look it over, tell you whether it is okay. Checking is not running. Except here, checking was running, and the door had no lock on it at all.
The software is Langflow, a popular tool for wiring up AI and language-model workflows by dragging boxes around instead of writing all the glue yourself. People self-host it, and a fair number leave it reachable from the internet. The bug is tracked as CVE-2025-3248, and I ran into a live one during a Comcast vulnerability-disclosure engagement. The target host is not shown here.
The endpoint that ran what it was meant to inspect
Langflow lets you drop small pieces of Python into your flows, so somewhere it needs to
make sure what you typed is valid code. That is what /api/v1/validate/code
is for. The trouble is in how it decided your code was valid. Instead of only
parsing it, the endpoint went ahead and evaluated it. Feed it a snippet and the server
would execute that snippet, on the server, as part of "validating" it.
On its own that might be a locked-room problem, dangerous only to people already logged in. But the second half of the bug is the part that turns it into a real one:
The endpoint was reachable without authentication. So the two failures stack: anyone who could reach the server could send code, and the server would run it. An unauthenticated attacker gets to run Python on the box, and Python that runs on the box can run system commands. That is remote code execution.
Proving it, without wrecking anything
There is a clean, public proof of concept for this CVE, so there was no need to reinvent it. It wraps the whole thing into one command: point it at the target and hand it a shell command to run.
# public PoC: github.com/ynsmroztas/CVE-2025-3248-Langflow-RCE
python3 langflow_rce_unauth.py -u http://<target>/ -c "cat /etc/passwd"
The safest first command is one that proves you are running on the machine while
touching nothing important. Reading /etc/passwd is the classic: it exists
everywhere, it is not a secret, and getting it back means your command genuinely ran on
the far side.
HTTP 200 and the command output. The host is redacted and the file contents are blurred; the point is only that the command ran on the server.Then two more, to answer the only question that really sets the severity: who am I on this machine?
whoami and id. The id output comes back with groups=0(root), so the code is running with root's group.That is the whole finding, and it is enough to stop at. Command execution, from the internet, with no credentials, at root-level privilege. On a box that runs commands for a stranger, everything else, reading files, reaching the internal network, pivoting deeper, is just a matter of typing more commands. The report does not, because proving the door opens does not require walking through and ransacking the house.
Why a "validator" was executing code at all
This is the interesting lesson under the impact. It is genuinely useful for a tool like Langflow to know whether a Python snippet is valid before it saves it into a flow. The cheap way to answer "is this valid Python" is to try to run it and see if it complains. The safe way is to parse it into a syntax tree and inspect that, never executing a line. This endpoint took the cheap way, on untrusted input, from unauthenticated callers. Any one of those three being different and it would not be a critical bug.
The fix
- Update Langflow. The straightforward fix is to run a version where this is patched. If you self-host, this is the first move.
- Never execute untrusted code to validate it. Parse it into an abstract syntax tree and inspect that. Checking whether code is well-formed should never run the code.
- Put a lock on the door. An endpoint that can influence code execution has no business being reachable without authentication, and ideally not from the public internet at all.
- Do not expose admin tooling to the world. Langflow is builder tooling. Keep it behind a VPN or an authenticating proxy, not open on a public port.
Key takeaways
- A reassuring name is not a security control. "Validate" ran the code, and the name is exactly why it is easy to overlook.
- Two ordinary mistakes, evaluating untrusted input and skipping authentication, combine into a critical one. Severity comes from the stack, not either half.
- Prove RCE with the smallest, most harmless command that shows execution:
idandcat /etc/passwdsay everything and break nothing. - Self-hosted builder tools drift onto the public internet constantly. Treat "is this exposed" as its own finding.