用于确定表面图像文件是否是真实图像文件的脚本

用于确定表面图像文件是否是真实图像文件的脚本

我想创建一个 shell 脚本来检查以确保目录中表面上看起来是图像文件的所有文件(例如具有典型的图像文件扩展名,如 .jpg、.bmp 等)实际上是图像文件。

我们最近遇到一个问题,黑客能够在目录中生成文件并将其掩码为 .jpg 文件。我想创建一个 shell 脚本来检查目录中的所有文件,以确保它们是真正的 jpg、gif 或 png 文件。

答案1

file我认为在提供完全不受信任的输入的情况下使用时要非常小心。例如,RHEL 5file将识别这一点:

GIF87a
<?php
echo "Hello from PHP!\n";
?>

作为“GIF 图像数据,版本 87a,15370 x 28735”。 PHP 解释器执行该输入没有任何问题。没有麻烦是“本地文件包含“(LFI)问题。

其次,file(甚至strings)实际解析输入文件以告诉您您想知道的内容。这些解析器很复杂有问题

我将建议该identify命令图像魔术师套房。它不会被我上面的简单示例所迷惑,它只会正确解析图像文件,因此它应该比file.

答案2

作为快速的第一遍,file命令可以快速检测图像标题:

if file "$FILE" |grep -qE 'image|bitmap'; then
  echo "File '$FILE' has the headers of an image"
fi

bitmap(如果您想识别,则需要第二次交替Windows BMP 文件因为 libmagic 不使用“图像”一词来描述位图图像。)

然而,我们可以file使用基于 PHP 的假图像进行欺骗布鲁斯·艾迪格的回答:

$ echo 'GIF87a<?php echo "Hello from PHP!"; ?>' > fake.gif
$ file fake.gif && echo image detected || echo no image detected
fake.gif: GIF image data, version 87a, 16188 x 26736
image detected

使用Imagemagick识别

图像魔术师套房设有确认带有 CLI 前端的脚本将返回给定图像的一些元数据。当预期的元数据不存在时,它会失败,因此它非常适合此目的:

$ identify fake.gif && echo image detected || echo no image detected
identify-im6.q16: negative or zero image size `fake.gif' @ error/gif.c/ReadGIFImage/1402.
no image detected

为了更快地扫描大量文件,我建议将两者放在一起:

if file "$FILE" |grep -qE 'image|bitmap' \
&& ! identify "$FILE" >/dev/null 2>&1; then
  echo "File '$FILE' is a fake image!"
fi

(这会将 的输出重定向identify到遗忘,因为我们只关心它是否能够成功完成,这是由其退出代码捕获的。)

即使这样仍然可以被欺骗

以下示例使用一个简单的 1x1 白色 GIF,并在末尾添加了相同的 PHP 代码。我不了解 PHP,也不确定这是否会实际运行,但由于 PHP 是一种模板语言,它将文字“文本”打印到其<?php … ?>标记之外的任何内容,我假设这将按原样运行给定的代码,有效负载之前只有一些垃圾。

$ { echo 'R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBAD'
    echo 's8P3BocCBlY2hvICJIZWxsbyBmcm9tIFBIUCEiOyA/Pgo='
  } | base64 -d > fake2.gif
$ strings fake2.gif
GIF87a
;<?php echo "Hello from PHP!"; ?>
$ file fake2.gif
fake2.gif: GIF image data, version 87a, 1 x 1
$ identify fake2.gif
fake2.gif GIF 1x1 1x1+0+0 8-bit sRGB 2c 68B 0.000u 0:00.000

这也可以通过 GIF 注释来完成,使其作为图像完全有效:

$ hd fake3.gif
00000000  47 49 46 38 39 61 01 00  01 00 80 00 00 ff ff ff  |GIF89a..........|
00000010  ff ff ff 21 fe 20 3c 3f  70 68 70 20 65 63 68 6f  |...!. <?php echo|
00000020  20 22 48 65 6c 6c 6f 20  66 72 6f 6d 20 50 48 50  | "Hello from PHP|
00000030  21 22 3b 20 3f 3e 00 2c  00 00 00 00 01 00 01 00  |!"; ?>.,........|
00000040  00 02 02 44 01 00 3b                              |...D..;|
00000047

我选择了 GIF 并利用了它的评论系统,但只要在任何图像后面连接一个有效负载也应该可以绕过这种检测技术。它只是比愚弄更难file并且(取决于实现)它可能留下一些证据(图像中的垃圾)。

答案3

按照@Adam Katz 整理的命令,我发现我的系统总是失败,因为-q用于抑制 grep 输出的选项使其始终给出零的返回码。删除它可以让它正常工作,但这意味着识别命令的输出散布在屏幕上。

我使用的是 GNU bash,版本 5.1.16(1)-release (x86_64-pc-linux-gnu) 和 GNU grep 3.7,默认情况下随 Xubuntu 22.04.1 LTS 一起提供。

我的解决方案的工作方式与 Adams 相同,从测试中运行必要的命令。

[[ ( $(file "$file" | grep -E 'image|bitmap') != ""  ) \
&& ( $(identify $file | grep error) -eq 0 ) ]] \
&& echo "File $file appears to be an image" \
|| echo "File $file appears to be a fake"

我希望这对某人有帮助,就像亚当的帖子对我的帮助一样。

相关内容