CVE-2026-50285
Pomerium: Pre-Auth Memory Exhaustion via Unbounded zstd Decompression in HPKE Callback
Pomerium is an identity and context-aware access proxy. Prior to 0.32.8, decodeQueryStringV2 in pkg/hpke/url.go performs zstd decompression of attacker-controlled data without an output-memory limit when DecryptURLValues processes HPKE V2 values for Stateless.Callback in internal/authenticateflow/stateless.go. In hosted or stateless authentication deployments, an unauthenticated attacker can obtain the receiver key from /.well-known/pomerium/hpke-public-key, provide a matching attacker-controlled sender key, and send a compressed payload to /.pomerium/callback that expands before validateSenderPublicKey rejects the sender. This can allocate hundreds of megabytes per request, exhaust proxy memory, crash or degrade the process, and block access to applications protected by the deployment. Stateful deployments are not affected because the stateful callback verifies its HMAC signature before decryption and decompression. This issue is fixed in version 0.32.8.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
7.5
Pomerium: Pre-Auth Memory Exhaustion via Unbounded zstd Decompression in HPKE Callback
Pomerium is an identity and context-aware access proxy. Prior to 0.32.8, decodeQueryStringV2 in pkg/hpke/url.go performs zstd decompression of attacker-controlled data without an output-memory limit when DecryptURLValues processes HPKE V2 values for Stateless.Callback in internal/authenticateflow/stateless.go. In hosted or stateless authentication deployments, an unauthenticated attacker can obtain the receiver key from /.well-known/pomerium/hpke-public-key, provide a matching attacker-controlled sender key, and send a compressed payload to /.pomerium/callback that expands before validateSenderPublicKey rejects the sender. This can allocate hundreds of megabytes per request, exhaust proxy memory, crash or degrade the process, and block access to applications protected by the deployment. Stateful deployments are not affected because the stateful callback verifies its HMAC signature before decryption and decompression. This issue is fixed in version 0.32.8.
- Record
CVE-2026-50285- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-1284, CWE-770
- Updated
- 2026-11-02
EUVDEUVD-2026-82410
7.5
Pomerium is an identity and context-aware access proxy. Prior to 0.32.8, decodeQueryStringV2 in pkg/hpke/url.go performs zstd decompression of attacker-controlled data without an output-memory limit when DecryptURLValues processes HPKE V2 values for Stateless.Callback in internal/authenticateflow/stateless.go. In hosted or stateless authentication deployments, an unauthenticated attacker can obtain the receiver key from /.well-known/pomerium/hpke-public-key, provide a matching attacker-controlled sender key, and send a compressed payload to /.pomerium/callback that expands before validateSenderPublicKey rejects the sender. This can allocate hundreds of megabytes per request, exhaust proxy memory, crash or degrade the process, and block access to applications protected by the deployment. Stateful deployments are not affected because the stateful callback verifies its HMAC signature before decryption and decompression. This issue is fixed in version 0.32.8.
- Record
EUVD-2026-82410- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-ggw3-5987-rx77
7.5
Pomerium Pre-Auth Memory Exhaustion via Unbounded zstd Decompression in HPKE Callback
Summary
The HPKE V2 URL decode path in pkg/hpke/url.go decompresses attacker-controlled zstd data without any size limit. On Pomerium deployments using the stateless authentication flow (Pomerium Zero / hosted authenticate), the proxy's /.pomerium/callback endpoint is reachable without credentials and processes attacker-crafted HPKE-encrypted payloads before the sender's identity is validated. Because Pomerium's HPKE receiver public key is publicly served, an attacker can encrypt a decompression bomb, deliver it to the callback endpoint, and cause unbounded memory allocation — crashing or degrading the proxy process.
Severity
High (CVSS 3.1: 7.5)
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Attack Vector: Network — the
/.pomerium/callbackroute on the proxy service is externally reachable. - Attack Complexity: Low — the receiver public key is publicly available at
/.well-known/pomerium/hpke-public-key; no special conditions apply. - Privileges Required: None — the callback endpoint is intentionally pre-authentication (it is the OAuth landing page).
- User Interaction: None
- Scope: Unchanged — the DoS is confined to the Pomerium proxy process itself.
- Confidentiality Impact: None
- Integrity Impact: None
- Availability Impact: High — repeated attacks can exhaust process memory and crash the proxy.
Affected Component
pkg/hpke/url.go—decodeQueryStringV2(line 171)internal/authenticateflow/stateless.go—Callback(line 385–393)proxy/handlers.go—Callback(line 105–107), route registered at line 53–54
CWE
- CWE-400: Uncontrolled Resource Consumption
- CWE-1284: Improper Validation of Specified Quantity in Input
Description
Unbounded zstd Decompression in decodeQueryStringV2
pkg/hpke/url.go defines two decoders. The V1 path is plaintext. The V2 path zstd-compresses the query string before encryption. Decoding reverses this with no output size cap (url.go:166–176):
var zstdDecoder, _ = zstd.NewReader(nil,
zstd.WithDecoderLowmem(true),
)
func decodeQueryStringV2(raw []byte) (url.Values, error) {
bs, err := zstdDecoder.DecodeAll(raw, nil) // no size limit
if err != nil {
return nil, err
}
return url.ParseQuery(string(bs))
}
WithDecoderLowmem(true) reduces the decoder's own memory footprint but applies no cap on the output. A 19 KB input can produce 128 MiB of output; a 38 KB input can produce 256 MiB.
By contrast, the codebase applies LimitReader when decompressing in internal/zero/api/download.go:75:
r = io.LimitReader(zr, maxUncompressedBlobSize) // 1 GB cap
The protection is available but not applied to decodeQueryStringV2, confirming this is an inconsistent defense.
HPKE Does Not Block the Attack — Sender Validation Is Too Late
DecryptURLValues for the V2 format (url.go:107–126):
case IsEncryptedURLV2(encrypted):
senderPublicKey, err = PublicKeyFromString(encrypted.Get(paramSenderPublicKeyV2)) // attacker-controlled
// ...
sealed, err := decode(encrypted.Get(paramQueryV2))
// ...
message, err := Open(receiverPrivateKey, senderPublicKey, sealed) // HPKE decrypt — succeeds
// ...
decrypted, err = decodeQueryStringV2(message) // zstd decompress — UNBOUNDED
Open uses SetupAuth (HPKE authenticated mode). It only verifies that sealed was created with a key pair whose public half is senderPublicKey. Because the attacker supplies both k (sender public key) and q (sealed payload), they choose a consistent key pair themselves. The Open call succeeds with their own freshly-generated keys.
Sender identity is validated after DecryptURLValues returns (stateless.go:391–397):
senderPublicKey, values, err := hpke.DecryptURLValues(s.hpkePrivateKey, r.Form)
// ... zstd already completed ...
err = s.validateSenderPublicKey(r.Context(), senderPublicKey) …
- Record
GHSA-ggw3-5987-rx77- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-1284
- Updated
- 2026-11-02
NVD
7.5
Pomerium is an identity and context-aware access proxy. Prior to 0.32.8, decodeQueryStringV2 in pkg/hpke/url.go performs zstd decompression of attacker-controlled data without an output-memory limit when DecryptURLValues processes HPKE V2 values for Stateless.Callback in internal/authenticateflow/stateless.go. In hosted or stateless authentication deployments, an unauthenticated attacker can obtain the receiver key from /.well-known/pomerium/hpke-public-key, provide a matching attacker-controlled sender key, and send a compressed payload to /.pomerium/callback that expands before validateSenderPublicKey rejects the sender. This can allocate hundreds of megabytes per request, exhaust proxy memory, crash or degrade the process, and block access to applications protected by the deployment. Stateful deployments are not affected because the stateful callback verifies its HMAC signature before decryption and decompression. This issue is fixed in version 0.32.8.
- Record
CVE-2026-50285- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-770, CWE-1284
- Updated
- 2026-11-02
OSV
7.5
Pomerium: Pre-Auth Memory Exhaustion via Unbounded zstd Decompression in HPKE Callback
Pomerium is an identity and context-aware access proxy. Prior to 0.32.8, decodeQueryStringV2 in pkg/hpke/url.go performs zstd decompression of attacker-controlled data without an output-memory limit when DecryptURLValues processes HPKE V2 values for Stateless.Callback in internal/authenticateflow/stateless.go. In hosted or stateless authentication deployments, an unauthenticated attacker can obtain the receiver key from /.well-known/pomerium/hpke-public-key, provide a matching attacker-controlled sender key, and send a compressed payload to /.pomerium/callback that expands before validateSenderPublicKey rejects the sender. This can allocate hundreds of megabytes per request, exhaust proxy memory, crash or degrade the process, and block access to applications protected by the deployment. Stateful deployments are not affected because the stateful callback verifies its HMAC signature before decryption and decompression. This issue is fixed in version 0.32.8.
- Record
CVE-2026-50285- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-1284, CWE-770
- Updated
- 2026-11-02
OSVGHSA-ggw3-5987-rx77
7.5
Pomerium Pre-Auth Memory Exhaustion via Unbounded zstd Decompression in HPKE Callback
Summary
The HPKE V2 URL decode path in pkg/hpke/url.go decompresses attacker-controlled zstd data without any size limit. On Pomerium deployments using the stateless authentication flow (Pomerium Zero / hosted authenticate), the proxy's /.pomerium/callback endpoint is reachable without credentials and processes attacker-crafted HPKE-encrypted payloads before the sender's identity is validated. Because Pomerium's HPKE receiver public key is publicly served, an attacker can encrypt a decompression bomb, deliver it to the callback endpoint, and cause unbounded memory allocation — crashing or degrading the proxy process.
Severity
High (CVSS 3.1: 7.5)
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Attack Vector: Network — the
/.pomerium/callbackroute on the proxy service is externally reachable. - Attack Complexity: Low — the receiver public key is publicly available at
/.well-known/pomerium/hpke-public-key; no special conditions apply. - Privileges Required: None — the callback endpoint is intentionally pre-authentication (it is the OAuth landing page).
- User Interaction: None
- Scope: Unchanged — the DoS is confined to the Pomerium proxy process itself.
- Confidentiality Impact: None
- Integrity Impact: None
- Availability Impact: High — repeated attacks can exhaust process memory and crash the proxy.
Affected Component
pkg/hpke/url.go—decodeQueryStringV2(line 171)internal/authenticateflow/stateless.go—Callback(line 385–393)proxy/handlers.go—Callback(line 105–107), route registered at line 53–54
CWE
- CWE-400: Uncontrolled Resource Consumption
- CWE-1284: Improper Validation of Specified Quantity in Input
Description
Unbounded zstd Decompression in decodeQueryStringV2
pkg/hpke/url.go defines two decoders. The V1 path is plaintext. The V2 path zstd-compresses the query string before encryption. Decoding reverses this with no output size cap (url.go:166–176):
var zstdDecoder, _ = zstd.NewReader(nil,
zstd.WithDecoderLowmem(true),
)
func decodeQueryStringV2(raw []byte) (url.Values, error) {
bs, err := zstdDecoder.DecodeAll(raw, nil) // no size limit
if err != nil {
return nil, err
}
return url.ParseQuery(string(bs))
}
WithDecoderLowmem(true) reduces the decoder's own memory footprint but applies no cap on the output. A 19 KB input can produce 128 MiB of output; a 38 KB input can produce 256 MiB.
By contrast, the codebase applies LimitReader when decompressing in internal/zero/api/download.go:75:
r = io.LimitReader(zr, maxUncompressedBlobSize) // 1 GB cap
The protection is available but not applied to decodeQueryStringV2, confirming this is an inconsistent defense.
HPKE Does Not Block the Attack — Sender Validation Is Too Late
DecryptURLValues for the V2 format (url.go:107–126):
case IsEncryptedURLV2(encrypted):
senderPublicKey, err = PublicKeyFromString(encrypted.Get(paramSenderPublicKeyV2)) // attacker-controlled
// ...
sealed, err := decode(encrypted.Get(paramQueryV2))
// ...
message, err := Open(receiverPrivateKey, senderPublicKey, sealed) // HPKE decrypt — succeeds
// ...
decrypted, err = decodeQueryStringV2(message) // zstd decompress — UNBOUNDED
Open uses SetupAuth (HPKE authenticated mode). It only verifies that sealed was created with a key pair whose public half is senderPublicKey. Because the attacker supplies both k (sender public key) and q (sealed payload), they choose a consistent key pair themselves. The Open call succeeds with their own freshly-generated keys.
Sender identity is validated after DecryptURLValues returns (stateless.go:391–397):
senderPublicKey, values, err := hpke.DecryptURLValues(s.hpkePrivateKey, r.Form)
// ... zstd already completed ...
err = s.validateSenderPublicKey(r.Context(), senderPublicKey) …
- Record
GHSA-ggw3-5987-rx77- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-1284, CWE-400
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
github.com/pomerium/pomeriumGo |
0.32.6 up to 0.32.8 | 0.32.8 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-50285 is a high vulnerability (CVSS 7.5) in Pomerium, published July 15, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.74%, the 54th percentile. Fixed in github.com/pomerium/pomerium 0.32.8.
References
github.com/pomerium/pomerium/security/advisories/GHSA-ggw3-5987-rx77
github.com/pomerium/pomerium/commit/593eb81c7e5bdbe6071a30d330f374967869577f
github.com/pomerium/pomerium/releases/tag/v0.32.8
github.com/advisories/GHSA-ggw3-5987-rx77
github.com/CVEProject/cvelistV5/tree/main/cves/2026/50xxx/CVE-2026-50285.json
nvd.nist.gov/vuln/detail/CVE-2026-50285
github.com/pomerium/pomerium
Left out here: the rest of 2 source descriptions. The JSON record has them.