← Writeups

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.

A Bugcrowd submission for Remote Code Execution in Langflow (CVE-2025-3248), target category Web App, VRT Server-Side Injection Remote Code Execution, priority P1
The submission: Langflow RCE, Server-Side Injection → Remote Code Execution, priority P1. The report ID and exact host are redacted.

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:

No login required

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.

The public PoC run against the target with the host redacted, returning HTTP 200 and the contents of /etc/passwd, most of the file blurred
The PoC returns 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?

The PoC running whoami and id against the redacted target, returning the user and id output showing groups equal to zero, root
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

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: id and cat /etc/passwd say everything and break nothing.
  • Self-hosted builder tools drift onto the public internet constantly. Treat "is this exposed" as its own finding.
RCE CVE-2025-3248 Langflow Unauthenticated
Share on X

More writeups