GitHub Advisoryadvisory · Packagist

GHSA-h5fh-7hwr-97mw Kimai has an arbitrary file read in its invoice PDF renderer (admin)

MEDIUM Published 2026-05-08 Modified 2026-05-08 Official page ↗
Aliases
CVE-2026-44298
Ecosystem
Packagist
Packages
kimai/kimai
Severity
Moderate
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N
Weaknesses
CWE-22
Reviewed
2026-05-08

Affected packages

• Packagist kimai/kimai: >= 2.32.0, < 2.56 → fixed in 2.56

Description

Summary 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. Root cause 1. `src/Twig/SecurityPolicy/StrictPolicy.php:123-128` explicitly whitelists `PdfContext::setOption()`: ```php if ($obj instanceof PdfContext) { if ($lcm !== 'setoption') { throw ...; } return; } ``` 2. `src/Pdf/MPdfConverter.php` keeps `associated_files` in the pass-through allowlist: ```php $allowed = ['mode','format','default_font_size','default_font', ... , 'associated_files','additional_xmp_rdf']; ``` and then forwards it to mPDF: ```php if (array_key_exists('associated_files', $options) && is_array($options['associated_files'])) { $associatedFiles = $options['associated_files']; unset($options['associated_files']); } ... $mpdf->SetAssociatedFiles($associatedFiles); ``` 3. mPDF 8.3.1 `MetadataWriter::writeAssociatedFiles()` calls `file_get_contents`, which respects PHP stream wrappers: ```php if (isset($file['path'])) { $fileContent = @file_get_contents($file['path']); } ... $filestream = gzcompress($fileContent); $this->writer->write(' 0) { // remove "path" so mPDF will not use file_get_contents() on local files // callers must pre-read and pass the bytes via "content" $associatedFiles = array_map(static function ($entry): array { if (!\is_array($entry)) { return []; } if (\array_key_exists('path', $entry)) { unset($entry['path']); } return $entry; }, $associatedFiles); $mpdf->SetAssociatedFiles($associatedFiles); } ```

Related

Alias · CVE (1)

  • CVE-2026-44298 Kimai is an open-source time tracking application. vulnerability

Weakness · CWE (1)

  • CWE-22 Improper Limitation of a Pathname to a Restricted Directory weakness

References