The Java – XXE billion laughs attack does not appear to have been mitigated as expected by Sonar’s recommended solution to prevent XXE attacks

XXE billion laughs attack does not appear to have been mitigated as expected by Sonar’s recommended solution to prevent XXE attacks… here is a solution to the problem.

XXE billion laughs attack does not appear to have been mitigated as expected by Sonar’s recommended solution to prevent XXE attacks

XXE security threats are not currently available. Ranked 4th on the OWASP Top 10 Web Application Security Threats list, I expect the Java Standard XML Library to prevent such attacks. However, when I use the Validator class in the way Sonar suggests, the rule “XML parser should not be vulnerable to XXE attacks (java:S2755)” (link to rule ):

String xsd = "xxe.xsd";
String xml = "billionlaughs.xml";
StreamSource xsdStreamSource = new StreamSource(xsd);
StreamSource xmlStreamSource = new StreamSource(xml);

SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdStreamSource);
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
 validators will also inherit of these properties
Validator validator = schema.newValidator();

validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");    Compliant
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");    Compliant

StringWriter writer = new StringWriter();
validator.validate(xmlStreamSource, new StreamResult(writer));

With Java 11, billionlaughs.xml is

<?xml version="1.0" encoding="UTF-8"?>
<! DOCTYPE lolz [
<! ENTITY lol "lol">
<! ENTITY lol2 "&lol; &lol; &lol; &lol; &lol; &lol; &lol; &lol; &lol; &lol; ">
<! ENTITY lol3 "&lol2; &lol2; &lol2; &lol2; &lol2; &lol2; &lol2; &lol2; &lol2; &lol2; ">
<! ENTITY lol4 "&lol3; &lol3; &lol3; &lol3; &lol3; &lol3; &lol3; &lol3; &lol3; &lol3; ">
<! ENTITY lol5 "&lol4; &lol4; &lol4; &lol4; &lol4; &lol4; &lol4; &lol4; &lol4; &lol4; ">
<! ENTITY lol6 "&lol5; &lol5; &lol5; &lol5; &lol5; &lol5; &lol5; &lol5; &lol5; &lol5; ">
<! ENTITY lol7 "&lol6; &lol6; &lol6; &lol6; &lol6; &lol6; &lol6; &lol6; &lol6; &lol6; ">
<! ENTITY lol8 "&lol7; &lol7; &lol7; &lol7; &lol7; &lol7; &lol7; &lol7; &lol7; &lol7; ">
<! ENTITY lol9 "&lol8; &lol8; &lol8; &lol8; &lol8; &lol8; &lol8; &lol8; &lol8; &lol8; ">
]>
<lolz>&lol9; </lolz>

I get the following exception:

Exception in thread "main" org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; JAXP00010001: The parser has encountered more than "64000" entity expansions in this document; this is the limit imposed by the JDK.
    at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:204)
    at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:178)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:284)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1413)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1337)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEntityReference(XMLDocumentFragmentScannerImpl.java:1842)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2982)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
    at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:534)
    at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:888)
    at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824)
    at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.StreamValidatorHelper.validate(StreamValidatorHelper.java:176)
    at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.ValidatorImpl.validate(ValidatorImpl.java:115)
    at trial. Trial.main(Trial.java:35)

So my question is, is this considered the right way to mitigate a billion laughter attack (after all, there is a limit of 64,000 entity extensions), or if there is another way to configure XML parsing to simply avoid looking at <! DOCTYPE .. > section.

Solution

OWASP Top Ten entry and SonarSource ruleabout XML External Entities, while “Billion Laughs” attacks are used XML Internal Entities built. Internal entities are defined as:

[…] There is no separate physical storage object, and the content of the entity is given in the declaration.

Java 自 at least Java 1.5 Entity scaling limitations that you encounter.

However, recommended mitigations are still needed to prevent XML external entity attacks. You can test it yourself using one of the examples provided on the OWASP website or in the SonarSource rule. For example, have your validator verify the following (assuming your operating system is Linux):

<pre class=”lang-xml prettyprint-override”><! DOCTYPE foo [
<! ELEMENT foo ANY >
<! ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe; </foo>

Then have your code output the value of your StringWriter writer. You see, without mitigation, it contains the contents of the /etc/passwd file.


For example, OWASP XML External Entity Prevention Cheat Sheet described In some cases, you can also disable DTD (Document Type Definition) entirely to prohibit external and internal entities.

Related Problems and Solutions