Tag: web application security
2011
04.26

Summary

Wordpress allows users with Author permissions and above to upload files with a variety of extensions. In some cases, it is possible for a user to mount a cross-site scripting attack using those uploaded files.

How Does It Work?

File uploads are allowed by default for users with Author permissions and above. Wordpress uses a list of file extensions to determine whether a particular upload should be allowed or not. It has a general set of extensions that are allowed for single-site Wordpress installs and a more restrictive set of extensions for multi-site Wordpress installations (formerly known as Wordpress MU). Individual administrators may also change the allowed extensions using the plugin system. There is no validation to make sure the content of an uploaded file matches its extension.

Due to this behavior, a hostile user can mount an XSS attack using the file upload feature in a number of ways; I’ve provided a list of some possibilities below. In cases where an attack will work on a single-site install, I’ve marked it Single-Site: in cases where an attack will work on a multi-site install, I’ve marked it Multi-Site:

  1. (Single-Site) Wordpress allows files with .htm/.html extensions to be uploaded. This is an obvious XSS vector.
  2. (Single-Site) Wordpress allows files with a .txt extension (and other files that can be served up as text/plain by a web browser) to be uploaded. IE6, IE7, IE8, and Safari will all perform content sniffing on files served up as text/plain (source: http://code.google.com/p/browsersec/wiki/Part2#Survey_of_content_sniffing_behaviors). That means HTML included in those files will be executed in IE and Safari.
  3. (Single-Site, Multi-Site) Wordpress allows files with a .swf extension to be uploaded on single-site installs. In addition, SWF applets can be uploaded to the server using any other file extension. If the file is on the same domain as the Wordpress installation, it can make requests back to the Wordpress installation without the need for explicit authorization via a crossdomain.xml file.
  4. (Single-Site, Multi-Site) IE6/7 will sniff for HTML when they encounter “corrupted” images (files served with image/* headers that don’t actually contain valid image data). So, lets say you upload a file with a GIF extension that contains all HTML: Wordpress will accept it, the web server will serve it up as image/gif, and IE6/7 will parse the HTML and be vulnerable to an attack.
  5. (Single-Site, Multi-Site) The configuration of the webserver for certain file extensions may trigger unexpected content-sniffing behaviors in specific browsers. For example, if a file (regardless of extension) gets served up as application/octet-stream, IE, Safari, and Opera will all sniff for HTML (see http://code.google.com/p/browsersec/wiki/Part2#Survey_of_content_sniffing_behaviors).

Note that uploads only allow for cross-site scripting when they are uploaded to the same domain as Wordpress itself. In other instances, while malicious content may be able to be uploaded, it will not be able to make requests back to Wordpress. Regardless, since the default behavior for Wordpress is to upload content into the wp-content/uploads folder on the same domain as the website, any site that has not reconfigured their upload directory is at risk.

This vulnerability is symptomatic of a larger design choice in Wordpress: by allowing the webserver to serve uploaded files directly, Wordpress has seceded control over how those files are presented to the user. This decision has led to an arbitrary code execution vulnerability in the not so distant past and there’s no guarantee that certain server configurations won’t lead to similar vulnerabilities in the future. Existing plugins may even open the door for an attack. Other software, like phpBB, serves all attachments to users through a PHP script: this design decision allows the software to more fully protect users from malicious uploads.

Where do we go from here?

There are a couple different workarounds for people who are hosting uploads from the same domain as their Wordpress installation:

  1. Move your uploaded files to a separate domain (or a subdomain)
    Thanks to the way browsers handle cross-domain communication, moving uploaded files off of your main domain will prevent them from having access to your Wordpress installation, mitigating the impact of this vulnerability. Once you’ve moved your content, you will need to update your Wordpress settings.
    If you’re considering moving your files to a subdomain, keep in mind that under certain circumstances, different subdomains of the same website may actually be able to communicate (ie: by setting document.domain equal to the same value). If you need to allow that kind of communication with your main domain, host your uploads on an entirely separate domain.
  2. Disable file uploads
    If an attacker can’t upload files, they can’t exploit the vulnerability.

In the longer term, there needs to be a discussion about how to balance the simplicity of the current system with the need to be secure.

2011
04.07

Summary

Several days ago, I had to deal with a compromised web application: an attacker had somehow managed to upload PHP backdoor scripts onto the application’s server. Thanks to some log file sleuthing and Google searches, I was quickly able to identify what had allowed the attack: a misconfigured nginx server can allow non-PHP files to be executed as PHP. As I researched the vulnerability a bit more, however, I realized that many of the nginx / PHP setup tutorials found on the Internet suggest that people use vulnerable configurations.

The misconfiguration

As I mentioned, the attack was made possible by a very simple misconfiguration between nginx and php-fastcgi. Consider the configuration block below, taken from a tutorial at http://library.linode.com/web-servers/nginx/php-fastcgi/fedora-14:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
server {
    listen 80;
    server_name www.bambookites.com bambookites.com;
    access_log /srv/www/www.bambookites.com/logs/access.log;
    error_log /srv/www/www.bambookites.com/logs/error.log;
    root /srv/www/www.bambookites.com/public_html;

    location / {
        index  index.html index.htm index.php;
    }

    location ~ \.php$ {
        include /etc/nginx/fastcgi_params;
        fastcgi_pass  127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param  SCRIPT_FILENAME /srv/www/www.bambookites.com/public_html$fastcgi_script_name;
    }
}

It may not be immediately clear, but this configuration block allows for arbitrary code execution under certain circumstances (and I don’t just mean if an attacker can upload a file ending in .php: that kind of vulnerability is independent of the web server used).

Consider a situation where remote users can upload their own pictures to the site. Lets say that an attacker uploads an image to http://www.bambookites.com/uploads/random.gif. What happens, given the server block above, if the attacker then browses to http://www.bambookites.com/uploads/random.gif/somefilename.php?

  1. nginx will look at the URL, see that it ends in .php, and pass the path along to the PHP fastcgi handler.
  2. PHP will look at the path, find the .gif file in the filesystem, and store /somefilename.php in $_SERVER['PATH_INFO'], executing the contents of the GIF as PHP.

Since GIFs and other image types can contain arbitrary content within them, it’s possible to craft a malicious image that contains valid PHP. That is how the attacker was able to compromise the server: he or she uploaded a malicious image containing PHP code to the site, then browsed to the file in a way that caused it to be parsed as PHP.

This issue was first discovered almost a year ago. The original report can be found in Chinese at http://www.80sec.com/nginx-securit.html. There is also a discussion about it on the nginx forums.

This issue can be mitigated in a number of ways, but there are downsides associated with each of the possibilities:

  1. Set cgi.fix_pathinfo to false in php.ini (it’s set to true by default). This change appears to break any software that relies on PATH_INFO being set properly (eg: Wordpress).
  2. Add try_files $uri =404; to the location block in your nginx config. This only works when nginx and the php-fcgi workers are on the same physical server.
  3. Add a new location block that tries to detect malicious URLs. Unfortunately, detecting based on the URL alone is impossible: files don’t necessarily need to have extensions (eg: README, INSTALL, etc).
  4. Explicitly exclude upload directories using an if statement in your location block. The disadvantage here is the use of a blacklist: you have to keep updating your nginx configuration every time you install a new application that allows uploads.
  5. Don’t store uploads on the same server as your PHP. The content is static anyway: serve it up from a separate (sub)domain. Of course, this is easier said than done: not all web applications make this easy to do.

[Note: If anyone is aware of other possible solutions (or workarounds to improve the effectiveness of these solutions), please let me know and I’ll add them here!]

Tutorials

Now, the configuration file for the compromised server wasn’t written by hand. When the server was set up, the configuration was created based on suggestions found on the Internet. I assume that other people use tutorials and walkthroughs for setting up their servers as well. Unfortunately, most of the documentation for configuring nginx and php-fastcgi still encourages people to set up their servers in a vulnerable way.

  1. The default configuration file for nginx suggests the use of an insecure location block (source).
  2. The nginx wiki supplies potentially dangerous examples as well. To be fair, some pages do encourage users to explicitly prevent PHP execution in upload directories [Edit: and in the Pitfalls document, which everyone should read before configuring nginx]. However, other pages ignore the issue entirely.
  3. The Linode Library has an extensive collection of documents, including a number that talk about setting up nginx and PHP on various OSes. Unfortunately, all of those tutorials suggest using a vulnerable configuration for PHP. I’ve contacted the documentation team at Linode and I’m waiting to hear back from them. [Update: The Linode documentation team has updated the tutorials with more information and workarounds]
  4. Howto Forge has several tutorials (1, 2) which show up when searching Google for “nginx php setup.” These tutorials also suggest the use of a vulnerable configuration.
  5. People have written many tutorials on blogs and other sites (ie: 1, 2). A number of these tutorials encourage using the same vulnerable configuration.

In contrast, codex.wordpress.org provides an excellent configuration example that warns people about and mitigates the vulnerability. I’ve reproduced the relevant portion below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ \.php$ {
   # Zero-day exploit defense.
   # http://forum.nginx.org/read.php?2,88845,page=3
   # Won't work properly (404 error) if the file is not stored on this server, which is entirely possible with php-fpm/php-fcgi.
   # Comment the 'try_files' line out if you set up php-fpm/php-fcgi on another machine.  And then cross your fingers that you won't get hacked.
   try_files $uri =404;

   fastcgi_split_path_info ^(.+\.php)(/.+)$;
   include fastcgi_params;
   fastcgi_index index.php;
   fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#    fastcgi_intercept_errors on;
   fastcgi_pass php;
}

Conclusion

  1. If you run PHP on an nginx web server, check your configuration and update if necessary.
  2. If you’re doing a security audit on a PHP application running on an nginx web server, remember to test for this configuration.
  3. If you run across a tutorial that is out of date, please point the author to this post.
  4. If you know of a way to better secure nginx / php-fastcgi, let me know!

Edit: relix, a redditor, has pointed out that nginx lists this exact issue in the “Pitfalls” page on their wiki. I encourage everyone to read through that page!

2011
04.05

Summary

Certain sections of the Google Support Forums (hosted on google.com) were vulnerable to a persistent XSS attack. An attacker could submit posts containing JavaScript URIs in specific locations, triggering the execution of arbitrary JavaScript.

How did it work?

A few of Google’s support forums (for instance, Webmaster Central) allow users to embed external content in their posts. This content includes links to external websites, search results, YouTube videos, etc. When the post is actually submitted to the server, the URL to the content is included in one of the POSTed fields, called wpiprsi (one example looked like 1%26asdf%26%26%26asdf%26http%253Awww.google.com%26%26%26%26%26%26%26%26%26%26%26%26%26%26%26%26). By manipulating the URL that was submitted, it was possible to execute JavaScript.

The simplest example involved links to websites. By modifying the URL in the example above, changing it from http://www.google.com to javascript:alert('works'), it was possible to create a link that would execute JavaScript when clicked. Of course, an XSS vulnerability that requires user interaction like that is less than ideal.

The clickable XSS in action

The clickable XSS vulnerability in action.

The other, more useful attack I found involved the ability to embed videos. All I needed to do was send a request with the URL for a video swapped out with a JavaScript URI. The malicious URI was put into the src attribute of an embed tag, which allowed it to be executed for anyone who viewed the page.

JavaScript alerts could also be executed on page load in certain browsers

JavaScript alerts could also be executed on page load in certain browsers

More Information

The vulnerability mentioned here has been confirmed patched by the Google Security Team. I owe them a ton of thanks for organizing this program and giving me a chance to improve my skills.

Interested readers are encouraged to take a look at other vulnerabilities I’ve reported under Google’s Vulnerability Reward Program.

2011
03.18

Summary

Jaiku was vulnerable to a persistent XSS vulnerability. It would detect and linkify URLs using certain protocol handlers (ie: javascript, data) that could take malicious actions on behalf of an attacker.

How did it work?

The commit that patched the vulnerability can be seen at http://code.google.com/p/jaikuengine/source/detail?r=157.

Jaiku used an overly broad regular expression in an attempt to detect URLs to turn into links. That regular expression allowed people to create links using a number of protocols, including javascript: and data:, which can be used by an attacker to take malicious actions.

The malicious comments, with their evil URLs

The malicious comments, with their evil URLs

More Information

The vulnerability mentioned here has been confirmed patched by the Google Security Team. I owe them a ton of thanks for organizing this program and giving me a chance to improve my skills.

Interested readers are encouraged to take a look at other vulnerabilities I’ve reported under Google’s Vulnerability Reward Program.

2011
03.08

Summary

The Facebook Translations tool’s search feature was vulnerable to a simple reflected XSS attack.

How did it work?

The Translations tool allows users to perform phrase searches within translations. In this case, when a search query returned 0 results, the script displayed a message (“Your search for “YOUR PHRASE HERE” did not match any results.”) which contained unsanitized user input (the search query).

A reflected XSS vulnerability in the search functionality for translations.

A reflected XSS vulnerability in the search functionality for translations.

Why is this important?

  1. The XSS vulnerability was on Facebook.com. An attacker could have used it to access or change information on people’s accounts.
  2. Despite Facebook’s claims that they’ve eliminated XSS vulnerabilities, it’s clear that some portions of the site are better protected than others (ie: Translations was probably not using XHP). Lesser used portions of the site, like the Translations tool, are often the most vulnerable since they’re not updated as often or tested as frequently.

More Information

I want to thank Facebook for responding to my report and fixing the vulnerability in a timely manner. I especially want to thank them for their support of responsible disclosure and their general policy toward whitehat security researcher.

2011
02.10

Summary

addons.mozilla.org was vulnerable to a directory traversal / local file inclusion vulnerability. As a result, it was possible for an attacker to load webserver-readable files from the local filesystem (and to execute PHP stored on the server).

This vulnerability is filed as Bug #628697.

How did it work?

In the PHP code for the addons website, there’s a controller called pages_controller.php that is used to load static / semi-static pages. The exact name of the page to be loaded is determined by the query string: for example, https://addons.mozilla.org/en-US/firefox/pages/credits loads the Site Credits page, which is stored as a template in the system. In older, vulnerable versions of the code, the method for displaying a page looked like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php
function display() {
    if (!func_num_args()) {
        $this->redirect('/');
    }

    $path = func_get_args();
    $path_string = join('/', $path);

    if (!count($path) || ($path[0] == 'home')) {
        $this->redirect('/');
    }

    // ...snip...

    $this->render($path_string);
}

This code is vulnerable to a directory traversal attack: the $path_string, which is used to load a template, is directly tied to user input (the arguments to the function here are the elements of the query string). By sending URL encoded slashes (%252F), it was possible to break out of the current directory and traverse via a relative path to any directory in the system. It was also possible to convince CakePHP (the framework used here) to load files without the .thtml file extension associated with templates by including a URL-encoded null byte (%2500) at the end of the URL.

To give one example of a possible traversal, here is the proof of concept that I included with the bug. It displayed the contents of the /etc/passwd file of the addons server (As Michael Coates properly notes, no password hashes were disclosed due to this vulnerability):

https://addons.mozilla.org/en-US/firefox/pages/..%252f..%252f..%252f..%252f..%252f..%252f..%252fetc/passwd%2500

The vulnerability was resolved by using CakePHP’s own built-in parameter handling, which precludes an attacker from including slashes in a parameter passed via the query string. The relevant code from the latest revision looks something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php
function display($page) {
    if (empty($page) || $page == 'home') {
        $this->redirect('/');
        exit;
    }

    // ...snip...

    $this->render("/$page");
}

More Information

The vulnerability mentioned here has been confirmed fixed by Mozilla.

I’d like to thank Wil Clouser and Michael Coates for handling this issue. I’d especially like to applaud them for the speed with which they handled this report: the vulnerability was patched and the fix was deployed in production about an hour and a half after my initial report.

Interested readers are encouraged to take a look at other vulnerabilities I’ve reported as a part of Mozilla’s Web Security Bug Bounty.

2011
02.03

Summary

Aardvark contained several reflected, DOM based XSS vulnerabilities. Due to CSRF protections, exploiting these vulnerabilities remotely was non-trivial.

How did it work?

1. “Topics” profile page

When adding a new topic to your profie via the Topics page, the text of the new topic was parsed as HTML, which caused any JavaScript contained in the text to be executed.

I tracked down the relevant function (add_interest_to_interface) in the JavaScript and ran it through a pretty-printer. Here’s what it looked like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var add_interest_to_interface = function (user_term) {
    user_term = $.trim(user_term);
    if (user_term.match(/any (.*) question/)) {
        user_term = user_term.replace(/any (.*) question/, "$1");
    }
    if (interest_is_active(user_term)) {
        return false;
    };
    user_interests[user_interests.length] = user_term.toLowerCase();
    tmpl = '<li class="interest" style="display:none">' +
    '<form class="delete_interest" title="Remove this topic" method="post" action="/interests/destroy_by_user_term">' +
    '<input type="image" src="/images/blank.png"/>' +
    '<input type="hidden" value="#{escaped_user_term}" name="user_term"/>' +
    '</form>' +
    '<span class="user_term">' +
    '#{user_term}' +
    '</span>' +
    '</li>'
    $('#user_interests').append($.tmpl(tmpl, {
        'user_term': user_term,
        'escaped_user_term': $.escapeHTML(user_term)
    }, {
        escape: false
    }));
    $('#user_interests li:last').fadeIn();
    $('.topic-menu').each(function () {
        add_styles($(this));
    });
    return true;
};

The code was generating a fragment of HTML to be used as a template (look at the variable tmpl). The vulnerability was caused by the use of a non-escaped version of the user’s input within the template (#{user_term} versus #{escaped_user_term}). The fix was simple: always use the escaped version.

When the page was refreshed, the new topic was loaded from the database, causing it to be sanitized properly.

The first XSS vulnerability, on the topics page

The first XSS vulnerability, on the topics page

2. “Questions you’ve asked” page

This vulnerability functioned in almost exactly the same way as the one above. It occurred on the Questions you’ve asked page when updating the topic for an existing question (“Question about…”). There were two separate locations on the page where the un-sanitized user input was used.

The second XSS vulnerability, when changing the topic of a question

The second XSS vulnerability, when changing the topic of a question

More Information

The vulnerability mentioned here has been confirmed patched by the Google Security Team. I owe them a ton of thanks for organizing this program and giving me a chance to improve my skills.

Interested readers are encouraged to take a look at other vulnerabilities I’ve reported under Google’s Vulnerability Reward Program.

2011
02.03

Summary

Google Baraza (www.google.com/baraza/) and Google Ejabat (ejabat.google.com) were vulnerable to a persistent XSS attack. A malicious user could create a post that would trigger JavaScript when an image or link was clicked on.

How did it work?

These Google services allow users to supplement their replies with external links, videos, and other content. When the reply is actually submitted to the server, this extra data is encoded separately from the rest of the message. By manipulating the encoded data in the request to use a javascript URI (ie: javascript:alert(1)) as the link to a video, it was possible to create a post with a link that would execute JavaScript when clicked on.

The XSS vulnerability in Google Baraza. Clicking on the image or the link resulted in JavaScript being executed.

The XSS vulnerability in Google Baraza. Clicking on the image or the link resulted in JavaScript being executed.

The XSS vulnerability on Google Ejabat. Exactly the same attack vector as in Google Baraza.

The XSS vulnerability on Google Ejabat. Exactly the same attack vector as in Google Baraza.

More Information

The vulnerability mentioned here has been confirmed patched by the Google Security Team. I owe them a ton of thanks for organizing this program and giving me a chance to improve my skills.

Interested readers are encouraged to take a look at other vulnerabilities I’ve reported under Google’s Vulnerability Reward Program.

2011
02.03

[Note: According to Google, I was not the first person to report this vulnerability to them. If the original reporter cares to come forward, I’ll be more than happy to cite their work in this writeup :-)]

Summary

Blogger’s Design Preview functionality served up author-generated content in the context of blogger.com, allowing an author to perform an XSS attack against a blog administrator.

How did it work?

Blogger is designed to allow authors to include arbitrary content in their blog posts. As the Google Security Team explains

Blogger users are permitted to place non-Google as well as Google JavaScript in their own blog templates and blog posts; our take on this is that blogs are user-generated content, in the same way that a third party can create their own website on the Internet. Naturally, for your safety, we do employ spam and malware detection technologies — but we believe that the flexibility in managing your own content is essential to the success of our blogging platform.

Note: when evaluating the security of blogspot.com, keep in mind that all the account management functionality – and all the associated HTTP cookies – use separate blogger.com and google.com domains.

So, for an attack on Blogger to be anything more than an annoyance, the attack has to target blogger.com or google.com as opposed to the subdomain where the blog is hosted.

In this case, there was a vulnerability in how user-generated content was presented on Blogger’s backend, which is served from blogger.com. The vulnerability involved Blogger’s Design Preview functionality, which is used by administrators to test layout changes. The preview of the blog’s content was being served from blogger.com, not from the blog’s (sub)domain. As a result, any JavaScript or other malicious content contained on the page (for instance, from a post written by an author) would be run in the context of blogger.com, exposing cookies and other sensitive information.

The JavaScript in the post creates an alert box with the value of document.location. As you can see, the JavaScript was executed on blogger.com

The JavaScript in the post creates an alert box with the value of document.location. As you can see, the JavaScript was executed on blogger.com

Same JavaScript as the example above, but executed using the Preview button on the Edit HTML page of the Design section

Same JavaScript as the example above, but executed using the Preview button on the Edit HTML page of the Design section

This vulnerability required the attacker to have author level (or higher) privileges on the same blog as the target, limiting its effectiveness. However, a malicious author could have used this vulnerability to perform privilege escalation via a CSRF attack, giving themselves administrator level privileges on the blog.

More Information

The vulnerability mentioned here has been confirmed patched by the Google Security Team. I owe them a ton of thanks for organizing this program and giving me a chance to improve my skills.

Interested readers are encouraged to take a look at other vulnerabilities I’ve reported under Google’s Vulnerability Reward Program.

2011
02.01

Summary

Google Code contained a static HTML page that was vulnerable to a reflected, DOM-based XSS vulnerability.

How Did It Work?

The page in question is located at http://code.google.com/testing/iframe_example.html. I originally came across it when I was performing a Google search for an unrelated vulnerability: I’m not sure how I would have found it otherwise. :-P

The page itself is fairly straightforward. It uses JavaScript to parse the query string and extract key/value pairs; it then document.write to add the data provided by the user directly to the page. If you look at the HTML on the page right now, you can see that user input is sanitized using JavaScript’s escape function: in the past, that was not the case. That lack of sanitization is what allowed the vulnerability to occur.

JavaScript could be executed by entering HTML in the url field, for example.

JavaScript could be executed by entering HTML in the url field, for example.

More Information

The vulnerability mentioned here has been confirmed patched by the Google Security Team. I owe them a ton of thanks for organizing this program and giving me a chance to improve my skills (and for putting up with me even when I report vulnerabilities that, unlike this one, affect only IE 6 / 7 :-P )

Interested readers are encouraged to take a look at other vulnerabilities I’ve reported under Google’s Vulnerability Reward Program.