
Nuclei template for detecting CVE-2026-33017, an unauthenticated remote code execution vulnerability in Langflow ≤ 1.8.2. Performs non-destructive pre-checks to distinguish vulnerable from patched builds via HTTP response analysis.
Nuclei template that detects CVE-2026-33017 — an unauthenticated remote-code-execution vulnerability in Langflow ≤ 1.8.2 reachable through the public flow-build endpoint:
POST /api/v1/build_public_tmp/{flow_id}/flow
The vulnerable handler accepts an attacker-controlled data flow graph and
passes data.nodes[].data.node.template.code.value straight into Python
exec() during graph build, with no sandboxing. The fix (≥ 1.9.0) drops the
data parameter from the handler signature, so a patched build rejects the
same request at FastAPI's validation layer with HTTP 422.
The template is non-destructive. It sends an inert payload against a
zero-UUID flow ID, so it cannot reach exec() even on a vulnerable host.
It distinguishes the vulnerable handler from the patched one by the HTTP
response signature alone.
| Stage | Request | What it tells you |
|---|---|---|
| 1 | GET /api/v1/version | Confirms the target is Langflow and extracts the build version. |
Routing for stage 2:
A real RCE confirmation still requires (a) a real PUBLIC flow UUID on the target and (b) an out-of-band callback (DNS/HTTP). That step is not included here on purpose — this template is the safe pre-check that decides whether running an active exploit makes sense at all.
Single host:
nuclei -t CVE-2026-33017.yaml -u https://target.example.com
Bulk scan from a list:
nuclei -t CVE-2026-33017.yaml -l targets.txt -rl 20 -c 10
With debug output (see the raw requests/responses):
nuclei -t CVE-2026-33017.yaml -u https://target.example.com -debug-req -debug-resp
Validate the template locally before running it:
nuclei -validate -t CVE-2026-33017.yaml
Vulnerable target:
[CVE-2026-33017] [http] [critical] https://target.example.com [vulnerable-flow-not-found] ["1.8.2","base"]
Patched target — no finding emitted (422 response does not match any rule).
Common discovery queries for Langflow hosts (use only against assets you are authorized to test):
http.title:"Langflow"app="Langflow"intitle:"Langflow" inurl:"/flows"AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HIn vulnerable builds the public handler is declared roughly as:
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_flow(flow_id: UUID, data: FlowDataRequest | None = None, ...):
...
await verify_public_flow_and_get_user(flow_id, ...)
...
# data.nodes[].data.node.template.code.value reaches exec() in the
# Component build path
data is attacker-controlled. The patched handler removes the parameter
entirely and forces data=None, which is why a request that includes a
data body field is rejected with 422 on patched builds.
/api/v1/build_public_tmp/ at
the reverse proxy.LANGFLOW_AUTO_LOGIN=False, configure superuser credentials, and
rotate LANGFLOW_SECRET_KEY.access_type=PUBLIC flows are the ones an
attacker would target in the URL.This template is published for defensive security and authorized testing only. Running it against systems you do not own or do not have explicit written permission to test is illegal in most jurisdictions and violates platform terms of service.
By using this template you agree that:
lopseg — https://github.com/lopseg
Pull requests and issues welcome.
MIT
| 2 | POST /api/v1/build_public_tmp/00000000-0000-0000-0000-000000000000/flow with a body containing the data field and an inert template.code.value | Routing decision below. |
| Status | Body contains | Verdict |
|---|
404 | flow / public | VULNERABLE — handler accepted data, the lookup then failed because the UUID is fake. |
200 | job_id / build / task | VULNERABLE — handler accepted data and entered the build path. |
5xx | exec / Component / traceback | VULNERABLE — handler reached the exec code path before failing. |
422 | Field required / extra / data | PATCHED — handler signature no longer accepts data. |