Skip to main content
VulnerabilityPublished

CVE-2026-44298: its invoice PDF renderer (admin) · Kimai Has an Arbitrary File Read

Affects kimai/kimaiSource GCVE Database

0
Authoritative record:CVE-2026-44298· Medium 4.1

Not known to be exploited · CVSS 4.1 Medium : upgrade to 2.56 when convenient.

Data as of 27 Oct 2026, 00:07 UTC

CVSS 3.1 base score
4.1Medium
How severe the flaw is on the 0–10 CVSS scale.
Patch
Available – fixed in 2.56
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N

Exploitation evidence

  1. Disclosed: evidence found.

    CVE published 8 May 2026

  2. Detectable: no evidence on record.

    No public nuclei template indexed

  3. Weaponised: no evidence on record.

    No public exploit code indexed in Metasploit or Exploit-DB

  4. Exploited in the wild: no evidence on record.

    Not in CISA KEV and no source reports exploitation

  5. Observed by OffSeq Mirage: no evidence on record.

    No attack traffic recorded by OffSeq Mirage sensors

A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 26 Oct 2026.

What to do

Upgrade kimai/kimai to 2.56.

Affected software

ProductAffectedFixed in
Packagistkimai/kimaiAffected
  • >=2.32.0 <2.56
Fixed in
  • 2.56

Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.

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('<</Type /EmbeddedFile'); ``` The 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. ## Fix The implemented fix has two aspects: 1. The `PdfContext` now works with a strict allow-list, that excludes `associated_files` 2. The `MPdfConverter` now removes any `path` from the `$associatedFiles` array, which can still be used by plugins: ```php if (\count($associatedFiles) > 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); } ```

References

Community reviews

Loading reviews…