2011
08.18

Summary

A PHP application that displays error reporting notices and contains specific code patterns may be vulnerable to a cross-site scripting attack. I’ve confirmed this issue on PHP 5.2.17 and a snapshot of PHP 5.4 (I assume it affects other versions of PHP as well). This issue was filed as Sec Bug #55139 back in July, but it was recently closed as “bogus” by a member of the PHP team, making the report public.

How does it work?

When display_errors is enabled and a PHP notice is generated, none of the text of the notice is HTML-encoded. That means if an attacker can control part of the notice text, they can inject arbitrary HTML and JavaScript into the page. Certain specific coding patterns make such an attack possible.

For example, the “Undefined index” notice does not properly sanitize the name of the index. That means an application containing code similar to the following would be vulnerable to cross-site scripting:

1
2
3
4
<?php  
error_reporting(E_ALL);
$array = array();
echo $array[$_GET['index']];

You can test this yourself: upload that script and try browsing to file.php?index=<s>test</s>. You’ll see strikethrough text instead of the literal string <s>test</s>.

This also holds true for less common coding patterns and notices. For example:

1
2
3
4
5
<?php
// Undefined function. this is included for completeness:
// if you have code that does this, you have much larger problems
error_reporting(E_ALL);
echo $_GET['funct']();

1
2
3
4
<?php
// undefined variable
error_reporting(E_ALL);
echo $$_GET['var'];

1
2
3
4
5
<?php
// defining a constant twice
error_reporting(E_ALL);
define($_GET['cons'], 'a');
define($_GET['cons'], 'a');

But display_errors needs to be enabled!

Yes, display_errors needs to be enabled. Yes, that means your server is potentially leaking sensitive information (including absolute paths). Yes, that’s not something you should be doing on a production server. But under normal circumstances, that’s the extent of any security issues. A sysadmin or a developer who makes the decision to enable display_errors has no expectation that doing so also (potentially) opens up their site to cross-site scripting.

Lets also not forget that display_errors may be enabled in development environments. If an attacker can identify a vulnerable page and can figure out the URL for a working development environment, the attacker can send that URL, modified to exploit the cross-site scripting vulnerability, to a developer. If it gets clicked on, the development environment can become compromised.

Does code like that exist in the real world?

Yes! ;-)

As a proof of concept, I identified a location in Wordpress (/wp-admin/upload.php) containing a vulnerable code pattern. It looked like:

1
2
3
4
5
<?php
if ( isset($_GET['message']) && (int) $_GET['message'] ) {
   $message = $messages[$_GET['message']];
   $_SERVER['REQUEST_URI'] = remove_query_arg(array('message'), $_SERVER['REQUEST_URI']);
}

In that case, an attacker could supply $_GET['message'] as 1<script>alert(1)</script>, triggering an alert box.

This issue would not be feasible to exploit on most installations: Wordpress explicitly excludes E_NOTICE from error_reporting unless a special debug mode is enabled. However, the code has been updated in SVN and is no longer vulnerable. I want to extend my thanks to the Wordpress developers for addressing this issue. :-)

Does it only affect PHP notices?

Under certain situations, no!

Greg asked in the comments what the impact of the html_errors INI option is. As it turns out, when that option is disabled, examples that I had previously tested become vulnerable. Previously, only notices appeared unsanitized. When that option is disabled, every message (warnings, fatal errors, etc) appears to end up unsanitized. Very good finding. :)

Conclusion

Don’t enable display_errors in production: it can now cause cross-site scripting as well as information disclosure.

2011
08.15

Summary

Safari for Windows employed unsafe content-sniffing behavior on pages that were served up as text/plain. As a result, an attacker could cause cross-site scripting to occur in locations that would not normally be vulnerable. This issue was fixed in Safari 5.1. It has been assigned the identifier CVE-2010-1420.

How Did It Work?

When web pages were served up with a text/plain content type, Safari for Windows would determine the correct content handler by looking at their file extension. For instance, a text/plain document located at http://example.com/file.html would be parsed as HTML rather than rendered as text. In most other browsers, a text/plain content type precludes the content from ever being handled as HTML (IE being the exception).

