Mar 25, 2026·6 min read·2 visits
A stored XSS vulnerability in MantisBT 2.28.0 allows authenticated attackers to execute arbitrary JavaScript in the context of other users viewing the issue timeline by leveraging deleted or renamed tags.
Mantis Bug Tracker (MantisBT) version 2.28.0 contains a stored Cross-Site Scripting (XSS) vulnerability in its Timeline feature. The flaw occurs when the application renders historical issue tags that have been subsequently renamed or deleted, falling back to an unescaped raw string from the database.
Mantis Bug Tracker (MantisBT) version 2.28.0 contains a stored Cross-Site Scripting (XSS) vulnerability tracked as CVE-2026-33548. The vulnerability manifests within the Timeline feature, specifically when rendering historical issue tags. An attacker can inject arbitrary JavaScript that executes when a victim views the timeline on the dashboard (my_view_page.php).
The flaw occurs due to improper output encoding when handling edge cases in the tag history. When an issue tag is modified, the application records the tag's state in the bug_history table. During timeline generation, the application attempts to resolve the historical tag name to an active tag object to generate a safe HTML link.
This vulnerability is classified under CWE-79 (Improper Neutralization of Input During Web Page Generation). It requires low privileges to stage the payload and relies on user interaction to execute. The execution context is the browser session of the victim, which exposes sensitive session tokens and administrative interfaces to the attacker.
The vulnerability is located in the IssueTagTimelineEvent::html() method within core/classes/IssueTagTimelineEvent.class.php. The timeline rendering logic fetches historical tag information from the database to reconstruct the issue's event history. When the timeline is rendered, the system queries the active tags to create a linked representation of the historical event.
If a tag referenced in the history has been deleted or renamed since the history entry was created, the active tag object cannot be found. In this scenario, the application defaults to using the raw, unescaped name string stored in the bug_history table. This fallback variable, $this->tag_name, is directly concatenated into the HTML output.
The absence of HTML entity encoding on this specific fallback path creates the vulnerability. While the primary execution path utilizing tag_get_link() properly sanitizes output, the fallback path assumes the historical data is safe. This violates the principle of context-aware output encoding.
The vulnerability exists in the formatting logic of the html() method. The method utilizes a ternary operator to determine whether to render a rich link or a plain text fallback. The vulnerable code path evaluates $t_tag_row to check for the existence of the tag object.
// Vulnerable Code (Version 2.28.0)
return sprintf(
$t_string,
prepare_user_name( $this->user_id ),
string_get_bug_view_link( $this->issue_id ),
$t_tag_row ? tag_get_link( $t_tag_row ) : $this->tag_name
);The patch applied in version 2.28.1 addresses this by passing the fallback variable through the string_html_specialchars() function. This ensures that any HTML characters within the raw tag name are properly encoded before being inserted into the DOM.
// Patched Code (Commit f32787c14d4518476fe7f05f992dbfe6eaccd815)
- $t_tag_row ? tag_get_link( $t_tag_row ) : $this->tag_name
+ $t_tag_row ? tag_get_link( $t_tag_row ) : string_html_specialchars( $this->tag_name )This fix is complete for this specific code path. It ensures that regardless of whether the tag object exists or the application falls back to the raw database string, the output is neutralized before reaching the browser context.
Exploitation requires an attacker to possess privileges sufficient to create issue tags, typically assigned to the Reporter role or higher. The attacker begins by creating a new tag containing a malicious JavaScript payload within HTML tags. This payload is stored in the application database.
The attacker then attaches this crafted tag to a target issue. This action creates a permanent record in the bug_history table containing the unescaped payload. To arm the exploit, the attacker must force the timeline rendering logic into the vulnerable fallback path.
The attacker achieves this by deleting or renaming the crafted tag. Once the tag object is no longer available, the trigger mechanism is established. Any subsequent visit to the issue timeline or the "My View" dashboard (my_view_page.php) will execute the payload.
The payload executes in the context of the viewing user's browser session. The attacker does not need to maintain any active access once the payload is staged, making this an asynchronous attack that persists in the environment until the specific history entry is removed.
Successful exploitation results in the execution of arbitrary JavaScript within the context of the victim's session. This allows the attacker to perform actions on behalf of the user, including viewing sensitive issue details or modifying platform configurations. If the victim holds administrative privileges, the attacker can achieve full application compromise.
The CVSS v4.0 base score of 8.6 reflects the high impact on confidentiality, integrity, and availability. The attack requires low privileges and user interaction, as the victim must view the specific timeline containing the armed payload. The vulnerability relies on persistent storage, meaning the exploit remains active until the underlying database records are sanitized.
The exploit maps to MITRE ATT&CK techniques T1189 (Drive-by Compromise) and T1185 (Browser Session Hijacking). While the current Exploit Prediction Scoring System (EPSS) score is low at 0.00069, the straightforward nature of the exploit and the availability of the patch diff make weaponization highly probable.
The primary remediation strategy is upgrading MantisBT to version 2.28.1 or later. This release contains the patch that correctly implements output encoding on the vulnerable fallback path. Organizations should prioritize patching systems exposed to untrusted users.
For environments where immediate patching is not feasible, administrators can apply a manual code-level fix. This involves locating the html() function within core/classes/IssueTagTimelineEvent.class.php and wrapping $this->tag_name in a string_html_specialchars() call. This directly replicates the official vendor patch without requiring a full version upgrade.
Administrators can also proactively sanitize existing databases to remove malicious payloads. Executing a SQL query to identify and remove HTML tags from the bug_history table where the field_name is 'tag' will disarm existing exploits. Implementing a strong Content Security Policy (CSP) will provide defense-in-depth against XSS execution.
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
MantisBT mantisbt | 2.28.0 | 2.28.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS v4.0 | 8.6 (High) |
| EPSS Score | 0.00069 |
| Impact | Stored Cross-Site Scripting |
| Exploit Status | None |
| KEV Status | Not Listed |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')