Difference between revisions of "Unsafe string replacement"
Rochell4259 (Talk | contribs) |
Rochell4259 (Talk | contribs) |
||
Line 2: | Line 2: | ||
__FORCETOC__ | __FORCETOC__ | ||
− | <font size="-2">Special thanks to [[User:hatter| | + | <font size="-2">Special thanks to [[User:hatter|hatter]] for his contributions to this article.</font> |
=Overview= | =Overview= | ||
Unsafe string replacement occurs when a replacement call is used to remove a series of text longer than one character from a string, invoked only once, to sanitize it. Because string replacement ([http://php.net/manual/en/function.str-replace.php str_replace] in [[PHP]], =~ s/// in [[Perl]], etc) functions only do a single replacement, it is necessary to loop over them until all unsafe characters or strings are removed if you are replacing more than a single character. This also applies to replacements powered by regular expressions. | Unsafe string replacement occurs when a replacement call is used to remove a series of text longer than one character from a string, invoked only once, to sanitize it. Because string replacement ([http://php.net/manual/en/function.str-replace.php str_replace] in [[PHP]], =~ s/// in [[Perl]], etc) functions only do a single replacement, it is necessary to loop over them until all unsafe characters or strings are removed if you are replacing more than a single character. This also applies to replacements powered by regular expressions. |
Revision as of 03:29, 20 September 2012
Unsafe string replacement requires a basic understanding of programming |
Special thanks to hatter for his contributions to this article.
Contents
Overview
Unsafe string replacement occurs when a replacement call is used to remove a series of text longer than one character from a string, invoked only once, to sanitize it. Because string replacement (str_replace in PHP, =~ s/// in Perl, etc) functions only do a single replacement, it is necessary to loop over them until all unsafe characters or strings are removed if you are replacing more than a single character. This also applies to replacements powered by regular expressions.
Examples
PHP
A trivial example:
<?php $filepath = $_GET['file']; $safe_filepath = str_replace('../', '', $filepath); echo("Safe filepath is '" . $safe_filepath . "'<br />"); include($safe_filepath); ?> |
First an attacker may try a simple directory transversal attack, using '../' to escape. The result:
Safe filepath is 'etc/passwd'
No dice, the dangerous string ('../') is dutifully sanitized by str_replace. But, our attacker isn't going to give up yet, now armed with the knowledge that '../' is being filtered out, he may try:
test.php?file=....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd
The result:
Safe filepath is '../../../../../../../../../../../etc/passwd' [contents of /etc/passwd]
Even if '../' is replaced twice, it can be easily bypassed by using ......///. No matter how many times the replacement is made, the attacker simply needs to nest another layer.
Other examples of unsafe uses of string replacement include:
str_replace('<?', '', $source); |
Bypassed by '<<??'
str_replace(array('<script', '<img'), '', $source); |
Bypassed by '<<imgscript>'
str_replace('file://', '', $source); |
Bypassed by 'file:/file:///'
PCRE
To test if perl compatible regular expressions are vulnerable to the attack, a small example perl script, rexpose.pl, is attached:
#!/usr/bin/perl use strict; use Getopt::Std; my %opts = (); getopts('s:p:',\%opts); my $string = $opts{s} if ($opts{s}) or usage(); my $pattern; $pattern = $opts{p} if ($opts{p}); $pattern = '[.]{2}\/' unless (defined $pattern); $string =~ s/$pattern//g; print "$string\n"; sub usage { print "rexpose.pl -s [string] [-p pattern]\n"; print "Regex exposure will display string replacements using s/\$pattern//g. Pattern defaults to '[.]{2}\\/' to match '../'.\n"; exit(0); } |
- First, a check is performed to make sure that the string is being stripped at all to being with:
[user@host ~]$ perl rexpose.pl -s ../../../../../../path/to/file path/to/file
- To identify if the replacement is vulnerable, a check using ....// in stead of ../ is performed:
[user@host ~]$ perl rexpose.pl -s ....//....//....//....//....//....//path/to/file ../../../../../../path/to/file
Defense
If one must use str_replace or PCRE for removal of a string (this does not apply to single bytes), it must be done using a loop or called recursively:
PHP
- Loop
function safe_str_replace($search, $replace, $subject) { while(strstr($subject, $search) !== FALSE) { $subject = str_replace($search, $replace, $subject); } return $subject; } |
- Recursion
function safe_str_replace($search, $replace, $subject) { if (strstr($subject, $search) !== FALSE) { return safe_str_replace($search,$replace,str_replace($search, $replace, $subject)); } return $subject; } |
PCRE
- When patching rexpose.pl, line 12 contains the following line:
$string =~ s/$pattern//g; |
- This can be fixed with a golfed until loop:
$string =~ s/$pattern//g until ($string !~ $pattern); |
- This patch was tested:
[user@host ~]$ perl rexpose.pl -s ....//....//....//....//....//....//path/to/file path/to/file
Whitelisting using PCRE
It is always best to use whitelisting to sanitize input. This can be done by using regular expressions to test to be sure that the input contains valid data. For example, if the developer only wants to refer to a file in the same directory:
my $string = <>; print $string if ($string ~ /^[\w\d\_\-]+[.][\w\d]{2,4}$/g); |
<center>