Of course, that behavior is not very interesting by itself. Most URIs that serve up content as text/plain don’t also have HTML file extensions. However, in certain cases it’s possible to append data to the path portion of the URI without changing how the request is routed. In those cases it was possible to exploit this content-sniffing behavior by appending a filename with an HTML extension. It was also possible to cause cross-site scripting when user-controlled content was served up in this way.

The simplest example is PHP’s support for PATH_INFO. By default, most PHP installations allow arbitrary data to be appended to the path portion of the URI; the portion of the path after the file itself is stored in $_SERVER['PATH_INFO']. So, just about any PHP script that serves up content as text/plain could also be used to exploit this vulnerability.

I set up a script, located at http://sandboxing.me/poc/b82553b2869b7fa80766ec55073e998a.php, as a demonstration. The code is as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php header('Content-type: text/plain'); ?>
<?php header('X-Content-Type-Options: nosniff'); ?>
<html>
    <head>
            <title>Test</title>
    </head>
    <body>
            <script>alert(1);</script>
    </body>
</html>

I included the X-Content-Type-Options header for completeness, since it’s used by IE to override similar insecure content-sniffing behavior. As expected, Safari for Windows ignored the header.

To use the script for testing, I simply appended a forward slash to the URL, followed by a filename. Safari used the newly provided extension to determine how the file contents were parsed. Some examples:

HTML:
http://sandboxing.me/poc/b82553b2869b7fa80766ec55073e998a.php/test.htm
http://sandboxing.me/poc/b82553b2869b7fa80766ec55073e998a.php/test.html
http://sandboxing.me/poc/b82553b2869b7fa80766ec55073e998a.php/test.shtml
SWF:
http://sandboxing.me/poc/b82553b2869b7fa80766ec55073e998a.php/test.swf
PDF:
http://sandboxing.me/poc/b82553b2869b7fa80766ec55073e998a.php/test.pdf
EXE:
http://sandboxing.me/poc/b82553b2869b7fa80766ec55073e998a.php/test.exe
XML:
http://sandboxing.me/poc/b82553b2869b7fa80766ec55073e998a.php/test.xml
http://sandboxing.me/poc/b82553b2869b7fa80766ec55073e998a.php/test.xhtml

What about OS X?

