# REPRO-2026-00240: Langflow OSS SSRF and privilege escalation ## Summary Status: published Severity: high Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00240 CVE: CVE-2026-10129 ## Package Name: Unknown Ecosystem: Unknown Affected: Unknown Fixed: Unknown ## Root Cause # Root Cause Analysis — CVE-2026-10129 ## Summary CVE-2026-10129 is a Server-Side Request Forgery (SSRF) protection bypass in the Langflow OSS **API Request** component. When SSRF protection is enabled, the component validates the initial URL and pins DNS for that original host, but if `follow_redirects=true` it lets `httpx` automatically follow redirects without re-validating each redirect destination. A low-privileged Langflow flow author can submit a public URL that redirects to `127.0.0.1` or another internal address and receive the internal response through normal Langflow build events. In this run, the SSRF was chained on the real Langflow HTTP API surface to a privilege boundary effect: the low-privileged attacker retrieved a loopback-only internal admin bearer token, used it against Langflow's real admin-only user-management API, promoted their own account, and verified that the original attacker session became a superuser. ## Impact - **Package/component affected:** `lfx.components.data_source.api_request.APIRequestComponent`, used by Langflow OSS as the "API Request" component. - **Affected versions:** IBM reports Langflow OSS 1.0.0 through 1.9.3 as affected. The reproduction installs `langflow==1.9.3`, pins `langflow-base==0.9.3`, and confirms the resolved vulnerable component stack is `lfx==0.4.6` with no `_follow_redirects_with_validation` method. - **Risk level and consequences:** High. An authenticated low-privileged flow author can bypass SSRF protections intended to block loopback/private/link-local destinations. The bypass can expose internal admin panels, metadata services, credentials, or tokens. This run demonstrates a concrete privilege escalation consequence: an internal admin token was obtained through the API Request component and used to promote the attacker account to superuser. ## Impact Parity - **Disclosed/claimed maximum impact:** Privilege escalation through an SSRF protection bypass to an internal/admin endpoint. - **Reproduced impact from this run:** Full production-surface privilege escalation. The script runs a real Langflow 1.9.3 server in multi-user mode, creates a real non-superuser attacker account through `POST /api/v1/users/`, proves that the attacker receives `403` from the admin-only `GET /api/v1/users/` endpoint before exploitation, runs a real API Request flow through `POST /api/v1/build/{flow_id}/flow`, retrieves a loopback-only internal admin bearer token from the flow output, uses that stolen token against real Langflow admin APIs, promotes the attacker with `PATCH /api/v1/users/{attacker_id}`, and verifies the original attacker token now returns `is_superuser=true` from `GET /api/v1/users/whoami`. - **Parity:** `full` for the ticket claim. The validated surface is `api_remote`, and the observed impact is `privilege_escalation`. - **Not demonstrated:** The proof does not attempt arbitrary code execution or cloud metadata compromise; it focuses on the claimed SSRF-to-privilege-escalation chain. ## Root Cause The vulnerable API Request component performs SSRF validation only for the initial user-supplied URL: 1. It parses and validates the URL and resolves the initial hostname. 2. It rejects private, loopback, link-local, and otherwise blocked IP ranges for that initial host. 3. It creates an SSRF-protected transport that pins DNS for the original hostname. 4. It calls the HTTP request layer with `follow_redirects=follow_redirects`. When `follow_redirects=true`, `httpx` handles `3xx` redirects internally. For a redirect from a public host to `http://127.0.0.1:9999/...`, the original public URL passes validation, but the redirect destination is a new host. The vulnerable code does not call `validate_and_resolve_url()` again for that redirect target. As a result, the `127.0.0.1` destination is reached even though a direct request to the same URL is correctly blocked by SSRF protection. The fixed code path present in repository HEAD / fixed `api_request.py` adds `_follow_redirects_with_validation`, which performs each request with redirects disabled, resolves the `Location` header, and re-runs SSRF validation before following every redirect hop. The reproduction's fixed-control swap uses that official HEAD `api_request.py` and observes the expected error: `SSRF Protection: blocked redirect to http://127.0.0.1:9999/admin-token`. ## Reproduction Steps 1. **Script:** `bundle/repro/reproduction_steps.sh`. 2. **What the script does:** - Reuses/creates `bundle/data/venv` and installs the vulnerable stack: `langflow==1.9.3`, `langflow-base==0.9.3`, `lfx==0.4.6`. - Starts a real Langflow server on `127.0.0.1:7860` in multi-user mode with SSRF protection enabled. - Configures a real Langflow superuser (`admin`) and creates a real low-privileged user (`attacker`) via the public user API. - Starts a loopback-only internal admin/metadata service on `127.0.0.1:9999`. This service reads the real Langflow admin user from SQLite and returns a real admin JWT signed with the same Langflow `SECRET_KEY`. - Generates a real Langflow flow containing the API Request component with a public `postman-echo.com/redirect-to` URL that redirects to `http://127.0.0.1:9999/admin-token`. - Logs in as the low-privileged attacker, creates the flow through `POST /api/v1/flows/`, executes it through `POST /api/v1/build/{flow_id}/flow?event_delivery=direct`, extracts the internal admin token from returned build events, and uses it against real admin-only Langflow endpoints to promote the attacker. - Runs a negative control showing a direct `http://127.0.0.1:9999/admin-token` API Request is blocked by SSRF protection. - Runs a fixed control by swapping in the official fixed `api_request.py`, showing redirect re-validation blocks the same public-to-loopback redirect. 3. **Expected evidence of reproduction:** - `logs/exploit_summary.json` contains `attacker_is_superuser_before=false`, `lowpriv_admin_list_status_before=403`, `marker_found=true`, `stolen_token_is_superuser=true`, `stolen_token_admin_list_status=200`, `promote_attacker_status=200`, `attacker_is_superuser_after=true`, and `privilege_escalation_observed=true`. - `logs/direct_summary.json` contains `marker_found=false` and `ssrf_blocked=true`. - `logs/fixed_harness.json` contains `ok=false` and a blocked-redirect SSRF protection error. - `bundle/repro/runtime_manifest.json` records `entrypoint_kind="api_remote"`, `service_started=true`, `healthcheck_passed=true`, and `target_path_reached=true`. ## Evidence Primary evidence artifacts: - `bundle/logs/reproduction_steps.log` — high-level script log and final verdict. - `bundle/logs/exploit_summary.json` — structured exploit and privilege-escalation evidence. - `bundle/logs/build_exploit.ndjson` — raw Langflow build events showing the API Request component response from the loopback internal service. - `bundle/logs/direct_summary.json` and `bundle/logs/build_direct.ndjson` — negative control showing direct loopback access is blocked. - `bundle/logs/fixed_harness.json` — fixed-control evidence that redirect re-validation blocks the attack. - `bundle/logs/vuln_harness.json` — secondary component-level evidence that the vulnerable component follows the redirect. - `bundle/logs/langflow_server.log` — real Langflow server runtime log. - `bundle/logs/internal_admin.log` — loopback internal admin service log. - `bundle/repro/runtime_manifest.json` — runtime manifest written by the reproduction script. Key excerpts from the successful run: - Vulnerable stack confirmation: `installed: langflow 1.9.3 langflow-base 0.9.3 lfx 0.4.6` and `installed api_request.py has redirect re-validation fix? no`. - Pre-exploit privilege boundary: `attacker_is_superuser_before: false` and `lowpriv_admin_list_status_before: 403` with body `The user doesn't have enough privileges`. - SSRF bypass: `marker_found: true`, `reached_internal: true`, and `stolen_admin_token_found: true` in `logs/exploit_summary.json`. - Admin-token use: `stolen_token_whoami_status: 200`, `stolen_token_username: admin`, `stolen_token_is_superuser: true`, and `stolen_token_admin_list_status: 200`. - Privilege escalation: `promote_attacker_status: 200`, `attacker_is_superuser_after: true`, and `privilege_escalation_observed: true`. - Direct SSRF protection control: `direct_blocked=true` and `SSRF Protection: Hostname 127.0.0.1 resolves to blocked IP address(es): 127.0.0.1`. - Fixed control: `ValueError: SSRF Protection: blocked redirect to http://127.0.0.1:9999/admin-token`. Environment details captured by the script include the Langflow package versions, SQLite runtime, internal admin service endpoint, public redirector URL, and all runtime proof artifact paths in `bundle/repro/runtime_manifest.json`. ## Recommendations / Next Steps - Apply per-hop SSRF validation for all redirects. Each `Location` destination should be resolved and checked against the same denylist/allowlist policy before the client connects. - Prefer manually handling redirects with `follow_redirects=False`, validating the next URL before each hop, enforcing a maximum redirect count, and preserving DNS pinning semantics for every validated host. - Upgrade affected deployments to a Langflow/Langflow-base/LFX build containing `_follow_redirects_with_validation` or an equivalent redirect-validation fix. - Add regression tests for public-to-loopback, public-to-private, public-to-link-local, and multi-hop redirect chains with SSRF protection enabled. - Treat internal admin/metadata endpoints as sensitive. Avoid exposing reusable admin credentials on loopback-only HTTP services where possible, because SSRF bugs can convert loopback trust into remote privilege escalation. ## Additional Notes - Idempotency: `bundle/repro/reproduction_steps.sh` was executed twice consecutively and exited successfully both times. - The primary proof uses the real Langflow HTTP API and real API Request component. The direct component harnesses are secondary controls only. - The internal admin service is intentionally local to the reproduction environment and models the internal/admin endpoint class described by the ticket. The privilege escalation effect is real within the running Langflow product: a non-superuser account is promoted via Langflow's actual admin-only API after SSRF leaks an admin bearer token. - The proof redacts JWT values in summaries where possible while preserving enough context to verify that an internal admin credential was retrieved and used. ## Reproduction Details Reproduced: 2026-07-06T08:31:29.282Z Duration: 3409 seconds Tool calls: 446 Turns: Unknown Handoffs: 3 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00240 pruva-verify CVE-2026-10129 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00240&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00240/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-10129 - Source: langflow/langflow ## Artifacts - bundle/repro/reproduction_steps.sh (reproduction_script, 16824 bytes) - bundle/repro/rca_report.md (analysis, 10540 bytes) - bundle/vuln_variant/reproduction_steps.sh (reproduction_script, 7880 bytes) - bundle/vuln_variant/rca_report.md (analysis, 8902 bytes) - bundle/artifact_promotion_manifest.json (other, 12512 bytes) - bundle/vuln_variant/source_identity.json (other, 1256 bytes) - bundle/vuln_variant/root_cause_equivalence.json (other, 1363 bytes) - bundle/logs/vuln_variant/latest_git_identity.txt (other, 140 bytes) - bundle/logs/vuln_variant/url_component_fix_diff_excerpt.txt (other, 11739 bytes) - bundle/repro/runtime_manifest.json (other, 1062 bytes) - bundle/repro/validation_verdict.json (other, 836 bytes) - bundle/logs/build_exploit.ndjson (other, 11771 bytes) - bundle/logs/build_direct.ndjson (other, 12807 bytes) - bundle/logs/fixed_harness.json (other, 628 bytes) - bundle/logs/vuln_harness.json (other, 1442 bytes) - bundle/logs/exploit_summary.json (other, 1709 bytes) - bundle/logs/direct_summary.json (other, 1355 bytes) - bundle/logs/reproduction_steps.log (log, 8872 bytes) - bundle/logs/langflow_server.log (log, 163830 bytes) - bundle/logs/internal_admin.log (log, 410 bytes) - bundle/logs/attacker_register.json (other, 334 bytes) - bundle/vuln_variant/patch_analysis.md (documentation, 7195 bytes) - bundle/vuln_variant/variant_manifest.json (other, 3781 bytes) - bundle/vuln_variant/validation_verdict.json (other, 2098 bytes) - bundle/vuln_variant/runtime_manifest.json (other, 896 bytes) - bundle/logs/vuln_variant/url_component_vulnerable.json (other, 2035 bytes) - bundle/logs/vuln_variant/url_component_fixed_latest.json (other, 1060 bytes) - bundle/logs/vuln_variant/reproduction_steps.log (log, 4038 bytes) - bundle/logs/vuln_variant/internal_marker.log (log, 134 bytes) - bundle/logs/vuln_variant/latest_version.txt (other, 41 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00240 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00240/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00240 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev