18

我有一个 PHP 网站,人们可以在其中填写帮助票。它允许他们为他们的票上传屏幕截图。我允许上传 gif、psd、bmp、jpg、png、tif。收到上传后,PHP 脚本会忽略文件扩展名。它仅使用 MIME 信息识别文件类型,对于这些文件类型,这些信息始终存储在文件的前 12 个字节中。

有人上传了几个 GIF,当用浏览器查看时,浏览器说它无效,我的病毒扫描程序提醒我这是注入(或类似的东西)。请参阅下面的包含这些 GIF 的 zip 文件。

我认为仅检查标题信息是不够的。我听说图像可以完全有效,但也包含漏洞利用代码。

所以我有两个基本问题:

  1. 有谁知道他们是如何将坏东西注入 GIF 的(同时仍然保持有效的 GIF MIME 类型)?如果我知道这一点,也许我可以在上传时检查它。
  2. 如何防止有人上传这样的文件?
    • 我在共享主机上,因此无法安装服务器端病毒扫描程序。
    • 将信息提交到在线病毒扫描网站可能太慢了。
    • 有没有办法使用检查这些东西的 PHP 类来检查自己?
    • 如果它无效,使用 GD 调整图像大小会失败吗?还是该漏洞仍然会溜走并出现在调整大小的图像中?如果它失败了,那将是理想的,因为那时我可以使用调整大小作为一种技术来查看它们是否有效。

更新:大家,感谢到目前为止的回复。我正在尝试在服务器上查找已上传的 GIF。如果我找到它们,我会更新这篇文章。

更新 2:我为任何感兴趣的人找到了 GIF。我将它们放在使用密码“123”加密的 zip 文件中。它位于此处(请注意,此托管站点上有多个“下载”按钮 - 其中一些用于广告)http://www.filedropper.com/badgifs。我的防病毒软件将名为 5060.gif 的那个标记为木马 (TR/Graftor.Q.2)。我应该注意,这些文件是在我实施前 12 个字节的 MIME 检查之前上传的。所以现在,我对这些特殊的人是安全的。但我仍然想知道如何检测隐藏在正确 MIME 类型后面的漏洞。


重要说明: 我只关心下载这些文件以查看它们的 PC 的风险。 这些文件对我的服务器没有风险。他们不会被处决。它们使用扩展名为“.enc”的干净名称(十六进制哈希输出)存储,我使用 fwrite 过滤器以加密状态将它们保存到磁盘:

// Generate random key to encrypt this file.
$AsciiKey = '';
for($i = 0; $i < 20; $i++)
    $AsciiKey .= chr(mt_rand(1, 255));

// The proper key size for the encryption mode we're using is 256-bits (32-bytes).
// That's what "mcrypt_get_key_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)" says.
// So we'll hash our key using SHA-256 and pass TRUE to the 2nd parameter, so we
// get raw binary output.  That will be the perfect length for the key.
$BinKey = hash('SHA256', '~~'.TIME_NOW.'~~'.$AsciiKey.'~~', true);

// Create Initialization Vector with block size of 128 bits (AES compliant) and CBC mode
$InitVec = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
$Args = array('iv' => $InitVec, 'key' => $BinKey, 'mode' => 'cbc');

// Save encoded file in uploads_tmp directory.
$hDest = fopen(UPLOADS_DIR_TMP.'/'.$Hash.'.enc', 'w');
stream_filter_append($hDest, 'mcrypt.rijndael-128', STREAM_FILTER_WRITE, $Args);
fwrite($hDest, $Data);
fclose($hDest);
4

7 回答 7

6

至于第一个问题,您永远不会真正知道您是否无法检索任何有问题的日志或图像,因为这些漏洞利用可能针对很多东西,并且取决于漏洞利用方式的目标是什么进入文件可以完全不同。

编辑: W32/Graftor是似乎具有木马特征的程序的通用名称。

在十六进制编辑器中打开文件后5060.gif,我注意到该程序实际上是一个重命名的 windows 程序。虽然它不是浏览器漏洞,因此除非它被实际打开并执行,否则它是无害的,但你必须确保它不是由上传者定义的 MIME 类型提供的,因为用户仍然可能被欺骗打开程序;看第二个问题的答案。

