Details
Affected Software:WordPress
Fixed in Version:2.8.1
Issue Type: URL Redirection / CRLF Injection / HTTP Header Injection / XSS
Original Code:Found Here
Description
When appended together the %0d (Carriage Return) and %0a (Line Feed) characters represent a Carriage Return Line Feed (CRLF).
The vulnerable WordPress code snippet actually contained logic to detect carriage returns and line feed characters,attempting to strip CRLF from data being assigned to the $url variable.
The CRLF detection logic used by the vulnerable WordPress version was not very robust. The CRLF detection logic simply checked for the presence of “%0d”and “%0a” in the $url variable and failed to consider UPPERCASE “%0D”or “%0A”. The $url variable is assigned the CRLF tainted string and eventually passed to a HTTP Location header,giving the attacker an opportunity for URL Redirection,CRLF injection,HTTP header injection,and even Cross Site Scripting (XSS).
In addition to adding UPPERCASE variants of “%0D”and “%0A”to the detection logic,a function to recursively detect the presence of CRLF was also added. Before this function was added,it was possible to defeat the detection logic by simply passing a string such as %0%0d%0ad%0%0d%0aa which would have “%0d%0a” character sequences stripped out,resulting in %0d%0a being passed to the $url variable. The WordPress developers addressed this issue by adding a recursive verifier (_deep_request()),whose source is included in the Developers Solution.
Developers Solution
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | function clean_url( $url,$protocols = null,$context = 'display' ) { $original_url = $url; if ('' == $url) return $url; $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i','',$url); - $strip = array('%0d','%0a'); - $url = str_replace($strip,'',$url); + $strip = array('%0d','%0a','%0D','%0A'); + $url = _deep_replace($strip,$url); $url = str_replace(';//','://',$url); if ( strpos($url,':') === false && substr( $url,0,1 ) != '/' &&substr( $url,0,1 ) != '#' &&!preg_match('/^[a-z0-9-]+?\.php/i',$url) ) $url = 'http://' . $url; // Replace ampersands and single quotes only when displaying. if ( 'display' == $context ) { $url = preg_replace('/&([^#])(?![a-z]{2,8};)/','&$1',$url); $url = str_replace( "'",''',$url ); } if ( !is_array($protocols) ) $protocols = array('http','https','ftp','ftps','mailto','news','irc','gopher','nntp','feed','telnet'); if ( wp_kses_bad_protocol( $url,$protocols ) != $url ) return ''; return apply_filters('clean_url',$url,$original_url,$context); } With _DEEP_REPLACE for recursive checks! + +function _deep_replace($search,$subject){ + $found = true; + while($found) { + $found = false; + foreach( (array) $search as $val ) { + while(strpos($subject,$val) !== false) { + $found = true; + $subject = str_replace($val,'',$subject); + } + } + } + + return $subject; +} |


