今天一位同事向我打赌,他知道一种方法可以提供一个特殊格式的字符串,该字符串可以通过以下正则表达式检查并仍然提供扩展名为.php
或.jsp
的文件名或.asp
:
if (preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i', $var) && preg_match('/\.(asp|jsp|php)$/i', $var) == false)
{
echo "No way you have extension .php or .jsp or .asp after this check.";
}
尽管我努力尝试并在网上搜索,但我无法找到能够使这种事情成为可能的缺陷。我能忽略一些东西吗?鉴于处理了“空字节”漏洞,这里还有什么问题?
注意:我绝不暗示此代码是检查文件扩展名的完全证明方法,preg_match()
函数中可能存在缺陷或文件内容可能格式不同,我只是用正则表达式语法本身来提问。
编辑 - 实际代码:
if (isset($_FILES["image"]) && $_FILES["image"]["name"] && preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i', $_FILES["image"]["name"]) && preg_match('/\.(asp|jsp|php)$/i', $_FILES["image"]["name"]) == false) {
$time = time();
$imgname = $time . "_" . $_FILES["image"]["name"];
$dest = "../uploads/images/";
if (file_exists($dest) == false) {
mkdir($dest);
}
copy($_FILES['image']['tmp_name'], $dest . $imgname);
}else{
echo "Invalid image file";
}
PHP版本:5.3.29
编辑:结语
原来,'漏洞'只出现在Windows上。然而,它确实完成了我的同事告诉我它将通过正则表达式检查并保存带有可执行扩展名的文件。以下WampServer 2.2
使用PHP 5.3.13
测试了以下内容:
将以下字符串传递给test.php:.jpg
上面的正则表达式检查(注意所需扩展名末尾的“:”冒号)将验证它,函数copy()
似乎省略冒号后的所有内容符号包括符号本身。
同样,这仅适用于Windows。在linux上,文件将使用与传递给函数相同的名称编写。
答案 0 :(得分:10)
没有一个步骤或完全直接的方式来利用您的代码,但这里有一些想法。
你在这个例子中将它传递给copy()
但是你已经提到过你现在一直在使用这种方法来验证文件ext,所以我假设你有其他的情况可能已经将这个过程与其他函数一起使用了在不同的PHP版本上。
将此视为测试程序(Exploiting include,require):
$name = "test.php#.txt";
if (preg_match('/\.(xml|csv|txt)$/i', $name) && preg_match('/\.(asp|jsp|php)$/i', $name) == false) {
echo "in!!!!";
include $name;
} else {
echo "Invalid data file";
}
这将最终打印“在!!!!”并执行'test.php'即使它被上传它将包含它来自tmp文件夹 - 当然,在这种情况下,你已经被攻击者拥有,但我们也考虑这个选项。 这不是上传过程的常见场景,但它是一个可以通过组合多种方法来利用的概念:
让我们继续前进 - 如果你执行:
//$_FILES['image']['name'] === "test.php#.jpg";
$name = $_FILES['image']['name'];
if (preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i', $name) && preg_match('/\.(asp|jsp|php)$/i', $name) == false) {
echo "in!!!!";
copy($_FILES['image']['tmp_name'], "../uploads/".$name);
} else {
echo "Invalid image file";
}
再次完美无缺。该文件被复制到“上传”文件夹中 - 您无法直接访问它(因为Web服务器将删除#的右侧)但是您注入了该文件而攻击者可能会找到方式或其他弱点稍后再称之为。
此类执行方案的一个示例在共享和托管站点中很常见,其中文件由PHP脚本提供(在某些不安全的情况下)可能通过将文件包含在错误类型的函数(如{{1 }},require
,include
都是易受攻击的,可以执行该文件。
空字节 空字节攻击是php中的一大弱点< 5.3但是在一些函数中由版本5.4+中的回归重新引入,包括所有与文件相关的函数以及更多的扩展。它被修补了几次,但它仍然存在,很多旧版本仍在使用中。如果你正在处理一个较旧的PHP版本,你肯定是暴露:
file_get_contents
将打印“在!!!!”并复制名为“test.php”的文件。
php修复的方式是通过检查字符串长度,然后将其传递给更深层的C程序,该程序创建实际的char数组,如果字符串被空字节截断(表示字符串在C中结束) )长度不匹配。 read more
奇怪的是,即使在补丁和现代PHP版本中,它仍然存在:
//$_FILES['image']['name'] === "test.php\0.jpg";
$name = $_FILES['image']['name'];
if (preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i', $name) && preg_match('/\.(asp|jsp|php)$/i', $name) == false) {
echo "in!!!!";
copy($_FILES['image']['tmp_name'], "../uploads/".$name);
} else {
echo "Invalid image file";
}
我的结论:
您可以显着改进验证文件扩展名的方法 - 您的代码允许名为$input = "foo.php\0.gif";
include ($input); // Will load foo.php :)
的PHP文件通过,而不应该通过。成功的攻击通常是通过组合几个漏洞甚至是次要漏洞来执行的 - 您应该将任何意外的结果和行为视为一个。
注意:关于文件名和图片的问题还有很多,因为它们会在以后的页面中包含很多时间,如果它们没有被正确过滤并安全地包含在内,那么就会让自己暴露在更多的XSS内容中但这不是主题。
答案 1 :(得分:2)
试试这段代码。
$allowedExtension = array('jpeg','png','bmp'); // make list of all allowed extension
if(isset($_FILES["image"]["name"])){
$filenameArray = explode('.',$_FILES["image"]["name"]);
$extension = end($filenameArray);
if(in_array($extension,$allowedExtension)){
echo "allowed extension";
}else{
echo "not allowed extension";
}
}
答案 2 :(得分:0)
preg_match()返回1,否则返回0;如果发生错误,则返回FALSE。
$var = "test.php";
if (preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i', $var) === 1
&& preg_match('/\.(asp|jsp|php)$/i', $var) !== 1)
{
echo "No way you have extension .php or .jsp or .asp after this check.";
} else{
echo "Invalid file";
}
因此,当您要检查代码时,请使用=== 1
。
function isImageFile($file) {
$info = pathinfo($file);
return in_array(strtolower($info['extension']),
array("jpg", "jpeg", "gif", "png", "bmp"));
}
答案 3 :(得分:0)
我记得在 PHP<中的某些版本中5.3.X ,PHP允许字符串包含0x00
,此字符串被视为字符串
的结尾
因此,例如,如果您的字符串包含: myfile.exe \ 0.jpg ,那么preg_match()
将匹配 jpg ,但其他 PHP 函数将停在 myfile.exe 中,例如include()
或copy()
函数