CVE-2023-38490 (published 2023-07-27T15:15Z)

Kirby is a content management system. A vulnerability in versions prior to 3.5.8.3, 3.6.6.3, 3.7.5.2, 3.8.4.1, and 3.9.6 only affects Kirby sites that use the `Xml` data handler (e.g. `Data::decode($string, 'xml')`) or the `Xml::parse()` method in site or plugin code. The Kirby core does not use any of the affected methods.\n\nXML External Entities (XXE) is a little used feature in the XML markup language that allows to include data from external files in an XML structure. If the name of the external file can be controlled by an attacker, this becomes a vulnerability that can be abused for various system impacts like the disclosure of internal or confidential data that is stored on the server (arbitrary file disclosure) or to perform network requests on behalf of the server (server-side request forgery, SSRF).\n\nKirby's `Xml::parse()` method used PHP's `LIBXML_NOENT` constant, which enabled the processing of XML external entities during the parsing operation. The `Xml::parse()` method is used in the `Xml` data handler (e.g. `Data::decode($string, 'xml')`). Both the vulnerable method and the data handler are not used in the Kirby core. However they may be used in site or plugin code, e.g. to parse RSS feeds or other XML files. If those files are of an external origin (e.g. uploaded by a user or retrieved from an external URL), attackers may be able to include an external entity in the XML file that will then be processed in the parsing process. Kirby sites that don't use XML parsing in site or plugin code are *not* affected.\n\nThe problem has been patched in Kirby 3.5.8.3, 3.6.6.3, 3.7.5.2, 3.8.4.1, and 3.9.6. In all of the mentioned releases, the maintainers have removed the `LIBXML_NOENT` constant as processing of external entities is out of scope of the parsing logic. This protects all uses of the method against the described vulnerability.

CVSS v3 base score: 10.0, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N)

Weakness information:
cwe_id: CWE-611
cwe_name: Improper Restriction of XML External Entity Reference
description: The product processes an XML document that can contain XML entities with URIs that resolve to documents outside of the intended sphere of control, causing the product to embed incorrect documents into its output.
extended_description: 
            XML documents optionally contain a Document Type Definition (DTD), which, among other features, enables the definition of XML entities. It is possible to define an entity by providing a substitution string in the form of a URI. The XML parser can access the contents of this URI and embed these contents back into the XML document for further processing.
            By submitting an XML file that defines an external entity with a file:// URI, an attacker can cause the processing application to read the contents of a local file. For example, a URI such as "file:///c:/winnt/win.ini" designates (in Windows) the file C:\Winnt\win.ini, or file:///etc/passwd designates the password file in Unix-based systems. Using URIs with other schemes such as http://, the attacker can force the application to make outgoing requests to servers that the attacker cannot reach directly, which can be used to bypass firewall restrictions or hide the source of attacks such as port scanning.
            Once the content of the URI is read, it is fed back into the application that is processing the XML. This application may echo back the data (e.g. in an error message), thereby exposing the file contents.
         
         
cwe_id: CWE-776
cwe_name: Improper Restriction of Recursive Entity References in DTDs ('XML Entity Expansion')
description: The product uses XML documents and allows their structure to be defined with a Document Type Definition (DTD), but it does not properly control the number of recursive definitions of entities.
extended_description: If the DTD contains a large number of nested or recursive entities, this can lead to explosive growth of data when parsed, causing a denial of service.
         

Fix information:
repo_url: https://github.com/getkirby/kirby
commit_details: repo_url: https://github.com/getkirby/kirby
author: Lukas Bestle
author_date: 2023-07-26 21:12:15+02:00
msg: Fix XXE vulnerability

https://github.com/getkirby/kirby/security/advisories/GHSA-q386-w6fg-gmgp
repository: repo_url: https://github.com/getkirby/kirby
repo_name: getkirby/kirby
description: Kirby's core application folder
repo_language: PHP
owner: getkirby
file_changes: filename: Xml.php
change_type: ModificationType.MODIFY
diff: @@ -271,7 +271,7 @@ class Xml
 	 */
 	public static function parse(string $xml): array|null
 	{
-		$xml = @simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);
+		$xml = @simplexml_load_string($xml);
 
 		if (is_object($xml) !== true) {
 			return null;

programming_language: PHP
filename: XmlTest.php
change_type: ModificationType.MODIFY
diff: @@ -139,6 +139,43 @@ class XmlTest extends TestCase
 		$this->assertNull(Xml::parse('<this>is invalid</that>'));
 	}
 
+	/**
+	 * @covers ::parse
+	 */
+	public function testParseEntities()
+	{
+		$xml   = '<!DOCTYPE d [<!ENTITY e "bar">]><x>this is a file: foo &e; (with entities)</x>';
+		$array = Xml::parse($xml);
+
+		$this->assertSame([
+			'@name' => 'x',
+			'@value' => 'this is a file: foo bar (with entities)'
+		], $array);
+	}
+
+	/**
+	 * @covers ::parse
+	 */
+	public function testParseRecursiveEntities()
+	{
+		$xml = file_get_contents(__DIR__ . '/fixtures/xml/billion-laughs.xml');
+		$this->assertNull(Xml::parse($xml));
+	}
+
+	/**
+	 * @covers ::parse
+	 */
+	public function testParseXXE()
+	{
+		$xml   = '<!DOCTYPE d [<!ENTITY e SYSTEM "' . __FILE__ . '">]><x>this is a file: &e; with an XXE vulnerability</x>';
+		$array = Xml::parse($xml);
+
+		$this->assertSame([
+			'@name' => 'x',
+			'@value' => 'this is a file:  with an XXE vulnerability'
+		], $array);
+	}
+
 	/**
 	 * @covers ::encode
 	 * @covers ::decode

programming_language: PHP
filename: billion-laughs.xml
change_type: ModificationType.ADD
diff: @@ -0,0 +1,9 @@
+<!DOCTYPE xml [
+  <!ENTITY lol "lol">
+  <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;">
+  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
+  <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
+]>
+<xml>
+  <p>&lol4;</p>
+</xml>

programming_language: HTML