今天一位同事跟我打赌他知道一种提供特殊格式字符串的方法,该字符串可以通过以下正则表达式检查并仍然提供带扩展名的文件名.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.";
}
Run Code Online (Sandbox Code Playgroud)
尽管我努力尝试并在网上搜索,但我无法找到能够使这种事情成为可能的缺陷.我能忽略一些东西吗?鉴于处理了"空字节"漏洞,这里还有什么问题?
注意:我绝不暗示此代码是检查文件扩展名的完全证明方法,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";
}
Run Code Online (Sandbox Code Playgroud)
PHP版本:5.3.29
编辑:结语
原来,"漏洞"只出现在Windows上.然而,它确实完成了我的同事告诉我它将通过正则表达式检查并保存带有可执行扩展名的文件.以下测试上WampServer 2.2有PHP 5.3.13:
将以下字符串传递给上面的正则表达式检查test.php:.jpg(注意所需扩展名末尾的":"冒号)将验证它,并且函数copy()似乎省略冒号后的所有内容,包括符号本身.同样,这仅适用于Windows.在linux上,文件将与传递给函数的名称完全相同.
Shl*_*sid 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";
}
Run Code Online (Sandbox Code Playgroud)
这将最终打印"在!!!!" 并执行'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";
}
Run Code Online (Sandbox Code Playgroud)
再次非常好.该文件被复制到" uploads "文件夹中 - 您无法直接访问它(因为Web服务器将删除#的右侧)但是您注入了该文件并且攻击者可能会找到一种方法或另一个弱点来调用它以后.
这种执行情况的一个例子是共同分享和发布网站,该文件是由PHP脚本提供其(在一些不安全的情况下),可以通过与错误类型的功能,例如,包括它加载的文件中require,include,file_get_contents这都很容易并可以执行该文件.
NULL字节 空字节攻击是php <5.3中的一个很大的弱点,但是在一些函数(包括所有与文件相关的函数以及更多扩展函数)中的版本5.4+中的回归重新引入.它被修补了几次,但它仍然存在,很多旧版本仍在使用中.如果你使用较旧的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";
}
Run Code Online (Sandbox Code Playgroud)
将打印"在!!!!" 并复制名为"test.php"的文件.
php修复的方式是通过检查字符串长度之前和之后将其传递给更深层次的C程序,该程序创建实际的char数组,如果字符串被空字节截断(表示C中字符串的结尾)的长度不会匹配.阅读更多
奇怪的是,即使在补丁和现代PHP版本中,它仍然存在:
$input = "foo.php\0.gif";
include ($input); // Will load foo.php :)
Run Code Online (Sandbox Code Playgroud)
我的结论:
您的验证文件扩展的方法可以得到显着改进 - 您的代码允许调用的PHP文件test.php#.jpg通过而不应该通过.成功的攻击通常是通过组合多个漏洞甚至是次要漏洞来执行的 - 您应该将任何意外的结果和行为视为一个.
注意:关于文件名和图片的问题还有很多,因为它们在以后的页面中有很多时间,如果它们没有被正确过滤并且安全地包含在内,那么你会暴露自己更多的XSS内容,但这不是主题.