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
* 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(0) </script>).
When the code to be sanitised is part of an attribute of an HTML tag (e.g. <;img onmouseover="alert(111)">), 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(2)">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('do'+'cument'+'.'+'coo'+'kie');alert(a)"
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.