{"id":"CVE-2026-44298","aliases":["EUVD-2026-28517","GHSA-h5fh-7hwr-97mw"],"status":"published","title":"Kimai: Arbitrary file read in invoice PDF renderer (admin)","description":"Kimai is an open-source time tracking application. From version 2.32.0 to before version 2.56.0, users with the role System-Admin (ROLE_SYSTE_ADMIN) and the permission upload_invoice_template can upload PDF invoice templates, which can call pdfContext.setOption('associated_files', ...) inside the sandboxed Twig render. This is forwarded to mPDF's SetAssociatedFiles(), whose writer calls file_get_contents($entry['path']) during PDF output and embeds the bytes as a FlateDecode stream in the PDF. Any file readable by the PHP worker is returned to the attacker inside the rendered invoice. This issue has been patched in version 2.56.0.","severity":{"version":"3.1","score":4.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00278,"percentile":0.20538,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22"],"published":"2026-05-08T03:32:06Z","modified":"2026-11-11T23:10:00Z","fixes":[{"ecosystem":"Packagist","package":"kimai/kimai","fixed_in":["2.56"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"kimai/kimai","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.32.0"},{"last_affected":"2.55"},{"fixed":"2.56"}]}]},{"source":"osv","ecosystem":"Packagist","package":"kimai/kimai","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.32.0"},{"fixed":"2.56"}]}],"versions":["2.32.0","2.33.0","2.34.0","2.35.0","2.35.1","2.36.0","2.36.1","2.37.0","2.38.0","2.39.0","2.40.0","2.41.0","2.42.0","2.43.0","2.44.0","2.45.0","2.46.0","2.47.0","2.48.0","2.49.0","2.50.0","2.51.0","2.52.0","2.53.0","2.54.0","2.55.0"]}],"products":[{"vendor":"kimai","product":"kimai"}],"references":[{"url":"https://github.com/kimai/kimai/security/advisories/GHSA-h5fh-7hwr-97mw","tags":["x_refsource_CONFIRM","Mitigation","Vendor Advisory","advisory","web"]},{"url":"https://github.com/kimai/kimai/releases/tag/2.56.0","tags":["x_refsource_MISC","Release Notes","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44298","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-h5fh-7hwr-97mw"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/44xxx/CVE-2026-44298.json","tags":["advisory"]},{"url":"https://github.com/kimai/kimai","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-44298","title":"Kimai: Arbitrary file read in invoice PDF renderer (admin)","description":"Kimai is an open-source time tracking application. From version 2.32.0 to before version 2.56.0, users with the role System-Admin (ROLE_SYSTE_ADMIN) and the permission upload_invoice_template can upload PDF invoice templates, which can call pdfContext.setOption('associated_files', ...) inside the sandboxed Twig render. This is forwarded to mPDF's SetAssociatedFiles(), whose writer calls file_get_contents($entry['path']) during PDF output and embeds the bytes as a FlateDecode stream in the PDF. Any file readable by the PHP worker is returned to the attacker inside the rendered invoice. This issue has been patched in version 2.56.0.","status":"published","published":"2026-05-08T03:32:06.672Z","modified":"2026-05-08T14:06:31.033Z","cvss":[{"version":"3.1","score":4.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T13:43:00.288193Z"}],"euvd":[{"id":"EUVD-2026-28517","description":"Kimai is an open-source time tracking application. From version 2.32.0 to before version 2.56.0, users with the role System-Admin (ROLE_SYSTE_ADMIN) and the permission upload_invoice_template can upload PDF invoice templates, which can call pdfContext.setOption('associated_files', ...) inside the sandboxed Twig render. This is forwarded to mPDF's SetAssociatedFiles(), whose writer calls file_get_contents($entry['path']) during PDF output and embeds the bytes as a FlateDecode stream in the PDF. Any file readable by the PHP worker is returned to the attacker inside the rendered invoice. This issue has been patched in version 2.56.0.","status":"published","published":"2026-05-08T03:32:06Z","modified":"2026-05-08T14:06:31Z","cvss":[{"version":"3.1","score":4.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T19:05:14.505765Z"}],"ghsa":[{"id":"GHSA-h5fh-7hwr-97mw","title":"Kimai has an arbitrary file read in its invoice PDF renderer (admin)","description":"## Summary\n\nUsers with the role `System-Admin` (`ROLE_SYSTE_ADMIN`) and the permission `upload_invoice_template` can upload PDF invoice templates, which can call `pdfContext.setOption('associated_files', ...)` inside the sandboxed Twig render. \n\nThis is forwarded to mPDF's `SetAssociatedFiles()`, whose writer calls `file_get_contents($entry['path'])` during PDF output and embeds the bytes as a FlateDecode stream in the PDF. Any file readable by the PHP worker is returned to the attacker inside the rendered invoice.\n\n## Root cause\n\n1. `src/Twig/SecurityPolicy/StrictPolicy.php:123-128` explicitly whitelists `PdfContext::setOption()`:\n   ```php\n   if ($obj instanceof PdfContext) {\n       if ($lcm !== 'setoption') { throw ...; }\n       return;\n   }\n   ```\n\n2. `src/Pdf/MPdfConverter.php` keeps `associated_files` in the pass-through allowlist:\n   ```php\n   $allowed = ['mode','format','default_font_size','default_font', ... , 'associated_files','additional_xmp_rdf'];\n   ```\n   and then forwards it to mPDF:\n   ```php\n   if (array_key_exists('associated_files', $options) \u0026\u0026 is_array($options['associated_files'])) {\n       $associatedFiles = $options['associated_files'];\n       unset($options['associated_files']);\n   }\n   ...\n   $mpdf-\u003eSetAssociatedFiles($associatedFiles);\n   ```\n\n3. mPDF 8.3.1 `MetadataWriter::writeAssociatedFiles()` calls `file_get_contents`, which respects PHP stream wrappers:\n   ```php\n   if (isset($file['path'])) {\n       $fileContent = @file_get_contents($file['path']);\n   }\n   ...\n   $filestream = gzcompress($fileContent);\n   $this-\u003ewriter-\u003ewrite('\u003c\u003c/Type /EmbeddedFile');\n   ```\n\nThe sandbox and the option allowlist were both written defensively (short whitelists, not blacklists), but neither side considered that `associated_files` is a PDF/A file-embedding feature whose `path` key is a sink.\n\n## Fix\n\nThe implemented fix has two aspects:\n\n1. The `PdfContext` now works with a strict allow-list, that excludes `associated_files`\n2. The `MPdfConverter` now removes any `path` from the `$associatedFiles` array, which can still be used by plugins:\n```php\n        if (\\count($associatedFiles) \u003e 0) {\n            // remove \"path\" so mPDF will not use file_get_contents() on local files\n            // callers must pre-read and pass the bytes via \"content\"\n            $associatedFiles = array_map(static function ($entry): array {\n                if (!\\is_array($entry)) {\n                    return [];\n                }\n\n                if (\\array_key_exists('path', $entry)) {\n                    unset($entry['path']);\n                }\n\n                return $entry;\n            }, $associatedFiles);\n            $mpdf-\u003eSetAssociatedFiles($associatedFiles);\n        }\n\n```","status":"published","published":"2026-05-08T22:22:36Z","modified":"2026-05-08T22:22:38Z","cvss":[{"version":"3.1","score":4.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:40:05.816188Z"}],"nvd":[{"id":"CVE-2026-44298","description":"Kimai is an open-source time tracking application. From version 2.32.0 to before version 2.56.0, users with the role System-Admin (ROLE_SYSTE_ADMIN) and the permission upload_invoice_template can upload PDF invoice templates, which can call pdfContext.setOption('associated_files', ...) inside the sandboxed Twig render. This is forwarded to mPDF's SetAssociatedFiles(), whose writer calls file_get_contents($entry['path']) during PDF output and embeds the bytes as a FlateDecode stream in the PDF. Any file readable by the PHP worker is returned to the attacker inside the rendered invoice. This issue has been patched in version 2.56.0.","status":"published","published":"2026-05-08T04:16:24.23Z","modified":"2026-11-11T23:10:00.263Z","cvss":[{"version":"3.1","score":4.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N","level":"medium","assessor":"secondary"},{"version":"3.1","score":4.9,"vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"primary"}],"cwes":["CWE-22"],"updated_at":"2026-11-12T01:26:59.692044Z"}],"osv":[{"id":"CVE-2026-44298","title":"Kimai: Arbitrary file read in invoice PDF renderer (admin)","description":"Kimai is an open-source time tracking application. From version 2.32.0 to before version 2.56.0, users with the role System-Admin (ROLE_SYSTE_ADMIN) and the permission upload_invoice_template can upload PDF invoice templates, which can call pdfContext.setOption('associated_files', ...) inside the sandboxed Twig render. This is forwarded to mPDF's SetAssociatedFiles(), whose writer calls file_get_contents($entry['path']) during PDF output and embeds the bytes as a FlateDecode stream in the PDF. Any file readable by the PHP worker is returned to the attacker inside the rendered invoice. This issue has been patched in version 2.56.0.","status":"published","published":"2026-05-08T03:32:06.672Z","modified":"2026-10-28T02:30:54.878434709Z","cvss":[{"version":"3.1","score":4.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:45:04.820169Z"},{"id":"GHSA-h5fh-7hwr-97mw","title":"Kimai has an arbitrary file read in its invoice PDF renderer (admin)","description":"## Summary\n\nUsers with the role `System-Admin` (`ROLE_SYSTE_ADMIN`) and the permission `upload_invoice_template` can upload PDF invoice templates, which can call `pdfContext.setOption('associated_files', ...)` inside the sandboxed Twig render. \n\nThis is forwarded to mPDF's `SetAssociatedFiles()`, whose writer calls `file_get_contents($entry['path'])` during PDF output and embeds the bytes as a FlateDecode stream in the PDF. Any file readable by the PHP worker is returned to the attacker inside the rendered invoice.\n\n## Root cause\n\n1. `src/Twig/SecurityPolicy/StrictPolicy.php:123-128` explicitly whitelists `PdfContext::setOption()`:\n   ```php\n   if ($obj instanceof PdfContext) {\n       if ($lcm !== 'setoption') { throw ...; }\n       return;\n   }\n   ```\n\n2. `src/Pdf/MPdfConverter.php` keeps `associated_files` in the pass-through allowlist:\n   ```php\n   $allowed = ['mode','format','default_font_size','default_font', ... , 'associated_files','additional_xmp_rdf'];\n   ```\n   and then forwards it to mPDF:\n   ```php\n   if (array_key_exists('associated_files', $options) \u0026\u0026 is_array($options['associated_files'])) {\n       $associatedFiles = $options['associated_files'];\n       unset($options['associated_files']);\n   }\n   ...\n   $mpdf-\u003eSetAssociatedFiles($associatedFiles);\n   ```\n\n3. mPDF 8.3.1 `MetadataWriter::writeAssociatedFiles()` calls `file_get_contents`, which respects PHP stream wrappers:\n   ```php\n   if (isset($file['path'])) {\n       $fileContent = @file_get_contents($file['path']);\n   }\n   ...\n   $filestream = gzcompress($fileContent);\n   $this-\u003ewriter-\u003ewrite('\u003c\u003c/Type /EmbeddedFile');\n   ```\n\nThe sandbox and the option allowlist were both written defensively (short whitelists, not blacklists), but neither side considered that `associated_files` is a PDF/A file-embedding feature whose `path` key is a sink.\n\n## Fix\n\nThe implemented fix has two aspects:\n\n1. The `PdfContext` now works with a strict allow-list, that excludes `associated_files`\n2. The `MPdfConverter` now removes any `path` from the `$associatedFiles` array, which can still be used by plugins:\n```php\n        if (\\count($associatedFiles) \u003e 0) {\n            // remove \"path\" so mPDF will not use file_get_contents() on local files\n            // callers must pre-read and pass the bytes via \"content\"\n            $associatedFiles = array_map(static function ($entry): array {\n                if (!\\is_array($entry)) {\n                    return [];\n                }\n\n                if (\\array_key_exists('path', $entry)) {\n                    unset($entry['path']);\n                }\n\n                return $entry;\n            }, $associatedFiles);\n            $mpdf-\u003eSetAssociatedFiles($associatedFiles);\n        }\n\n```","status":"published","published":"2026-05-08T22:22:36Z","modified":"2026-05-08T22:41:25.47268Z","cvss":[{"version":"3.1","score":4.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:46:48.546324Z"}]}}
