PHP正则表达式漏洞赌注

PHP正则表达式漏洞赌注,php,regex,Php,Regex,今天一位同事跟我打赌,他知道一种方法,可以提供一个特殊格式的字符串,该字符串可以通过下面的正则表达式检查,并且仍然提供扩展名为.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 .j

今天一位同事跟我打赌,他知道一种方法,可以提供一个特殊格式的字符串,该字符串可以通过下面的正则表达式检查,并且仍然提供扩展名为
.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上出现。尽管如此,它还是按照我同事告诉我的那样做了——通过了正则表达式检查,并以可执行扩展名保存了文件。使用
PHP5.3.13
WampServer 2.2
上测试了以下内容:

将以下字符串传递给上面的正则表达式检查
test.php:.jpg
(注意“:”所需扩展名末尾的冒号符号)将对其进行验证,函数
copy()
似乎会忽略冒号符号后面的所有内容,包括符号本身。
同样,这仅适用于windows。在linux上,该文件将使用与传递给函数的名称完全相同的名称写入。

没有一个步骤或完全直接的方法来利用您的代码,但这里有一些想法

在本例中,您将其传递给
copy()
,但您已经提到,您使用此方法验证文件ext已经有一段时间了,因此我假设您有其他情况,可能在不同的PHP版本上也将此过程与其他函数一起使用

将此视为测试程序(包括、要求):

这将通过打印“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) && preg_match('/\.(asp|jsp|php)$/i', $name) == false) {
    echo "in!!!!";
    copy($_FILES['image']['tmp_name'], "../uploads/".$name);
} else {
    echo "Invalid image file";
}
再好不过了。该文件被复制到“uploads”文件夹中-您无法直接访问它(因为web服务器将剥离#的右侧),但您插入了该文件,攻击者可能会找到一种方法或另一个弱点来稍后调用它

此类执行场景的示例在共享和托管站点中很常见,其中文件由PHP脚本提供服务,而PHP脚本(在某些不安全的情况下)可能通过使用错误类型的函数(如
require
include
)来加载文件,
file\u获取所有易受攻击且可执行文件的内容

空字节 空字节攻击是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";
}
将打印“in!!!!”并复制名为“test.php”的文件

php修复的方法是,在将字符串传递给更深层的C过程(创建实际的字符数组)之前和之后检查字符串长度,如果字符串被空字节截断(表示C中字符串的结尾),则长度将不匹配

奇怪的是,即使在经过修补的现代PHP版本中,它仍然存在:

$input = "foo.php\0.gif";
include ($input); // Will load foo.php :)
我的结论: 您验证文件扩展名的方法可以得到显著改进—您的代码允许名为
test.PHP#.jpg的PHP文件通过,而它本不应该通过。成功的攻击主要是通过组合几个漏洞,甚至是小漏洞来执行的,你应该考虑任何意外的结果和行为。
注意:对于文件名和图片还有更多的担忧,因为它们会在以后的页面中多次被包含,如果它们没有被正确过滤并安全地包含,您将暴露在更多XSS内容中,但这不属于主题

如果模式与给定主题匹配,则返回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

理想情况下,您应该使用。
我记得在PHP<5.3.X的某些版本中,PHP允许字符串包含
0x00
,此字符被视为字符串的结尾
因此,例如,如果字符串包含:myfile.exe\0.jpg,那么
preg\u match()
将匹配jpg,但其他PHP函数将停止在myfile.exe中,
include()
copy()
函数请尝试此代码

$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";
     }
}

为什么两个单独的
preg_match
?为了更好地说明,这不是实际的生产代码为什么要问我们?如果你的同事真的知道一种方法,让他说出来:P@SeanBright对。关于这个问题本身有什么吗?@revo它是
D(PCRE\u DOLLAR\u ENDONLY)
我已经找到你了<代码>复制
可以。大多数人都喜欢这样。但是仍然在想什么时候任何人都可以在PHP代码中包含图像文件?(主要是可以执行PHP代码)引用
,但在5.4+版本的某些函数中重新引入了回归
?如果您将其限制为图像,则大多数情况下不会,但这是一个常见的初学者错误,可能会根据需要或包含而不是
readfile
-或在模板中将文件返回给任何类型的用户
function isImageFile($file) {
    $info = pathinfo($file);
    return in_array(strtolower($info['extension']), 
                    array("jpg", "jpeg", "gif", "png", "bmp"));
}
$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";
     }
}