阻止人们上传含有注入物的GIF的方法是什么?

17
我有一个 PHP 网站,人们可以填写帮助票据。它允许他们上传票据的截图。我允许 gif、psd、bmp、jpg、png、tif 的上传。在接收到上传时,PHP 脚本会忽略文件扩展名。它仅使用 MIME 信息来识别文件类型,对于这些文件类型,MIME 信息始终存储在文件的前12个字节中。
有人上传了几个 GIF 文件,在浏览器中查看时,浏览器说它是无效的,并且我的病毒扫描器警告我它是注入(或类似的东西)。请参见下面的 zip 文件,其中包含这些 GIF 文件。
我认为仅检查头信息是不足够的。我听说过一张图片可能完全有效,但也可能包含利用代码。
所以我有两个基本问题:
  1. 有人知道他们如何将恶意内容注入GIF图像(同时保持有效的GIF MIME类型)吗?如果我知道这个,也许我可以在上传时检查它。
  2. 我如何防止某人上传此类文件?
    • 我在共享托管上,无法安装服务器端病毒扫描程序。
    • 提交信息到在线病毒扫描网站可能太慢了。
    • 是否有任何使用PHP类自己检查这些内容的方法?
    • 如果使用GD调整图像大小失败,那么这种方法会失效吗?还是说这种漏洞仍然会通过并出现在调整大小后的图像中?如果失败了,那就太理想了,因为我可以使用调整大小作为一种技术来判断它们是否有效。

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

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


重要澄清:我只关心下载这些文件并查看它们的计算机的风险。这些文件不会对我的服务器造成风险。它们不会被执行。它们使用干净的名称(十六进制哈希输出)进行存储,扩展名为".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);

1
我记得这篇文章帮助我解决了当时的问题 http://www.phpclasses.org/blog/post/67-PHP-security-exploit-with-GIF-images.html - Benjamin Gruenbaum
一个非常好的问题,我也想知道答案。+1 - halfer
相关:过去曾经有ImageMagick的漏洞被报告。 - halfer
这是一个很好的答案,针对这个问题——在单独的、沙箱化的进程中运行您的图像处理。 - halfer
1
多年前遇到過這個問題。通過使用GD庫(從一個圖形內容獲取並將其寫入另一個gif文件)來重寫內容到新文件中解決了這個問題。但它無法處理動畫GIF。惡意代碼大多放置在GIF的“注釋”字段中。 - Wh1T3h4Ck5
显示剩余4条评论
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提供文件;在向用户提供文件时,请勿使用上传文件指定的Content-Type。如果您依赖于上传者指定的Content-Type,则该文件可能会被作为text/html或类似内容提供,并被用户浏览器解析。

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

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

还要注意,此代码无法防御包含用户浏览器所使用的图像解析器漏洞利用的图像;为了防御此类攻击,您可以检查getimagesize()是否为真,如Jeroen所建议的那样。

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


这只是一个开始,但不能保护GD2/ImageMagick等理论漏洞。 - halfer
不,我的意思是有人可以将一个 .exe 文件重命名为 .gif,这样就可以通过你的初始代码。 - Jeroen
@Jeroen 你说得对,第一段代码假设服务器将使用与图像相关联的正确“Content-Type”来提供具有这些扩展名的文件 - 这可能并非总是如此。 - user2428118
只是为了澄清,我只是在评论你最初提供的代码;既然你的答案已经被编辑过了,它现在确实是一个好建议! - Jeroen
@user1419007,我没有从你的答案中读到这一点。不要依赖内容头部分是清楚的,但背后的原因可能需要改进一些。 - Jacco
显示剩余5条评论

2
你可以使用getimagesize()函数来实现这个功能。如果图片无效,它将会返回false
if (getimagesize($filename)) {
    // valid image
} else {
    // not a valid image
}

值得注意的是,这种方法也不是100%安全的,但据我所知,这已经是你所能做到的最好了。
阅读更多关于此的信息

2
你关于“不是100%安全”的说法是正确的。在完全合法的图像文件中放置利用代码是完全可能的。 - paulsm4
谢谢这篇文章,非常有启发性。我不认为getimagesize()是可靠的。 - Lakey

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

1

我对图像格式不是很了解,但重新创建图像并存储结果,我觉得有很大的机会消除不必要的棘手问题。特别是如果您剥离所有元数据,如注释和某些图像格式支持的所有其他类型的可选嵌入字段。


这可能会删除嵌入的PHP,但重新创建图像理论上可能触发低级缓冲区溢出漏洞(例如我在问题下发布的ImageMagick漏洞)。 - halfer
如果按照这种逻辑,所有软件都应该被避免使用,因为所有软件都容易受到缓冲区溢出的攻击。除非你是在暗示图像库有着特别糟糕的记录,因此应该基于这个原因而避免使用它们。无限循环。 - goat
我认为这个建议很不错——重新创建上传的图像。问题是,如果出现漏洞,GD(或插入图像库)是否会失败,还是会不知情地将漏洞传递给重新创建的文件? - Lakey
@chris:不是避免,而是因为对于其他类别的网络输入(如字符串、整数),我们知道在使用它们之前如何进行过滤。因此,需要进行非平凡低级处理(例如图像调整)的文件会感觉有点更危险,因为它们没有经过我们在 PHP 中进行的检查。处理恶意文件的可能解决方案:对于大型安装,使用一个单独的上传服务器,该服务器无法访问数据库;对于较小的安装,则可以使用沙盒进程。 - halfer

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

0
非常有用的提示,可以避免注入 PHP 的问题,来自我的主机系统管理员:我有一个网站,人们可以上传自己的内容。我想确保上传图像的目录不运行任何 PHP。这样,即使有人发布名为“test.php”的图片,如果它在上传目录中,也永远不会被 PHP 解析。解决方案很简单:在提供上传内容的文件夹中放置以下 .htaccess 文件:
RewriteEngine On
RewriteRule \.$ - [NC]
php_flag engine off

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


0

1) 如果您删除了 .gif 文件并且您的 A/V 没有写日志,那么您永远不会知道问题到底是什么。

问:相关的 .gif 文件是否仍在服务器上?

问:您是否已经检查了您的 A/V 日志?

2) 有许多不同的可能利用方式,这些可能与 .gif 文件格式直接或间接地有关。以下是一个例子:

3) 为了减轻这个例子中的风险,您应该:

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

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

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


是的,我更新了第一篇帖子,包括一个带有病毒的ZIP文件,其中包含gif。 - Lakey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接