CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2025-49136
9.141.27%

Listmonk's Dirty Laundry: Exfiltrating Secrets via SSTI

Alon Barad
Alon Barad
Software Engineer

Feb 26, 2026·5 min read·35 visits

PoC Available

Executive Summary (TL;DR)

Critical SSTI in listmonk versions < 5.0.2 allows authenticated users to read host-level environment variables (including secrets) by injecting Sprig helper functions into email templates.

In the world of self-hosted software, 'convenience' is often a polite synonym for 'security hole.' Listmonk, a high-performance newsletter manager written in Go, fell victim to this classic trade-off. By importing the entire Sprig template library without adequate filtering, the application allowed authenticated users to perform Server-Side Template Injection (SSTI). This wasn't just a minor UI glitch; it gave attackers a direct line to the server's environment variables, exposing database passwords, SMTP credentials, and AWS keys in cleartext.

The Hook: When 'Feature-Rich' Becomes 'Leak-Rich'

Listmonk is the darling of the self-hosted marketing world. It’s fast, written in Go, and lets you send millions of emails without paying a cent to Mailchimp. To make emails dynamic—like greeting a user by name or formatting dates—it uses Go's native templating engine. But native Go templates are a bit bare-bones.

To spice things up, the developers integrated Sprig, a library that adds over 100 convenience functions to Go templates. Need to base64 encode a string? Sprig has you covered. Need to format a timestamp? Easy.

But here is the problem: Sprig also includes functions that have absolutely no business being accessible inside a marketing email template. Functions like env (read environment variables) and expandenv (expand system paths). By exposing the default Sprig function map to the user, listmonk effectively turned its template editor into a system shell's printenv command. It’s like installing a smart lock on your front door but leaving a key under the mat, and then broadcasting the location of the mat on a billboard.

The Flaw: The Kitchen Sink Approach

The root cause of CVE-2025-49136 is a classic case of Implicit Trust. The developers trusted the Sprig library to be safe by default. In reality, Sprig is a general-purpose utility belt; it contains sharp knives alongside the harmless sporks.

When listmonk initialized the template engine for campaigns, it loaded the generic Sprig function map. This map includes:

  • os.Getenv: Wraps the system call to read environment variables.
  • os.ExpandEnv: Replaces ${var} in strings with their environment values.

In a secure context, like a CLI tool running on your laptop, these are fine. In a web application where users (even trusted marketing interns) define the template content, this is catastrophic. The application failed to curate this list, blindly passing the ability to read server memory into the hands of the user. It is a textbook breakdown of the Principle of Least Privilege.

The Code: Deleting the Dangerous bits

Let's look at the smoking gun. The vulnerability existed because the code grabbed the default map and served it up raw. The fix, applied in version 5.0.2, is elegantly simple: it manually deletes the dangerous keys from the map before the template engine ever sees them.

Here is the diff from internal/manager/manager.go:

func (m *Manager) makeGnericFuncMap() template.FuncMap {
    // ... existing setup ...
 
    // VULNERABLE CODE (Conceptual Previous State)
    // sprigFuncs := sprig.GenericFuncMap()
    // maps.Copy(funcs, sprigFuncs)
 
    // PATCHED CODE (v5.0.2)
    sprigFuncs := sprig.GenericFuncMap()
    
    // The fix: Explicitly nuke the dangerous functions
    delete(sprigFuncs, "env")
    delete(sprigFuncs, "expandenv")
 
    maps.Copy(funcs, sprigFuncs)
    return funcs
}

> [!NOTE] > This is a "allow-list via deny-list" approach. While effective here, a more robust security posture would be to explicitly allow only the functions you need (e.g., string manipulation and math) rather than trying to delete the ones you know are bad. Who knows what other weird functions Sprig might add in the future?

The Exploit: Hello, {{ env "DB_PASSWORD" }}

Exploiting this requires authenticated access, but don't let that lower your blood pressure. In many organizations, the person managing newsletters has weak credentials, no MFA, or is a third-party contractor. Once inside, the escalation to full infrastructure compromise is trivial.

The Attack Chain:

  1. Access: Log in as a user with campaigns or templates permissions.
  2. inject: Create a new Campaign. In the body editor (switch to Raw HTML to be safe), insert the payload.
  3. Payload Construction: You don't need to guess variable names. Standard Docker setups for listmonk use predictable keys.
<!-- The "I own your database" Payload -->
<p>Welcome to our newsletter!</p>
<p>Debug Info: {{ env "LISTMONK_db__password" }}</p>
<p>AWS Key: {{ env "AWS_SECRET_ACCESS_KEY" }}</p>
  1. Trigger: Click the "Preview" button.
  2. Exfiltration: The server parses the template, executes os.Getenv("LISTMONK_db__password"), and renders the cleartext password right into the preview pane in your browser.

If the attacker wants to be subtle, they could send a "Test Email" to themselves. The server will render the secrets into the email body and kindly deliver them to the attacker's inbox via the configured SMTP server.

The Impact: Why You Should Panic

This is not just about reading the newsletter configuration. Because listmonk is a modern, 12-factor app, it is almost exclusively configured via Environment Variables.

If you run listmonk in Kubernetes or Docker, the process environment likely contains:

  • Database Credentials: Full read/write access to your user data.
  • SMTP Credentials: The ability to send spoofed emails from your domain, or reset passwords on other platforms via email loops.
  • Object Storage Keys: AWS S3 or MinIO credentials used for uploads. An attacker could use these to steal backups or host malware on your domain.

CVSS 9.1 is arguably conservative. If your listmonk instance shares an environment with other services (e.g., a monolithic .env file mounted into multiple containers), this vulnerability compromises your entire stack.

The Fix: Patch and Rotate

The remediation is two-fold. First, stop the bleeding. Second, assume the patient has already lost blood.

Step 1: Update Software Pull the latest Docker image or build from source. You need version v5.0.2 or higher. There is no config change required; the binary itself has been patched to disable the template functions.

Step 2: Rotate Secrets (Mandatory) If you were running a vulnerable version exposed to the internet (or untrusted internal users), you must treat your environment variables as compromised.

  • Change your PostgreSQL password.
  • Generate new SMTP API keys.
  • Rotate AWS IAM keys.

Simply patching the software closes the window, but it doesn't chase the burglar out of the house if they already stole the keys.

Official Patches

listmonkRelease v5.0.2 containing the fix

Fix Analysis (1)

Technical Appendix

CVSS Score
9.1/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H
EPSS Probability
41.27%
Top 3% most exploited

Affected Systems

listmonk < 5.0.2

Affected Versions Detail

Product
Affected Versions
Fixed Version
listmonk
knadh
>= 4.0.0, < 5.0.25.0.2
AttributeDetail
CWECWE-1336 (Improper Neutralization of Special Elements Used in a Template Engine)
Attack VectorNetwork (Authenticated)
CVSS v3.19.1 (Critical)
ImpactInformation Disclosure (High), System Compromise
Exploit StatusPoC Available
ArchitectureGo (text/template + Sprig)

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1552.001Unsecured Credentials: Environment Variables
Credential Access
CWE-1336
Improper Neutralization of Special Elements Used in a Template Engine

Known Exploits & Detection

GitHub Security AdvisoryOriginal advisory containing payload examples and impact analysis.

Vulnerability Timeline

Internal identification of Sprig misconfiguration
2025-05-25
Patch committed by maintainer
2025-06-08
Public disclosure (GHSA) and Release v5.0.2
2025-06-09

References & Sources

  • [1]NVD - CVE-2025-49136
  • [2]Fix Commit on GitHub

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.