CVE-2025-49136

Listmonk SSTI: When Helper Libraries Help You Get Hacked

Alon Barad
Alon Barad
Software Engineer

Jan 6, 2026·5 min read

Executive Summary (TL;DR)

Listmonk included the 'Sprig' template library without sanitization. This allowed any user with permission to create email templates to use the `{{ env }}` function. This function dumps the server's environment variables, handing over DB passwords, AWS keys, and config secrets to anyone who can preview an email. Fixed in v5.0.2.

A critical Server-Side Template Injection (SSTI) vulnerability in listmonk allows authenticated users to read host environment variables via the Sprig template library, exposing database credentials and cloud secrets.

The Hook: The Newsletter That Knew Too Much

Listmonk is the go-to self-hosted solution for newsletters. It’s fast, written in Go, and generally robust. But like many modern Go applications, it relies on the text/template and html/template packages to render dynamic content. To make life easier for users who want to do fancy things in their email subjects—like capitalizing names or formatting dates—developers often reach for Sprig.

Sprig is a collection of over 100 template functions for Go. It's incredibly useful. It's also dangerous if you blindly trust it. The developers of listmonk imported the entire Sprig feature set into the user-accessible template engine. This is akin to giving a house guest the keys to your car just because they asked for a ride to the store. You gave them way more access than the context required.

This vulnerability (CVE-2025-49136) isn't a complex buffer overflow or a race condition. It's a classic case of "Default Configurations Considered Harmful." By enabling Sprig's default function map, listmonk accidentally gave every marketing intern with a login the ability to read the server's deepest secrets.

The Flaw: A Dangerous Import

The root cause lies in how listmonk initializes its template engine. Go's template engine allows developers to pass a FuncMap—a map of function names to actual Go functions that can be called inside the {{ }} delimiters. Sprig provides a convenient method called GenericFuncMap() which returns all its supported functions.

Among innocent helpers like upper (uppercase) or date, Sprig includes env and expandenv. These functions are designed for DevOps tools (like Helm) where reading environment variables is a feature, not a bug. In a multi-tenant or multi-user web application, however, env is catastrophic.

The vulnerability exists because listmonk took this map and applied it directly to the campaign rendering context. There was no filter list, no deny list, just a raw copy-paste of functionality. This means the template engine went from being a text formatter to a system inspection tool.

The Code: The Smoking Gun

Let's look at the code. In the vulnerable versions (4.0.0 through 5.0.1), the manager initialized the function map roughly like this:

// The Vulnerable Implementation
func (m *Manager) makeGenericFuncMap() template.FuncMap {
    // Load all Sprig functions, including dangerous ones
    funcs := sprig.GenericFuncMap()
    // Merge with internal listmonk functions
    // ...
    return funcs
}

The fix, applied in commit d27d2c32cf3af2d0b24e29ea5a686ba149b49b3e, is a textbook example of "sanitize your inputs" (or in this case, your imports). The developers explicitly delete the dangerous keys from the map before returning it.

// The Fixed Implementation (v5.0.2)
func (m *Manager) makeGenericFuncMap() template.FuncMap {
    sprigFuncs := sprig.GenericFuncMap()
    
    // explicit kill list
    delete(sprigFuncs, "env")
    delete(sprigFuncs, "expandenv")
 
    maps.Copy(funcs, sprigFuncs)
    return funcs
}

It’s a simple three-line change that closes a massive security hole. It highlights the importance of auditing third-party libraries not just for CVEs, but for features that become vulnerabilities in your specific context.

The Exploit: Reading the Keys to the Kingdom

Exploiting this is trivially easy. You don't need to be a binary ninja; you just need a login with "Campaign" permissions. The attack vector leverages the "Preview" functionality found in the campaign editor.

Step 1: Log in as a low-privileged user.

Step 2: Create a new Campaign.

Step 3: In the body of the email, insert the following Sprig payload:

Whoops, I found your database password: 
{{ env "LISTMONK_db__password" }}
 
And your AWS keys:
{{ env "AWS_SECRET_ACCESS_KEY" }}

Step 4: Click the "Preview" tab (the eye icon).

The Result: The backend Go code processes the template. When it hits {{ env ... }}, it executes os.Getenv(...) on the server and pastes the result directly into the HTML preview. The attacker now has full credentials to the backend database or cloud provider.

[!NOTE] A Metasploit module (gather/listmonk_env_disclosure) already automates this, cycling through common environment variable names like POSTGRES_PASSWORD, AWS_ACCESS_KEY_ID, and LISTMONK_app__admin_password.

The Impact: Why This is Critical

In modern containerized environments (Docker, Kubernetes), configuration is almost exclusively passed via Environment Variables. This is factor III of the "12-Factor App" methodology. While this is great for DevOps, it means that os.Getenv is effectively root access to the application's configuration.

By exploiting this, an attacker gets:

  1. Database Access: LISTMONK_db__password allows direct connection to the Postgres DB, leading to data exfiltration (user emails, PII) or modification.
  2. SMTP Credentials: LISTMONK_smtp__password allows the attacker to send spam/phishing emails from your trusted domain.
  3. Cloud Lateral Movement: If the listmonk instance is running on AWS/GCP and keys are in the env (common in ECS/Lambda), the attacker pivots to your cloud infrastructure.

While this isn't direct Remote Code Execution (RCE) via the template itself, the leaked credentials usually provide five different ways to achieve RCE within minutes.

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
35.14%
Top 3% most exploited
4,500
via Shodan

Affected Systems

listmonk < 5.0.2Docker deployments of listmonkKubernetes deployments of listmonk

Affected Versions Detail

Product
Affected Versions
Fixed Version
listmonk
knadh
>= 4.0.0, < 5.0.25.0.2
AttributeDetail
CWE IDCWE-1336 (Template Injection)
CVSS v3.19.1 (Critical)
Attack VectorNetwork (Authenticated)
EPSS Score0.35141 (High)
Exploit StatusWeaponized (Metasploit)
Affected ComponentSprig Template Library Integration
CWE-1336
Server-Side Template Injection

Improper Neutralization of Special Elements Used in a Template Engine (SSTI)

Vulnerability Timeline

Vulnerability Discovered
2025-05-25
Patch Commit Merged
2025-06-08
CVE Published
2025-06-09
Metasploit Module Released
2025-10-09

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.