Difference between revisions of "Unsafe string replacement"
(→Overview) |
(→Example) |
||
Line 5: | Line 5: | ||
Unsafe use of string replacement functions to sanitize user input is extremely common. Because string replacement ([http://php.net/manual/en/function.str-replace.php str_replace] in PHP) 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 use of string replacement functions to sanitize user input is extremely common. Because string replacement ([http://php.net/manual/en/function.str-replace.php str_replace] in PHP) 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: | A trivial example: | ||
Line 61: | Line 63: | ||
}} | }} | ||
Bypassed by 'file:/file:///' | 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: | ||
+ | {{code|text=<source lang="perl"> | ||
+ | #!/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); | ||
+ | } | ||
+ | </source>}} | ||
+ | |||
+ | * 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= | =Defense= |
Revision as of 14:25, 6 May 2012
This is a programming language agnostic vulnerability, effecting any application which incorrectly uses string replacement to sanitize data. |
Contents
Overview
Unsafe use of string replacement functions to sanitize user input is extremely common. Because string replacement (str_replace in PHP) 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 for sensitization, it must be done using a loop or called recursively:
- 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; } |
However, the use of whitelists with 'positive' regex matching (i.e. does the input match /[a-z]+/) is more effective.
<center>