Forums Bug Reports Thread

Broken access control: moveThread relocates a thread into ANY forum without checking the target forum

Patrick Bass · Jun 6 · 18 · 1 Locked
[Major] [High Priority] [Bug Fixed] [Always Reproduces]
🚀 OP Jun 6, 2026 8:51pm

Area: Forums (re-run) (audit p2r) · Surface: POST /thread/{threadId}/move (ForumController@moveThread) · Dimension: Security (OWASP) · Severity: major

A forum moderator (forum-scoped, not site-wide) can move a thread out of a forum they moderate into a forum they have no authority over — including hidden/private forums, paid forums, or the mod-only escalations forum. This lets a single-forum mod inject content into restricted areas and remove content from public view by relocating it into a hidden forum. The privilege check covers only the source side; the target side is attacker-controlled via target_forum_id with no authorization. OWASP A01:2021 (Broken Access Control / IDOR).

Evidence

ForumController::moveThread (platform/src/Controllers/ForumController.php:5137-5168) only authorizes against the SOURCE forum: `if (!$this->canModerate((int) $user['id'], (int) $user['role'], (int) $thread['forum_id'])) { http_response_code(403); return; }`. It then loads the target purely by POST id: `$targetForumId = (int) ($_POST['target_forum_id'] ?? 0); $targetForum = Forum::findById($targetForumId);` and on success does `ForumThread::update((int) $thread['id'], ['forum_id' => $targetForumId]);` with NO check that the actor moderates, is a member of, or can even view the target forum. Contrast with crosspost (line 4300-4306) which explicitly re-checks the target: `if (!Forum::canPost($targetForumId, (int) $user['id'], (int) $user['role'])) { ... 'You cannot post in that forum.' }` — its own comment says 'Without this a source-forum moderator or thread author could shove a new thread into a private forum they have no access to.' moveThread is missing that exact guard.

Suggested fix. Mirror crosspost: after resolving $targetForum, require the actor to have authority over it — e.g. `if (!$this->canModerate((int)$user['id'],(int)$user['role'],$targetForumId)) { 403 }` (site admins role>=4 already pass canModerate). At minimum block moving into forums the actor cannot view/post in.

Filed by the automated tenant-app audit and adversarially evidence-verified. Status: verified. Open — not yet actioned.


Patrick Bass
@mobieus

🚀 Jun 6, 2026 9:00pm

Resolved — fixed and deployed. Commit e3a3fc5f66c5, shipped dev-first then to all tenants on 2026-06-06.

ForumController::moveThread now authorizes the TARGET forum (canModerate on target_forum_id), mirroring crosspost(). Previously only the source forum was checked, so a forum-scoped moderator could relocate a thread into a private/hidden/paid forum they have no authority over (and could hide content by moving it into a hidden forum).

Status: fixed. Thread closed and locked.


Patrick Bass
@mobieus

Log in or register to reply to this thread.