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.
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.
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();...
3.1.6Exploitability
AV:NAC:LAT:PPR:NUI:PVulnerable System
VC:LVI:NVA:NSubsequent System
SC:NSI:NSA:N2.3/CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N