Aegra (LangGraph), Cross-Tenant IDOR, CVE-NOT-PROVIDED (Critical)

Listen to this Post

How the CVE works (technical breakdown):

  1. Aegra versions 0.9.0–0.9.6 lack authorization checks on run-creation endpoints.
  2. The framework delegates per‑resource policy to user‑defined `@auth.on` handlers.
  3. If no handler is registered, `handle_event()` returns `None` → default‑allow.
  4. Read endpoints (/threads/...) add an SQL `user_id` filter, but write endpoints do not.

5. Three endpoints: `POST /threads/{thread_id}/runs`, `/runs/stream`, `/runs/wait`.

  1. Attacker (User A) obtains another user’s `thread_id` (UUID leaks via logs/URLs).
  2. User A sends a request to any affected endpoint with User B’s thread_id.
  3. The server does not verify that `thread_id` belongs to the authenticated user.
  4. Run is created under User B’s thread, attaching User A’s `user_id` as the runner.
  5. The run’s `output` field returns User B’s full checkpoint state (messages, data).
  6. Attacker can also inject arbitrary messages into User B’s conversation history.
  7. Streaming variant `/runs/stream` returns the entire `messages` array via the first SSE `values` frame – no graph execution needed.
  8. User B’s `GET /threads/{thread_id}/runs` does not list these runs because the run carries User A’s user_id.
  9. Thus the attacker’s activity is completely hidden from the victim.
  10. Stateless endpoints (POST /runs, /runs/wait, /runs/stream) generate a fresh `thread_id` server‑side and are safe.
  11. Fixed in 0.9.7 by adding SQL‑level `user_id == authenticated_user.identity` check on the three vulnerable endpoints.
  12. If mismatch, returns `404 Thread not found` (no existence leak).
  13. Workaround: register `@auth.on(“threads”, “create_run”)` handler to manually verify thread ownership.
  14. Root cause: default‑allow authorization model + missing SQL filter in api/runs.py.
  15. Credits: discovered by @JoJoTheBizarre, fixed by @victorjmarin & @jawhardjebbi.

dailycve form:

Platform: `LangGraph SDK`
Version: `0.9.0–0.9.6`
Vulnerability: `Cross‑tenant IDOR`
Severity: `Critical`
Date: `2024‑10‑01`

Prediction: `2024‑10‑20`

What Undercode Say:

List vulnerable endpoints
curl -X POST https://target/threads/{victim_thread_id}/runs \
-H "Authorization: Bearer $ATTACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"assistant_id": "some_id"}'
Stream to instantly dump victim’s entire message history
curl -X POST https://target/threads/{victim_thread_id}/runs/stream \
-H "Authorization: Bearer $ATTACKER_TOKEN" \
-H "Accept: text/event-stream"
Inject a malicious message into victim’s thread
curl -X POST https://target/threads/{victim_thread_id}/runs \
-H "Authorization: Bearer $ATTACKER_TOKEN" \
-d '{"input": {"messages": [{"role": "user", "content": "Injected by attacker"}]}}'

Exploit:

Attacker needs any valid user account + a victim’s `thread_id` (leaked via frontend, logs, or shared links). Send `POST /threads/{thread_id}/runs/stream` – the first SSE event returns the victim’s full conversation. No brute‑forcing required.

Protection from this CVE:

Upgrade to 0.9.7 immediately. If not possible, register a custom `@auth.on(“threads”, “create_run”)` handler that implements thread ownership validation. Example:

@auth.on("threads", "create_run")
async def enforce_thread_owner(ctx, value):
thread = await fetch_thread(value["thread_id"])
if thread.owner != ctx.user.identity:
raise HTTPException(404, "Thread not found")

Impact:

Complete cross‑tenant data breach. Attacker can read, modify, and inject messages into any user’s conversation history without detection. Affects all multi‑tenant Aegra/LangGraph deployments without custom auth handlers.

🎯Let’s Practice Exploiting & Learn Patching For Free:

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top