Mar 1, 2026·5 min read·31 visits
listmonk contains a Stored XSS vulnerability in its campaign preview feature. Attackers with campaign management rights can inject malicious scripts that execute in the administrator's browser, leading to full Account Takeover (ATO). The issue is resolved in version 6.0.0.
A critical Stored Cross-Site Scripting (XSS) vulnerability exists in listmonk versions 5.1.0 and prior, originating from the campaign and template management modules. The flaw allows authenticated users with limited permissions to inject arbitrary JavaScript via unsanitized template fields. Execution occurs when an administrator views a campaign preview or the public archive, bypassing the same-origin policy within the administrative dashboard. Successful exploitation permits attackers to hijack the administrator's session, enabling unauthorized API calls such as creating new super-admin users.
listmonk, a self-hosted newsletter and mailing list manager written in Go and Vue, contains a Stored Cross-Site Scripting (XSS) vulnerability in its campaign and template editing interfaces. The vulnerability (CWE-79) arises because the application permits users with campaigns:manage or templates:manage permissions to save content containing arbitrary HTML and JavaScript.
The core issue manifests when this stored content is rendered. Specifically, the application failed to adequately sanitize input server-side and relied on a frontend rendering context that did not enforce strict isolation. When a privileged user, such as a Super Admin, initiates a "Preview" of a compromised campaign or views the public archive, the injected script executes within their authenticated session context.
While the CVSS base score is calculated at 5.4 (Medium) due to the requirement for privileges and user interaction, the practical impact is Critical. The vulnerability facilitates a direct path to Administrative Account Takeover (ATO), allowing attackers to pivot from a low-privileged editor account to full system control.
The vulnerability stems from two concurring architectural deficiencies regarding input handling and output rendering.
1. Unsafe Template Handling: The Go html/template package provides contextual auto-escaping to prevent XSS. However, listmonk exposed a mechanism to bypass this protection, likely via a custom template filter (e.g., Safe). This filter casts raw strings to template.HTML, a type that instructs the Go template engine to treat the content as trusted and render it without escaping. Attackers utilized this to inject raw HTML payloads.
2. Lack of Context Isolation: The campaign preview feature rendered user-controlled HTML directly within the administrative dashboard's Document Object Model (DOM) or a non-sandboxed context. Without the sandbox attribute on the iframe used for previews, the rendered content retained access to the parent window's context, including cookies, local storage, and the ability to issue XHR/Fetch requests on behalf of the logged-in administrator.
The remediation strategy focused on isolating the rendering context rather than attempting to sanitize complex HTML, which is error-prone for newsletter software that requires rich layout capabilities. The fix was implemented in frontend/src/views/Campaigns/Preview.vue (and related files) by enforcing strict sandboxing on the preview iframe.
The following diff illustrates the critical change in commit 74dc5a01:
<!-- Vulnerable Implementation (Conceptual) -->
<iframe
:srcdoc="campaignContent"
class="preview-frame"
></iframe>
<!-- Patched Implementation (Commit 74dc5a01) -->
<iframe
:srcdoc="campaignContent"
class="preview-frame"
sandbox="allow-scripts"
></iframe>By adding sandbox="allow-scripts", the browser treats the content within the iframe as being from a unique origin. While scripts can still run (necessary for rendering some dynamic email components), they are blocked from accessing the parent document's DOM (hosting the admin session) or making same-origin requests to the API. This effectively neutralizes the ATO vector even if malicious scripts are injected.
Additional hardening in version 6.0.0 included removing the getHostByName sprig function (Commit 39658c44) to prevent DNS reconnaissance and implementing stricter CORS policies (Commit cdf0a5c1).
An attacker with campaigns:manage permissions can exploit this vulnerability using the following workflow:
Injection: The attacker creates a new campaign or edits an existing template. In the body content, they inject a JavaScript payload designed to create a new administrative user. A typical payload might look like:
<script>
fetch('/api/users', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({username: 'hacker', password: 'Password123!', role: 'admin'})
});
</script>Staging: The attacker saves the campaign. The payload is now stored in the database.
Trigger: The attacker waits for an administrator to view the campaign list and click "Preview" to check the draft, or sends a link to the public archive view.
Execution: Upon rendering, the script executes in the admin's browser. Because the admin has an active session, the fetch request is authenticated automatically by the browser sending the session cookies.
Takeover: The API processes the request, creating the rogue admin account. The attacker can now log in with full privileges.
The impact of this vulnerability extends beyond simple script execution to complete system compromise.
Although NVD rates this as Medium (5.4), the business impact is Critical due to the Account Takeover (ATO) capability. The S:C (Scope Changed) vector component correctly identifies that the vulnerability affects resources beyond the immediate component (the user's browser/session).
The primary mitigation is to upgrade listmonk to version 6.0.0 or later immediately. This version includes the iframe sandboxing fix, CORS hardening, and the removal of dangerous template functions.
Temporary Workarounds: If an immediate upgrade is not feasible:
campaigns:manage and templates:manage permissions from all untrusted users. Only fully trusted administrators should be allowed to edit content./api/campaigns containing <script> tags or {{.*|.*Safe.*}} patterns in the request body.Verification: After patching, administrators should inspect the User list (/admin/users) for any unrecognized accounts created prior to the update.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
listmonk knadh | <= 5.1.0 | 6.0.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS Score | 5.4 (Medium) |
| Impact | Account Takeover (ATO) |
| EPSS Score | 0.0001 |
| Exploit Status | PoC Available |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
netfoil, an allowlist-based DNS proxy, failed to sanitize ALPN fields parsed from untrusted DNS-over-HTTPS (DoH) HTTPS Resource Records. This allowed attackers to inject ANSI escape sequences into log files or trigger Denial of Service (DoS) via uncontrolled memory allocations.
An issue was discovered in the tokio-postgres library for Rust prior to version 0.7.18. A trust assumption mismatch between the PostgreSQL protocol messages sent by a server and how they are parsed and indexed by the client-side library allows a rogue or compromised database server to trigger a Denial of Service (DoS) crash via an unhandled out-of-bounds slice indexing panic.
CVE-2026-14669 is a critical heap-based buffer overflow vulnerability in PostgreSQL's date/time formatting function to_char(timestamptz). The flaw arises from unsafe copying of user-controlled timezone abbreviations into a fixed-size internal buffer. An authenticated database user can trigger this issue by setting a long POSIX timezone abbreviation containing custom formatting, allowing them to overwrite adjacent heap structures and hijack execution control to achieve remote code execution (RCE) with the privileges of the 'postgres' operating system user.
An unauthenticated remote denial of service vulnerability exists in the Unleash feature management platform. By submitting a crafted JSON payload containing deeply nested structures to an OpenAPI-validated endpoint, an attacker can trigger uncontrolled recursion within the error formatting module. This leads to a call-stack exhaustion (RangeError: Maximum call stack size exceeded) inside the Node.js runtime, causing the service to crash immediately without recovery.
CVE-2026-63004 is a server-side request forgery (SSRF) vulnerability in the Unleash feature management platform. Authenticated administrators with CREATE_ADDON or UPDATE_ADDON privileges can exploit this vulnerability to initiate requests to loopback addresses, private networks, and cloud metadata endpoints, potentially leading to information disclosure and credential extraction.
Prior to version 8.0.3, Unleash's Markdown event formatter directly mutated the global template-escaping function of the shared mustache Node.js module, resulting in a process-wide security degradation where HTML/Markdown escaping was permanently disabled for the application lifetime.