View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0037093 | mantisbt | security | public | 2026-04-19 04:51 | 2026-05-10 07:58 | ||||||||||||
| Reporter | TristanInSec | Assigned To | dregad | ||||||||||||||
| Priority | high | Severity | major | Reproducibility | always | ||||||||||||
| Status | closed | Resolution | duplicate | ||||||||||||||
| Summary | 0037093: Authorization Bypass in Bugnote Editing via Issue Update API | ||||||||||||||||
| Description | The Vulnerability Details
Root Cause
The functions In contrast, the dedicated
Default Access Levels
Impact
| ||||||||||||||||
| Steps To Reproduce | Proof of Concept
This succeeds via REST API but the same user would be denied via the dedicated note update endpoint. | ||||||||||||||||
| Additional Information | FixAdd per-note authorization checks in the | ||||||||||||||||
| Tags | No tags attached. | ||||||||||||||||
| Attached Files | vuln-2-bugnote-edit-auth-bypass.md (3,909 bytes)
# Authorization Bypass in Bugnote Editing via Issue Update API
**Validation: Code trace** (TODO — manual review required, lint-added 2026-04-16)
## Summary
The `mc_issue_update()` function in MantisBT allows users with UPDATER (level 40) access to edit, change view state, and modify time tracking on bugnotes belonging to other users — bypassing the DEVELOPER (level 55) threshold required by the dedicated `mc_issue_note_update()` function.
## Vulnerability Details
- **CWE**: CWE-863 (Incorrect Authorization)
- **CVSS 3.1**: 5.4 (Medium) — `AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L`
- **Affected Version**: MantisBT 2.29.0-dev (and likely all prior versions with SOAP/REST API)
- **Auth Required**: UPDATER access level (level 40) to the project
## Root Cause
`mc_issue_update()` in `api/soap/mc_issue_api.php` checks only `update_bug_threshold` (UPDATER, level 40) at line 1012 before processing note modifications at lines 1180-1208:
```php
// Line 1012 — only gate for the entire function
if( !access_has_bug_level( config_get( 'update_bug_threshold' ), $p_issue_id, $t_user_id ) ) {
return mci_fault_access_denied( $t_user_id, 'Not enough rights to update issues' );
}
// Lines 1189-1196 — NO per-note auth check
if( isset( $t_note['text']) && $t_bugnote->note !== $t_note['text'] ) {
bugnote_set_text( $t_bugnote_id, $t_note['text'] ); // Direct DB write
}
if( $t_bugnote->view_state != $t_view_state_id ) {
bugnote_set_view_state( $t_bugnote_id, $t_view_state_id == VS_PRIVATE ); // Direct DB write
}
```
The functions `bugnote_set_text()`, `bugnote_set_view_state()`, and `bugnote_set_time_tracking()` are raw database operations with **zero authorization checks**.
In contrast, the dedicated `mc_issue_note_update()` (line 1461) properly enforces:
- `bugnote_user_edit_threshold` for note owners (line 1497-1498)
- `update_bugnote_threshold` for non-owners (line 1504-1506)
Both default to DEVELOPER (level 55).
## Default Access Levels
| Config | Default | Level |
|--------|---------|-------|
| `update_bug_threshold` | UPDATER | 40 |
| `update_bugnote_threshold` | DEVELOPER | 55 |
| `bugnote_user_edit_threshold` | DEVELOPER | 55 |
## Impact
1. **UPDATER can edit notes by DEVELOPER/MANAGER/ADMIN** — bypassing the DEVELOPER threshold
2. **UPDATER can change private notes to public** — exposing confidential internal discussion
3. **UPDATER can change public notes to private** — hiding information from reporters/viewers
## Proof of Concept
```bash
# As UPDATER user, modify note #42 (written by admin) on issue #5
curl -X PATCH "http://mantis.local/api/rest/issues/5" \
-H "Authorization: UPDATER_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"notes": [{
"id": 42,
"text": "MODIFIED BY UPDATER",
"view_state": {"id": 10, "name": "public"}
}]
}'
```
This succeeds via REST API but the same user would be denied via the dedicated note update endpoint.
## Fix
Add per-note authorization checks in the `mc_issue_update()` note modification loop (around line 1185):
```php
if( array_key_exists( $t_bugnote_id, $t_bugnotes_by_id ) ) {
$t_bugnote = $t_bugnotes_by_id[$t_bugnote_id];
// ADD: Check note-level edit permission
$t_user_owns_note = ($t_bugnote->reporter_id == $t_user_id);
if( $t_user_owns_note ) {
$t_edit_threshold = config_get('bugnote_user_edit_threshold', null, $t_user_id, $t_project_id);
if( !access_has_bugnote_level( $t_edit_threshold, $t_bugnote_id, $t_user_id ) ) {
return mci_fault_access_denied( $t_user_id );
}
} else {
$t_edit_threshold = config_get('update_bugnote_threshold', null, $t_user_id, $t_project_id);
if( !access_has_bugnote_level( $t_edit_threshold, $t_bugnote_id, $t_user_id ) ) {
return mci_fault_access_denied( $t_user_id );
}
}
// ... existing code ...
}
```
| ||||||||||||||||
|
Many thanks for the detailed and well-written report. This vulnerability has already been identified by another researcher, so I'm closing this as duplicate of 0037089, but I will nevertheless credit you for the finding (CVE has not yet been assigned). As mentioned in my earlier email, please let me know your GitHub Id. |
|
This case is only reproducible when the UPDATER user also has 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. |
|
|
@TristanInSec I have a patch ready for review to fix this vulnerability, I have sent you an invite to a private repository so you can have a look and provide feedback. |
|