Safari for OS X did not exhibit the same behavior: in fact, it appears that similar behavior was patched in 10.4.4 (see http://www.mnot.net/blog/2006/01/11/safari_content_sniffing).

Example Vulnerability

Bug 637981, which was recently patched in Bugzilla, relies upon this content sniffing behavior. Raw user-supplied content (in the form of an attached patch file) was being served as text/plain from the main Bugzilla domain. By uploading a malicious patch file, it was possible to cause Safari (and IE) to execute arbitrary Javascript.

Conclusion

I would like to thank Apple Product Security for handling my report and keeping me in the loop as the issue was patched. I also want to acknowledge Hidetake Jo, who discovered this vulnerability independently.

2011
05.26

Summary

Back in March, I determined that the Textpattern blogging software contained a number of very serious security vulnerabilities, including a remote code execution vulnerability that affected every single version of the software ever released (since September 2004). In response to my report, the Textpattern developers released a new version of the software, 4.4.0, which contained fixes for almost all of the vulnerabilities. One outstanding vulnerability has been patched in SVN and should be a part of the next release.

What were the vulnerabilities?

I reported a total of 12 issues to the Textpattern developers:

1. Lack of CSRF Protection

Textpattern does not contain any mechanism for protecting against cross-site request forgery (CSRF) attacks. This vulnerability allows for everything from privilege elevation (via a CSRF attack on the “Add User” page) to arbitrary code execution (via use of the <txp:php> tag, a Textpattern template tag that allows for code execution).

This issue was not addressed in version 4.4.0, but the developers have subsequently added support for CSRF tokens in SVN (see r3528-r3557).

2. Arbitrary code execution for unauthenticated users via <txp:php> and form previews

textpattern/index.php contained the following code, which was meant to provide a preview of an updated Textpattern form, near the very top of the file:

1
2
3
4
5
6
<?php
if (isset($_POST['form_preview'])) {
    include txpath.'/publish.php';
    textpattern();
    exit;
}

That code was executed prior to authentication checks being run. In addition, forms are allowed (by default) to use a special template tag, <txp:php>, which executes PHP code. As a result, an unauthenticated attacker could execute arbitrary code on a server running Textpattern by submitting a modified POST request containing <txp:php> tags.

Although support for <txp:php> tags can be disabled on a per-site basis, other vulnerabilities (#3 / #4 on this list) allowed that protection to be bypassed.

This vulnerability was patched for version 4.4.0. In researching this vulnerability, I discovered that it had existed since the first version of Textpattern was released.

3. Textpattern tag system allows for code execution

Due to a design flaw in Textpattern’s tag system, it was possible to build a tag that would execute arbitrary PHP without using <txp:php>. The bad code was in the processTags function in textpattern/publish.php:

1
2
3
4
5
<?php
if (function_exists($tag))
{
    $out = $tag(splat($atts), $thing);
}

In other words, a tag like <txp:some_function a="b" c="d">some text</txp:some_function> would try to execute the PHP function some_function, passing in the attributes ($atts) as a PHP array and the inner content as a string. Textpattern template tags were just regular PHP functions that accepted at most two parameters.

But guess what? Other functions also follow this pattern: array_filter, array_walk, usort, uksort, and uasort all accept an array and a callback function as their parameters.

So, placing the following tag on a page would result in shell commands being run and the results being displayed in the response:

1
<txp:array_filter 0="whoami" 1="ls -al">passthru</txp:array_filter>

This tag would create a page, test.php, that calls eval on user input, allowing for arbitrary code execution (eval can’t be called directly since it’s a language construct, not a function).

1
<txp:array_filter 0='echo "<?php eval($_GET1); ?>" > test.php'>passthru</txp:array>

A partial fix for this vulnerability was released as part of version 4.4.0. The code now uses get_defined_functions to avoid calling the internal functions mentioned above. However, there is still no whitelist of valid functions for the Textpattern parser to call: all user-defined PHP functions are considered fair game. If the codebase contained a function that looked like the evil function defined below, for instance, sites would again be vulnerable.

1
2
3
4
5
<?php
function evil($array, $callback)
{
    return array_filter($array, $callback);
}

This vulnerability, just like #2, has existed since Textpattern was first released.

4. Attacker can bypass <txp:php> settings

Remember how I just said that any user-defined PHP function could be called as a tag, provided it accepts the right number of parameters (<= 2)? Well, a function inside of Textpattern made it possible to bypass configuration settings and reliably execute code via the <txp:php> tag.

The function was named fileDownloadFormatTime and it looked like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php
function fileDownloadFormatTime($params)
{
    global $prefs;

    extract($params);

    if (!empty($ftime))
    {
        return !empty($format) ?
            safe_strftime($format, $ftime) : safe_strftime($prefs['archive_dateformat'], $ftime);
    }
    return '';
}

What’s so special about this function?

  1. It calls extract on $params, which is provided by the user as input. extract, by default, will overwrite (clobber) the values of variables in the current scope. Inside a function, that’s not a huge deal: however, $prefs is marked as a global variable here, which means it can be overwritten and the new value will be persisted.
  2. $prefs['allow_page_php_scripting'] and $prefs['allow_article_php_scripting'] are supposed to be boolean values that indicate whether or not <txp:php> tags are allowed in a particular context.

So, by calling fileDownloadFormatTime with an attribute named prefs, it was possible to overwrite the $prefs variable. If the variable was overwritten with the correct value (eg: 1), $prefs['allow_page_php_scripting'] and $prefs['allow_article_php_scripting'] would be enabled, allowing PHP execution in subsequent tags. An example is below:

1
2
<txp:fileDownloadFormatTime prefs="1"></txp:fileDownloadFormatTime>
<txp:php>phpinfo();</txp:php>

This issue was fixed for version 4.4.0 over the course of several different SVN revisions.

  1. r3506 changed the call to extract so it could not be used to overwrite $prefs
  2. r3495 and r3496 added calls in strategic locations to explicitly verify that $prefs is an array, as it should be.

5. File Uploader allows for arbitrary file uploads

The file uploader, which is accessible to all authenticated users except for freelancers, does not perform filtering on the extensions of uploaded files. By default, the files are publicly accessible in the files/ directory of the Textpattern install. That means a malicious user can, depending on the extension of the file they upload:

  1. Execute arbitrary PHP
  2. Execute arbitrary Javascript by uploading an HTML file
  3. Perform CSRF / XSS by uploading an SWF file

Unfortunately, this vulnerability was not fully addressed in the 4.4.0 release. r3484 added a .htaccess file to the files/ directory that would have blocked direct access. However, after a small outcry in the Textpattern forums, r3501, r3502, and r3503 renamed the .htaccess file to .htaccess-dist and added a note to the README file suggesting that people should use it. That means installs are vulnerable to this kind of attack by default. The only mitigating factor is that file uploads are restricted to non-freelance level users (they are still CSRF-able though!).

6. Image Uploader allows for SWF uploads

The image uploader, which is accessible to all authenticated users except for freelancers, does perform filtering on the extensions of uploaded files. However, it allows SWF files, which can be used to perform CSRF and XSS attacks. This issue is otherwise identical to #5.

7. Persistent XSS via Articles

HTML in articles is not subject to any sort of filters. As a result, it’s possible for a freelancer to add a malicious Javascript payload to his/her article: when another user looks at the article preview, that Javascript will be executed. Due to the lack of CSRF protection, an attacker could trick an authenticated user into creating such an article.

This issue has not been addressed in 4.4.0. The development team is currently investigating possible HTML filtering libraries (ie: htmLawed).

8. Reflected XSS in textpattern/include/txp_page.php

The following fragment of code was found in textpattern/include/txp_page.php. $name was derived from user input and was not properly sanitized.

1
2
3
<?php
$buttons = '<div
    class="edit-title">'.gTxt('you_are_editing_page').sp.strong($name).'</div>';

This vulnerability was resolved for version 4.4.0.

9. Directory Traversal via Uploaded Files

When editing uploaded files (possible for everyone except freelancers), it was possible to manually edit the ‘filename’ parameter. This parameter was vulnerable to a directory traversal attack: by using ../ to change the relative path, it was possible to access any file on the filesystem which the webserver could read. The file_download URL handler would then serve up that file without question, even to unauthenticated users. This attack made it possible to grab the contents of the Textpattern config file, /etc/passwd, etc. Due to the lack of CSRF protection, an attacker could trick an authenticated user into creating such a malicious download.

This vulnerability was resolved for version 4.4.0.

10. Passwords stored insecurely

Account passwords were stored in lower-case and hashed using the PASSWORD function for MySQL. Passwords should always be case sensitive and the MySQL PASSWORD function should not be used for a web application (see http://dev.mysql.com/doc/refman/5.1/en/encryption-functions.html#function_password).

This vulnerability was resolved for version 4.4.0 through the use of phpass.

11. $txp_user not consistently escaped in SQL

Textpattern does not make use of stored procedures and prepared statements due to the age of its codebase; instead, it uses string concatenation combined with manual escaping. There were several places in the code where $txp_user, the username of the currently logged in user, was not properly escaped.

This vulnerability was resolved for version 4.4.0.

12. GET requests modify application state

There were several locations in the code where actions were taken in the application based on GET requests (ie: banning/unbanning users, updating plugin code, etc). Ideally, GET requests should be idempotent to prevent unintended submissions that alter the application. This issue is closely tied to #1, since the lack of cross-site request forgery makes all requests, including GETs, impossible to validate.

Conclusion

Anyone who is running a Textpattern installation should immediately upgrade to 4.4.0 if they haven’t already. They should also be aware of the vulnerabilities that still exist in that version (cross-site request forgery, file uploads, persistent XSS from article previews).

I would like to thank the Textpattern development team, especially Robert Wetzlmayr, for their responses to my report.

2011
04.26

[Note: The fix for this security vulnerability is omitted from the release history for BuddyPress v1.2.8]

[Note: This writeup is related to another vulnerability in Wordpress that I’ve written about.]

Summary

BuddyPress 1.2.7 and older contains a broken check for validating avatar uploads. As a result, file extension restrictions are not properly enforced (the only restrictions in effect are Wordpress’s default restrictions, which can be fairly broad). Since registered user (Subscriber level and above) can upload avatars to BuddyPress, they are also able to exploit the file upload XSS vulnerability in Wordpress that I recently wrote about.

How Did It Work?

Internally, BuddyPress’s avatar upload feature uses built-in Wordpress functionality for handling uploads. Those functions are built to allow a variety of file extensions without much validation, as I pointed out in my other report. BuddyPress has written its own set of checks; when a file is uploaded, BuddyPress attempts to verify that the file extension is valid for an image (jpeg/jpg/gif/png) and that the file’s claimed MIME-type is valid for an image.

Unfortunately, older versions of BuddyPress contain a mistake in the logic of the test. As a result, they allow uploads where only one of the two checks passes. Accordingly, a malicious attacker can send a request to a vulnerable server, uploading a file with an extension that is acceptable to Wordpress (eg: .html) and a MIME type that is acceptable to BuddyPress (image/gif). The upload will be accepted and stored on the server, where it can be used to cause a persistent XSS attack.

For the curious, this is the patch that corrects the logic of BuddyPress’s avatar validation.

So, What’s The Fix?

If you can’t upgrade to the newest version of BuddyPress, at least update your code using the aforementioned patch. For other mitigation tips, check out the Wordpress vulnerability report.

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.30

About a month or so ago, my Dad’s HP Officejet L7580 stopped working: some paper had gotten jammed in it. Although I cleaned it out thoroughly, the printer still refused to acknowledge that the jam was cleared. After spending several days trying to clear the phantom jam, we ended up buying a new printer (the L7580 having long since been out of warranty).

Then today, I decided to take another crack at fixing the printer. After a lot of false starts and dead ends from Google, I managed to stumble upon a very helpful post on fixya.com:

Here’s another from the Hp Forum, but I myself have tried once, but if it’s actual paper jam, this wont help. If in case there’s mechanical issue, this will definitely help you..
Tools: (2) Medium sized paper clips
Preferable Tools: (1) Pair of pliers with rubber grips
(2) Edged tool such as wall paper knife, metal pick or toothpick to clean out gear teeth.

This fix applies to paper jams the device believes are in the rear duplexer and often occurs after a real jam that has shredded the paper inside the mechanism.
Despite the fact you’ve carefully cleaned out all the visible paper from inside with the cover open, paper can jam the gears used to drive the rear rollers and even the tiniest amount of shred prevents operation resulting in the impossible Paper Jam error.
HP Tech Support online and on the phone don’t troubleshoot it past “clear the paper from the device than try again please” b.s.
After removing the duplexer, grip any of the four rubber rollers and attempt to rotate in either direction, if they don’t move freely with little effort, then continue with these instructions.
 Begin:

  1. Take the first paper clip and contact the lower two brass rectangles, this fools the device into believing the duplexer is installed, keep the contacts bridged (don’t remove your hand or the paper clip until Step 4.) There’s no electrocution risk.
  2. Press the “OK” button on the control panel to pretend the paper jam is cleared.
  3. Carefully observe the pair of white gears on the left, if they seem to clutch, stall, skip or freeze then you have paper shards which are causing the jam.
  4. Using a firmer grip or pliers, grip the furthest left rubber roller and rotate in either direction , the gears should rotate with your efforts and look for shards of paper in the gear teeth.
  5. Use the paper clip, toothpick or any appropriate edged tool to clear the shards.

Bridge the lower contacts again and observe if the gears rotate freely, if so then you’ve cleared the insidious jam should be able to print until the next error from this piece of chit printer.

I decided to test it out, since it was the first new suggestion I’d come across. After bridging the connectors, I saw that the gears were indeed jammed. I got a screwdriver and applied pressure: the gears moved and I saw little scraps of paper! Lo and behold, after I got all of the scraps of paper out, the printer was completely back to normal. :-)

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.