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上呈现.然而,它确实是我的同事告诉我的,它将通过正则表达式检查,并以可执行扩展名保存文件.以下是使用PHP 5.3.13在WampServer 2.2上进行了测试:
将以下字符串传递到test.PHP上面的正则表达式检查:.jpg(注意在所需扩展名结尾处的“:”冒号符号)将验证它,并且函数copy()似乎省略了包含符号的冒号后的所有内容本身.
再次,这只适用于Windows.在linux上,文件将被完全写入与传递给该函数的相同名称.
你在这个例子中传递给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"; }
这将最终通过打印“in !!!!”并执行’test.PHP’,即使它是上传它将包括它从tmp文件夹 – 当然,在这种情况下,你已经拥有的攻击者,但我们也考虑这个选项.
上传过程不是常见的情况,但它是一个可以通过组合几种方法来利用的概念:
我们继续吧 – 如果你执行:
//$_FILES['image']['name'] === "test.PHP#.jpg"; $name = $_FILES['image']['name']; if (preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i',$name) == false) { echo "in!!!!"; copy($_FILES['image']['tmp_name'],"../uploads/".$name); } else { echo "Invalid image file"; }
再次完美的该文件被复制到“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',"../uploads/".$name); } else { echo "Invalid image file"; }
通过检查字符串长度之前和之后,将其传递给更深入的C过程,创建实际的字符数组,以及如果该字符串被空字节(表示C中的字符串结尾)截断的长度将不匹配. read more
奇怪的是,即使在补丁和现代PHP版本,它仍然在那里:
$input = "foo.PHP\0.gif"; include ($input); // Will load foo.PHP :)
我的结论:
您的验证文件扩展名的方法可以显着改善 – 您的代码允许一个名为test.PHP#.jpg的PHP文件通过,而不应该.成功的攻击主要是通过组合几个脆弱性甚至是小的攻击来执行的 – 你应该将任何意想不到的结果和行为视为一个.
注意:有更多的关于文件名和图片的问题,因为他们很多时间包括在页面以后,如果没有被正确过滤,并安全地包括你暴露了许多更多的XSS的东西,但这不在话题.