XML Entity Expansion in Scala
The Scala mechanisms for parsing XML are essentially the same used in Java, so the same examples/descriptions apply.
Vulnerable example #1
object XXE {
private def receiveXMLStream(inStream: InputStream,
defaultHandler: DefaultHandler): Unit = {
val factory: SAXParserFactory = SAXParserFactory.newInstance()
val saxParser: SAXParser = factory.newSAXParser()
saxParser.parse(inStream, defaultHandler)
// process xml data
}
def main(args: Array[String]): Unit = {
try receiveXMLStream(new FileInputStream("evil.xml"), new DefaultHandler())
catch {
case mue: java.net.MalformedURLException =>
System.err.println("Malformed URL Exception: " + mue)
}
}
}
This program is subject to an XML Entity Expansion attack if the evil.xml file contains the following:
<?xml version="1.0" ?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>
After parsing, the evil.xml file will include the contents of the /etc/passwd file of the host where the application is running. The attack can be exploited to exfiltrate arbitrary files, mount server-side request forgery attacks (e.g. port scanning from the network perspective of the victim application) and perform denial of service.
Prevention
Java applications using XML libraries are particularly vulnerable to XXE because the default settings for most Java XML parsers is to have XXE enabled. To use these parsers safely, you have to explicitly disable XXE in the parser you use. The following describes how to disable XXE in the most commonly used XML parsers for Java.
JAXP DocumentBuilderFactory, SAXParserFactory and DOM4J
DocumentBuilderFactory,
SAXParserFactory
and DOM4J
XML
Parsers can be configured using the same techniques to protect them against XXE. The JAXP DocumentBuilderFactory
setFeature method allows a developer to control which implementation-specific XML processor features are enabled or disabled. The features can either be set on the factory or the underlying XMLReader
setFeature method. Each XML processor implementation has its own features that govern how DTDs and external entities are processed. For a syntax highlighted example code snippet using SAXParserFactory
, look here.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
String FEATURE = null;
try {
// This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all
// XML entity attacks are prevented
// Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
dbf.setFeature(FEATURE, true);
// If you can't completely disable DTDs, then at least do the following:
// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
// JDK7+ - http://xml.org/sax/features/external-general-entities
FEATURE = "http://xml.org/sax/features/external-general-entities";
dbf.setFeature(FEATURE, false);
// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
// JDK7+ - http://xml.org/sax/features/external-parameter-entities
FEATURE = "http://xml.org/sax/features/external-parameter-entities";
dbf.setFeature(FEATURE, false);
// Disable external DTDs as well
FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
dbf.setFeature(FEATURE, false);
// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
// And, per Timothy Morgan: "If for some reason support for inline DOCTYPEs are a requirement, then
// ensure the entity settings are disabled (as shown above) and beware that SSRF attacks
// (http://cwe.mitre.org/data/definitions/918.html) and denial
// of service attacks (such as billion laughs or decompression bombs via "jar:") are a risk."
// remaining parser logic
} catch (ParserConfigurationException e) {
// This should catch a failed setFeature feature
logger.info("ParserConfigurationException was thrown. The feature '" + FEATURE
+ "' is probably not supported by your XML processor.");
} catch (SAXException e) {
// On Apache, this should be thrown when disallowing DOCTYPE
logger.warning("A DOCTYPE was passed into the XML document");
} catch (IOException e) {
// XXE that points to a file that doesn't exist
logger.error("IOException occurred, XXE may still possible: " + e.getMessage());
}
// Load XML file or stream using a XXE agnostic configured parser
DocumentBuilder safebuilder = dbf.newDocumentBuilder();
- Do not include external entities by setting the feature http://xerces.apache.org/xerces-j/features.html#external-general-entities to
false
. - Do not include parameter entities by setting http://xerces.apache.org/xerces-j/features.html#external-parameter-entities to
false
. - Do not include external DTDs by setting http://xerces.apache.org/xerces-j/features.html#load-external-dtd to
false
.
- Disallow an inline DTD by setting http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl to
true
. - Do not include external entities by setting http://xerces.apache.org/xerces2-j/features.html#external-general-entities to
false
. - Do not include parameter entities by setting http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities to
false
. - Do not include external DTDs by setting http://xerces.apache.org/xerces-j/features.html#load-external-dtd to
false
.
Note: The above defenses require Java 7 update 67, Java 8 update 20, or above, because the above countermeasures for DocumentBuilderFactory
and SAXParserFactory are broken in earlier Java versions, per: CVE-2014-6517.
XMLInputFactory (a StAX parser)
StAX parsers such as XMLInputFactory
allow various properties and features to be set. To protect a Java XMLInputFactory
from XXE:
// This disables DTDs entirely for that factory
xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
// disable external entities
xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
TransformerFactory
To protect a javax.xml.transform.TransformerFactory
from XXE:
TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
Validator
To protect a javax.xml.validation.Validator
from XXE:
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema();
Validator validator = schema.newValidator();
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
SchemaFactory
To protect a javax.xml.validation.SchemaFactory
from XXE:
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
Schema schema = factory.newSchema(Source);
SAXTransformerFactory
To protect a javax.xml.transform.sax.SAXTransformerFactory
from XXE:
SAXTransformerFactory sf = SAXTransformerFactory.newInstance();
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
sf.newXMLFilter(Source);
Note: Use of the following XMLConstants
requires JAXP 1.5, which was added to Java in 7u40 and Java 8:
javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD
javax.xml.XMLConstants.ACCESS_EXTERNAL_SCHEMA
javax.xml.XMLConstants.ACCESS_EXTERNAL_STYLESHEET
XMLReader
To protect a Java org.xml.sax.XMLReader
from XXE:
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// This may not be strictly required as DTDs shouldn't be allowed at all, per previous line.
reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXReader
To protect a Java org.dom4j.io.SAXReader
from XXE:
saxReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
saxReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
saxReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
Based on testing, if you are missing one of these, you can still be vulnerable to an XXE attack.
SAXBuilder
To protect a Java org.jdom2.input.SAXBuilder
from XXE:
SAXBuilder builder = new SAXBuilder();
builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);
builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
Document doc = builder.build(new File(fileName));
No-op EntityResolver
For APIs that take an EntityResolver
, you can neutralize an XML parser’s ability to resolve entities by supplying a no-op implementation:
class NoOpEntityResolver extends EntityResolver {
def resolveEntity(publicId: String, systemId: String): InputSource =
new InputSource(new StringReader(""))
}
xmlReader.setEntityResolver(new NoOpEntityResolver())
documentBuilder.setEntityResolver(new NoOpEntityResolver())
JAXB Unmarshaller
Since a javax.xml.bind.Unmarshaller
parses XML and does not support any flags for disabling XXE, it is imperative to parse the untrusted XML through a configurable secure parser first, generate a source object as a result, and pass the source object to the Unmarshaller. For example:
//Disable XXE
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
//Do unmarshall operation
Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(),
new InputSource(new StringReader(xml)));
JAXBContext jc = JAXBContext.newInstance(Object.class);
Unmarshaller um = jc.createUnmarshaller();
um.unmarshal(xmlSource);
XPathExpression
A javax.xml.xpath.XPathExpression
is similar to an Unmarshaller, wherein it can’t be configured securely by itself, so the untrusted data must be parsed through another securable XML parser first. For example:
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
DocumentBuilder builder = df.newDocumentBuilder();
String result = new XPathExpression().evaluate( builder.parse(
new ByteArrayInputStream(xml.getBytes())) );
java.beans.XMLDecoder
The readObject() method in this class is fundamentally unsafe. Not only is the XML it parses subject to XXE, but the method can be used to construct any Java object, and execute arbitrary code as described here. Importantly, there is no way to make safe use of this class except to trust or properly validate the input being passed into it. As such, we strongly recommend completely avoiding the use of this class and replacing it with a safe or properly configured XML parser as described elsewhere in this cheat sheet.
Other XML Parsers
There are many 3rd party libraries that parse XML either directly or through their use of other libraries. Please test and verify that their XML parser is secure against XXE by default. If the parser is not secure by default, look for flags supported by the parser to disable all possible external resource inclusions like the examples given above. If there is no control exposed to the outside, make sure the untrusted content is passed through a secure parser first and then passed to insecure 3rd party parser similar to how the Unmarshaller is secured.
Spring Framework MVC/OXM XXE Vulnerabilities
For example, some XXE vulnerabilities were found in Spring OXM and Spring MVC. The following versions of the Spring Framework are vulnerable to XXE:
- 3.0.0 to 3.2.3 (Spring OXM & Spring MVC)
- 4.0.0.M1 (Spring OXM)
- 4.0.0.M1-4.0.0.M2 (Spring MVC)
There were other issues as well that were fixed later, so to fully address these issues, Spring recommends you upgrade to Spring Framework 3.2.8+ or 4.0.2+. For Spring OXM, this is referring to the use of org.springframework.oxm.jaxb.Jaxb2Marshaller.
Note that the CVE for Spring OXM specifically indicates that 2 XML parsing situations are up to the developer to get right, and 2 are the responsibility of Spring and were fixed to address this CVE. Here’s what they say: Two situations developers must handle:
- For a
DOMSource
, the XML has already been parsed by user code, and that code is responsible for protecting against XXE. - For a
StAXSource
, the XMLStreamReader has already been created by user code, and that code is responsible for protecting against XXE.
The issue Spring fixed: For SAXSource and StreamSource instances, Spring processed external entities by default, thereby creating this vulnerability. Here’s an example of using a StreamSource that was vulnerable, but is now safe, if you are using a fixed version of Spring OXM or Spring MVC:
import org.springframework.oxm.Jaxb2Marshaller;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// Must cast return Object to whatever type you are unmarshalling
marshaller.unmarshal(new StreamSource(new StringReader(some_string_containing_XML));
So, per the Spring OXM CVE writeup, the above is now safe. But if you were to use a DOMSource or StAXSource instead, it would be up to you to configure those sources to be safe from XXE.
References
OWASP - XML External Entity (XXE) Processing OWASP - XML External Entity Prevention Cheat Sheet CMU - IDS17-J. Prevent XML External Entity Attacks