View Issue Details

IDProjectCategoryView StatusLast Update
0037089mantisbtsecuritypublic2026-05-09 19:56
Reporterninjasec Assigned Todregad  
PrioritynormalSeveritymajorReproducibilityalways
Status closedResolutionfixed 
Product Version2.28.1 
Target Version2.28.2Fixed in Version2.28.2 
Summary0037089: CVE-2026-42070: REST/SOAP mc_issue_update Embedded Note Update Bypasses Note-Level Authorization
Description

mc_issue_update() accepts a notes array. When a note entry in that array
contains an id referencing an existing visible bugnote, the function applies
three direct DB-level operations without enforcing the corresponding access
thresholds:

  1. bugnote_set_text() (line 1190) - no update_bugnote_threshold check.
    Default threshold: DEVELOPER (55). An UPDATER (40) can modify any visible
    note's text.

  2. bugnote_set_view_state() (line 1195) - no
    bugnote_user_change_view_state_threshold / change_view_status_threshold
    check. Default threshold: UPDATER (40). But the guard in the normal
    bugnote_set_view_state.php also checks update_bugnote_threshold (DEVELOPER)
    for notes owned by others.

  3. bugnote_set_time_tracking() (line 1200) - no time_tracking_enabled
    feature gate and no time_tracking_edit_threshold check.

The only gating applied before reaching the note-update branch is
update_bug_threshold (issue-level, default UPDATER 40), which is lower than
update_bugnote_threshold (note-level, default DEVELOPER 55).

Relevant code at mc_issue_api.php:1185-1197:

if( array_key_exists( $t_bugnote_id, $t_bugnotes_by_id ) ) {
    $t_bugnote_changed = false;
    $t_bugnote = $t_bugnotes_by_id[$t_bugnote_id];

    if( isset( $t_note['text']) && $t_bugnote->note !== $t_note['text'] ) {
        bugnote_set_text( $t_bugnote_id, $t_note['text'] );      // no update_bugnote_threshold check
        $t_bugnote_changed = true;
    }

    if( $t_bugnote->view_state != $t_view_state_id ) {
        bugnote_set_view_state( $t_bugnote_id, $t_view_state_id == VS_PRIVATE );  // no view-state threshold check
        $t_bugnote_changed = true;
    }

    if( isset( $t_note['time_tracking']) && $t_note['time_tracking'] != $t_bugnote->time_tracking ) {
        bugnote_set_time_tracking( $t_bugnote_id, ... );          // no time_tracking_enabled or threshold check
        $t_bugnote_changed = true;
    }
Steps To Reproduce

Precondition: authenticated user has update_bug_threshold access (UPDATER) but
not update_bugnote_threshold (DEVELOPER). Note 5 was created by a different user.

PATCH /api/rest/issues/1 HTTP/1.1
Authorization: Bearer <updater-api-token>
Content-Type: application/json

{
  "summary": "Existing summary",
  "description": "Existing description",
  "reporter": {"id": 2},
  "category": {"id": 1},
  "notes": [
    {
      "id": 5,
      "text": "EMBED-BYPASS-MODIFIED",
      "view_state": {"name": "private"},
      "time_tracking": {"duration": "2:00"}
    }
  ]
}

Observed: note 5 text is changed, privacy toggled to private, time tracking
updated - all without the UPDATER having update_bugnote_threshold,
change_view_status_threshold, or time_tracking_edit_threshold access.

Additional Information

Severity:
Medium

Type:
Authorization bypass / missing access control

Affected code:
api/soap/mc_issue_api.php:1185-1206 (embedded note update branch)
REST: PATCH /api/rest/issues/{id} (delegates to mc_issue_update)

Impact:
An UPDATER (level 40) who cannot edit another user's note via the web interface or
the standalone note endpoint can bypass note-edit authorization by embedding the
modification inside a PATCH /api/rest/issues/{id} request. The same path allows
toggling note privacy and updating time-tracking data without the appropriate
per-note thresholds.

TagsNo tags attached.

Relationships

has duplicate 0037093 closeddregad Authorization Bypass in Bugnote Editing via Issue Update API 

Activities

dregad

dregad

2026-04-19 19:41

developer   ~0071039

Advisory https://github.com/mantisbt/mantisbt/security/advisories/GHSA-pq86-j2c2-47f6 created, CVE request sent.

dregad

dregad

2026-04-24 13:06

developer   ~0071046

The same path allows toggling note privacy

By default, changing view state from public to private is possible, but to make a private note public the UPDATER user also needs to have private_bugnote_threshold (which is not the case by default).

mc_issue_update() calls bugnote_get_all_visible_bugnotes(), which only returns bugnotes the UPDATER user has access to, excluding other user's private notes.

dregad

dregad

2026-04-28 12:31

developer   ~0071052

CVE-2026-42070 assigned

dregad

dregad

2026-05-02 05:40

developer   ~0071058

@ninjasec Patch is ready for review, https://github.com/mantisbt/mantisbt-private/pull/4. looking forward to receiving your feedback.

Related Changesets

MantisBT: master-2.28 6e58fae4

2026-05-06 19:33

dregad

Committer: community


Details Diff
Fix Bugnote udpate auth bypass via REST/SOAP API

Add a note-level permission check in mc_issue_update() to ensure the
user is authorized to update each bugnote individually.

Fixes 0037089, GHSA-pq86-j2c2-47f6 / CVE-2026-42070
Affected Issues
0037089
mod - api/soap/mc_issue_api.php Diff File