How to protect against XML vulnerabilities in .NET
This post is part of the series 'Vulnerabilities'. Be sure to check out the rest of the blog posts of the series!
- Impersonation and security
- SQL injections
- How to prevent CSRF attacks
- ASP MVC and XSRF
- Cross-site scripting (XSS)
- ASP MVC: Mass Assignment
- Regex - Deny of Service (ReDoS)
- Deserialization can be dangerous
- Prevent Zip bombs in a .NET application
- Prevent Zip Slip in .NET
- How to protect against XML vulnerabilities in .NET (this post)
Lots of applications use XML files. For instance, this blog has RSS and Atom feeds that are XML documents. Applications can communicate using SOAP. You can use XML serialization. But most applications use only a subset of the XML features. When you start exploring the rest of the specifications, you can find some possible vulnerabilities when dealing with XML documents! Let's explore some of them and see how to prevent them in a .NET application.
#Deny of service (DOS) using custom XML entities
There are well-known entities in XML such as <
which is translated to <
. But, you can also define custom entities:
<?xml version="1.0" ?>
<!DOCTYPE samples [
<!ENTITY name "value">
]>
<test>&name;</test> <!-- Evaluated to <test>value</test> -->
But you can also define entities that evaluate recursively:
<?xml version="1.0" ?>
<!DOCTYPE samples [
<!ENTITY name "value">
<!ENTITY name2 "&name; &name;">
]>
<test>&name2;</test> <!-- Evaluated to <test>value value</test> -->
Now, let's consider this small document (< 1kB), also known as the billion laughs attack. When evaluating this document, you'll need about 3GB of memory to store the expanded string. Indeed, &lol9;
expands to a string composed of 1 billion "lol".
<?xml version="1.0"?>
<!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>
Another variation of the billion laughs attack that does work is the Quadratic Blowup attack. While not as effective as the previous attack, it does defeat parsers that don't allow recursive expansion. Instead of defining multiple small, deeply nested entities, it defines one very large entity and refers to it many times. A document of about 200kB can expand up to 2.5GB.
<?xml version="1.0"?>
<!DOCTYPE QuadraticBlowup [
<!ENTITY a "aaaaaaaaaaaaaaaaaa...">
]>
<QuadraticBlowup>&a;&a;&a;&a;&a;&a;&a;&a;&a;...</QuadraticBlowup>
#Information disclosure with custom XML entities
Entity expansion also allows you to access local or remote data. An attacker could use this to get the content of a local file or an internal URL.
<!DOCTYPE doc [
<!ENTITY localfile SYSTEM "c:\test.txt">
<!ENTITY remotefile SYSTEM "https://sample/">
]>
<doc>&localfile;</doc>
#Remote code execution using msxsl
XSLT is a language to transform an XML into another XML document. XSLT syntax allows you ta do lots of things depending on the underlying engine. For instance, the msxsl engine on Windows allows you to execute any C# or JavaScript code during the transformation. This means you can access local or remote resources.
<!-- full code (XML + C#): https://gist.github.com/meziantou/2c377432b178ebab17a6802f189adce7 -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="http://dummy/ns">
<msxsl:script language="C#" implements-prefix="user">
<![CDATA[
public string CustomCode()
{
return DateTime.Now.ToString();
}
]]>
</msxsl:script>
<xsl:template match="/">
<xsl:value-of select="user:CustomCode()"/>
</xsl:template>
</xsl:stylesheet>
#Information disclosure with XSLT
You can also copy the content of an existing XML file using document(...)
:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:copy-of select="document('file.xml')" />
</xsl:template>
</xsl:stylesheet>
XSLT 2.0 introduced unparsed-text
. It allows us to read a non-xml file and returns its content as a string. So, once again you can read the content of a file:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:copy-of select="unparsed-text('file.txt')" />
</xsl:template>
</xsl:stylesheet>
#Detect vulnerable XSLT engines
XSLT also allows you to use system-properties
such as <xsl:value-of select="system-property('xsl:product-name')" />
. Using this, you can gather information about the tool that processes the file and maybe detect a vulnerable engine.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="system-property('xsl:version')" />
<xsl:value-of select="system-property('xsl:vendor')" />
<xsl:value-of select="system-property('xsl:vendor-url')" />
<xsl:value-of select="system-property('xsl:product-name')" />
<xsl:value-of select="system-property('xsl:product-version')" />
</xsl:template>
</xsl:stylesheet>
#How to prevent XML vulnerabilities in .NET
To sum up, by parsing XML/XSLT files you are potentially exposed to:
- Deny of service
- Information disclosure
- Remote code execution
In .NET there are many ways to read an XML document: XmlReader
, XmlDocument
, XDocument
, XslTransform
. Most of them are safe by default since .NET 4 but the default values can change in the future. So, I strongly advise you to test your code against the different attacks.
Here're some settings to protect your code:
var readerSettings = new XmlReaderSettings()
{
DtdProcessing = DtdProcessing.Prohibit, // Prohibit or Ignore
XmlResolver = null, // Do not allow to open external resources
};
var reader = XmlReader.Create("file.xml", readerSettings);
var settings = new XsltSettings()
{
EnableScript = false, // Disallow execution of scripts
};
var xslTransform = new XslCompiledTransform(enableDebug: true);
xslTransform.Load(xsl.CreateReader(), settings, stylesheetResolver: null);
Additional resources:
Do you have a question or a suggestion about this post? Contact me!