Sometimes a security function just does not work like intended by its designers or developers. Suppose you run a website that offers a file download mechanism implemented in PHP. Nowadays, many programmers are aware of the security problems that might arise from flawed implementations in that area. They might easily lead to the disclosure of arbitrary files of the web server.
Always keeping the user experience in mind, people often tend to sanitize user input (e.g. removing unwanted content and then continue) rather than failing gracefully and confront the user with an error message. Just as in the following code:
$dlfile = $_GET["file"];
...
// Prevent directory traversal attack
$dlfile = preg_replace("/\.\.\//", "", $dlfile);
...
What the developer tries to do is removing "../" sequences in order to prevent attackers from reading other files than those available in a specific directory. In the following example, the first request should lead to a normal file download, while the second one should be prevented:
/download.php?file=funnycat.jpg
/download.php?file=../../../../etc/passwd
What has not been kept in mind is that the parameter can be chosen in order that it uses the regular expression to construct the desired outcome. Just consider the following input (without the parenthesis):
/download.php?file=.(../)./.(../)./.(../)./.(../)./etc/passwd
Those parts highlighted with parenthesis are removed by the "security function". The rest stays. This leads to:
../../../../etc/passwd
In order to prevent that pitfall there are many possible solutions. The easiest would be to simply abort processing when an error is encountered, instead of applying sanitization. Others include performing the sanitization repeatedly, whitelist allowed file names or character ranges, or (in this case) link to the files directly instead of using a PHP indirection. Of course, contents could also be stored in a database instead, but SQL injections are a different topic ;-)
No comments:
Post a Comment