FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration <= 1.91.2 - Authenticated (Board Member+) Insecure Direct Object Reference
Description
The FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 1.91.2 due to missing validation on a user controlled key. This makes it possible for authenticated attackers, with Custom-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.91.2What Changed in the Fix
Changes introduced in v1.91.3
Source Code
WordPress.org SVN# Research Plan: CVE-2026-40784 FluentBoards IDOR ## 1. Vulnerability Summary The **FluentBoards** plugin (<= 1.91.2) contains an Insecure Direct Object Reference (IDOR) vulnerability in its comment management system. The `CommentController` does not sufficiently validate whether the authenticated …
Show full research plan
Research Plan: CVE-2026-40784 FluentBoards IDOR
1. Vulnerability Summary
The FluentBoards plugin (<= 1.91.2) contains an Insecure Direct Object Reference (IDOR) vulnerability in its comment management system. The CommentController does not sufficiently validate whether the authenticated user has the authority to modify or delete a specific comment based on its comment_id. While the API requires a board_id in the URL, it fails to verify if the provided comment_id actually belongs to that board or if the user is the author/administrator of that comment. This allows any user with "Board Member" access (or higher) to modify comments across the entire system by manipulating the ID.
2. Attack Vector Analysis
- Endpoint:
POST /wp-json/fluent-boards/v1/boards/{board_id}/comments/{comment_id} - Method:
POST(used for updates in the Fluent framework) - Authentication: Required (User must have at least "Board Member" permissions within the plugin).
- Vulnerable Parameter:
{comment_id}in the REST URL. - Payload Parameter:
comment(string) containing the new comment text. - Preconditions:
- The attacker must be logged in.
- The attacker must be a member of at least one Board (to access the API).
- The attacker needs to know the ID of the comment they wish to modify.
3. Code Flow
- Entry Point: The request hits the REST API route associated with
CommentController@update. - Controller Logic (
app/Http/Controllers/CommentController.php):- The
updatemethod (truncated in provided source, but visible at line 147) receives$board_idand$comment_idfrom the URL. - It calls
$this->commentSanitizeAndValidate()to process thecommentbody. - It attempts to update the comment via
$this->commentService->update($commentData, $comment_id).
- The
- Missing Check: There is no logic in the controller to verify:
- That the comment identified by
$comment_idbelongs to the board identified by$board_id. - That the current user is the owner of the comment or a Board Administrator.
- That the comment identified by
- Sink: The database is updated with the new comment content regardless of ownership.
4. Nonce Acquisition Strategy
FluentBoards uses the standard WordPress REST API nonce. It is localized in the admin dashboard.
- Identify Trigger: The FluentBoards dashboard is located at
/wp-admin/admin.php?page=fluent-boards. - Access Dashboard: Navigate to the FluentBoards page as the "Board Member" user.
- Extract Nonce: Use
browser_evalto extract the nonce from thewindow.FluentBoardsAdminglobal object.- JS Key:
window.FluentBoardsAdmin.nonce
- JS Key:
- Alternative: The standard REST nonce
wp_restmight also work if passed in theX-WP-Nonceheader.
5. Exploitation Strategy
Step 1: Data Gathering
Identify the Target Comment ID. For this PoC, we will create a comment as a "Victim" user and capture its ID.
Step 2: Perform the IDOR Update
Use the http_request tool to send a spoofed update request.
- URL:
http://localhost:8080/wp-json/fluent-boards/v1/boards/{ATTACKER_BOARD_ID}/comments/{VICTIM_COMMENT_ID} - Method:
POST - Headers:
Content-Type: application/jsonX-WP-Nonce: {EXTRACTED_NONCE}Cookie: {ATTACKER_SESSION_COOKIES}
- Payload:
{ "comment": "This comment has been modified by an unauthorized user.", "mentionData": [] }
6. Test Data Setup
- Users:
victim_user(Subscriber role)attacker_user(Subscriber role)
- Boards:
Victim Board(ID: 1):victim_useradded as "Admin".Attacker Board(ID: 2):attacker_useradded as "Member".
- Content:
- A task created on
Victim Board. - A comment created by
victim_useron that task (Target ID:X).
- A task created on
- Plugin Configuration: Ensure both users are assigned their respective boards via the FluentBoards interface.
7. Expected Results
- The server returns a
200 OKor201 Createdresponse. - The response JSON contains the updated comment object with the new description.
- The comment ID in the response matches
{VICTIM_COMMENT_ID}.
8. Verification Steps
- Check via CLI:
wp db query "SELECT description FROM wp_fboards_comments WHERE id = {VICTIM_COMMENT_ID}" - Confirm Content: Verify the description matches the string:
"This comment has been modified by an unauthorized user.". - Ownership Check: Verify the
created_byfield in the database remains thevictim_userID, proving the comment was modified by someone else.
9. Alternative Approaches
- IDOR Deletion: If an
updatevulnerability exists, adeletevulnerability often exists at the same level.- Endpoint:
DELETE /wp-json/fluent-boards/v1/boards/{attacker_board_id}/comments/{victim_comment_id}
- Endpoint:
- Board Settings IDOR: Check if
BoardController::modifyAuthenticationPermission(line 309 inBoardService.phpreference) can be triggered by a non-admin by providing a validboardIdin a POST request to the settings update endpoint. - Task IDOR: Attempt to move a task from a victim board to an attacker board by changing the
board_idandstage_idin a task update request. (CheckTaskController.php).
Summary
The FluentBoards plugin is vulnerable to Insecure Direct Object Reference (IDOR) due to insufficient validation that a requested object (such as a task, comment, or stage) actually belongs to the board specified in the API request. This allows authenticated attackers with Board Member access to view, modify, or delete tasks and comments on other boards by manipulating the IDs in the REST API endpoints.
Vulnerable Code
// app/Http/Controllers/CommentController.php line 212 public function deleteComment($board_id, $comment_id) { try { $this->commentService->delete($comment_id); --- // app/Http/Controllers/TaskController.php line 188 public function find($board_id, $task_id) { $board_id = absint($board_id); $task_id = absint($task_id); try { $stageService = new StageService(); $task = Task::findOrFail($task_id); --- // app/Http/Controllers/CommentController.php line 330 public function updateCommentPrivacy($board_id, $comment_id) { $comment = Comment::findOrFail($comment_id); // Check if user has permission to update the comment if ($comment->created_by != get_current_user_id()) {
Security Fix
@@ -968,7 +968,7 @@ public function archiveAllTasksInStage($board_id, $stage_id) { - $updates = $this->stageService->archiveAllTasksInStage($stage_id); + $updates = $this->stageService->archiveAllTasksInStage($stage_id, $board_id); return [ 'message' => __('Tasks have been archived', 'fluent-boards'), 'updatedTasks' => $updates, @@ -27,7 +27,10 @@ public function getComments(Request $request, $board_id, $task_id) { + $board_id = absint($board_id); + $task_id = absint($task_id); try { + Task::where('board_id', $board_id)->where('id', $task_id)->firstOrFail(); $filter = $request->getSafe('filter', 'sanitize_text_field'); $per_page = 10; @@ -105,7 +108,7 @@ $usersToSendEmail = []; if ($comment->type == 'reply') { - $parentComment = Comment::findOrFail($comment->parent_id); + $parentComment = Comment::where('board_id', (int) $board_id)->where('id', $comment->parent_id)->firstOrFail(); $commenterId = $parentComment->created_by; if ($commenterId != get_current_user_id()) { @@ -212,7 +215,10 @@ public function deleteComment($board_id, $comment_id) { + $board_id = absint($board_id); + $comment_id = absint($comment_id); try { + Comment::where('board_id', $board_id)->where('id', $comment_id)->firstOrFail(); $this->commentService->delete($comment_id); return $this->sendSuccess([ @@ -261,7 +267,10 @@ public function deleteReply($board_id, $reply_id) { + $board_id = absint($board_id); + $reply_id = absint($reply_id); try { + Comment::where('board_id', $board_id)->where('id', $reply_id)->firstOrFail(); $this->commentService->deleteReply($reply_id); return $this->sendSuccess([ @@ -330,7 +339,9 @@ public function updateCommentPrivacy($board_id, $comment_id) { - $comment = Comment::findOrFail($comment_id); + $board_id = absint($board_id); + $comment_id = absint($comment_id); + $comment = Comment::where('board_id', $board_id)->where('id', $comment_id)->firstOrFail(); // Check if user has permission to update the comment if ($comment->created_by != get_current_user_id()) {
Exploit Outline
The exploit targets the FluentBoards REST API endpoints for comment and task management. 1. **Authentication**: The attacker must be authenticated and have at least "Board Member" permissions on at least one legitimate board. 2. **Information Gathering**: The attacker identifies the ID of a task or comment they wish to target (the 'victim' object) that belongs to a board they do not have access to. 3. **Request Construction**: The attacker crafts a REST request to a vulnerable endpoint, such as `DELETE /wp-json/fluent-boards/v1/boards/{ATTACKER_BOARD_ID}/comments/{VICTIM_COMMENT_ID}`. 4. **Payload Execution**: The attacker includes their valid `X-WP-Nonce` and session cookies. By providing an ID for a board they *do* have access to in the URL path, they bypass the initial authorization check. 5. **Impact**: Because the controller logic previously used `findOrFail($comment_id)` or direct service calls without verifying that the `$comment_id` belonged to `$board_id`, the action (delete, update, or view) is performed on the victim's object despite the lack of permission.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.