至于第二个问题:为了防止任何漏洞利用代码被运行或用户,您必须确保所有文件都以安全的扩展名存储在文件名中,以便使用正确的 MIME 类型提供服务。例如,您可以使用这个正则表达式来检查文件名:

if(!preg_match ( '/\\.(gif|p(sd|ng)|tiff?|jpg)$/' , $fileName)){
    header("415 Unsupported Media Type");
    die("File type not allowed.");
}

还要确保您使用正确的内容类型提供文件;确保在向用户提供文件时不使用上载文件指定的内容类型。如果您依赖上传者指定的 Content-Type,则该文件可能会被用作text/html或任何类似的东西,并将由用户的浏览器进行解析。

请注意,这只防止恶意文件利用用户浏览器中的漏洞,图像解析器除外。

如果你试图阻止对服务器的攻击,你必须确保你不会让 PHP 解析器执行图像的内容,并且你用来处理图像的图像库没有任何已知的漏洞。

另请注意,此代码不会保护您免受包含用户浏览器使用的图像解析器漏洞的图像的攻击;为了防止这种情况,您可以getimagesize()按照 Jeroen 的建议检查评估是否为真。

请注意,getimagesize()如果您不检查文件名并确保使用正确的标题提供文件,则单独使用是不够的Content-Type,因为完全有效的图像可以在注释中嵌入 HTML / PHP 代码。

于 2012-06-02T19:27:49.807 回答
2

您可以在任何接受上传的 php 脚本上尝试 phpMussel。该文件将使用 ClamAV 签名以及一些专门寻找此类入侵的内部启发式签名进行扫描。

于 2014-09-23T09:25:35.627 回答
2

您可以为此使用getimagesize()函数。如果图像无效,它将简单地返回false

if (getimagesize($filename)) {
    // valid image
} else {
    // not a valid image
}

值得注意的是,这也不是 100% 安全的,但据我所知,这是你能做的最好的。

在此处阅读有关此内容的更多信息。

于 2012-06-02T19:33:13.680 回答
1

我不太了解图像格式,但是重新创建图像然后存储结果,我觉得很有可能消除不必要的棘手内容。特别是如果您剥离所有元数据,如评论和某些图像格式支持的所有其他类型的可选嵌入字段。

于 2012-06-02T19:36:22.013 回答
0

1) 如果您删除了 .gif 并且您的 A/V 没有写入日志,您将永远不会确切知道问题出在哪里。

问:有问题的 .gif 文件还在服务器上吗?

问:您检查过您的 A/V 日志吗?

2) 有许多不同的可能漏洞,它们可能与 .gif 文件格式直接相关,也可能不相关。这是一个例子:

3) 为了减轻本示例中的风险,您应该:

a) 仅将文件(任何文件)上传到服务器上的安全目录

b) 仅提供具有特定后缀(.gif、.png 等)的文件

c) 对上传到您网站的任何内容都非常偏执(特别是如果您随后允许其他人从您的网站下载它!)

于 2012-06-02T19:35:05.440 回答
0

防止注入 PHP 问题的非常有用的提示来自我主机的系统管理员:我有一个站点,人们可以在其中上传自己的内容。我想确保上传图片的目录不运行任何 PHP。这样,有人甚至可以发布一张名为“test.php”的图片,如果它位于上传目录中,PHP 将永远不会对其进行解析。解决方案很简单:在文件夹中,上传的内容是从以下 .htacess 提供的:

RewriteEngine On
RewriteRule \.$ - [NC]
php_flag engine off

这将关闭文件夹的 PHP 引擎,从而阻止任何启动任何 PHP 以利用服务器端漏洞的尝试。

于 2015-06-24T10:48:01.683 回答
0

迟到的回应,但可能对某人有用。您可以尝试这样的方法:

//saves filtered $image to specified $path
function save($image,$path,$mime) {
    switch($mime) {
        case "image/jpeg"   : return imagejpeg(imagecreatefromjpeg($image),$path);
        case "image/gif"    : return imagegif(imagecreatefromgif($image),$path);
        case "image/png"    : return imagepng(imagecreatefrompng($image),$path);
    }
    return false;
};
于 2016-05-10T09:36:25.440 回答