【问题标题】:PHP Imagick reinterpretation of PNG IDAT chunksPHP Imagick 重新解释 PNG IDAT 块
【发布时间】:2023-12-02 09:11:01
【问题描述】:

我注意到 PHP Imagick 在处理 PNG 时会更改 IDAT 块。 这究竟是如何完成的?是否有可能创建保持不变的 IDAT 块?是否可以预测 Imagick 的结果?

此问题的背景信息: 我想知道以下代码(PHP 文件上传的一部分)是否可以防止在 PNG 中隐藏 PHP 代码(例如 webshel​​l):

$image = new Imagick('uploaded_file.png');
$image->stripImage();
$image->writeImage('secure_file.png');

评论被删除,因此绕过此过滤器的唯一方法是将 PHP 有效负载隐藏在 IDAT 块中。如here 所述,理论上是可能的,但即使我将CompressionCompressionQuality 设置为我用来创建PNG 的值,Imagick 也会以某种方式重新解释此图像数据。我还设法创建了一个 PNG,其 ZLIB 标头由 Imagick 保持不变,但原始压缩图像数据没有。我获得相同输入和输出的唯一PNG是之前通过Imagick的PNG。我也尝试在source code中找到原因,但找不到。

我知道其他检查是必要的,以确保上传的文件实际上是 PNG 等,如果服务器配置正确,PNG 中的 PHP 代码没有问题,但现在我只对这个问题。

【问题讨论】:

    标签: php imagemagick png zlib imagick


    【解决方案1】:

    IDAT 块可以变化,但仍会产生相同的图像。不幸的是,PNG 规范强制 IDAT 块形成单个连续数据流。这意味着可以对数据进行不同的分组/分块,但是当重新组合成单​​个流时将是相同的。是实际数据不同还是只是“分块”改变了?如果是后者,那么图像是否相同又有什么关系呢? PNG 是一种无损压缩类型,剥离元数据,甚至解压缩+重新压缩图像都不应该改变任何像素值。

    如果您比较压缩数据并期望它是相同的,它可能会不同,但仍会产生相同的图像。这是因为 FLATE 压缩使用迭代过程来查找先前数据中的最佳匹配。您给它的“质量”数字越高,它搜索匹配项并缩小输出数据大小的次数就越多。使用 zlib,9 级 deflate 请求将比默认请求花费更长的时间,并导致输出数据大小略小。

    所以,请回答以下问题:

    1) 您是否尝试在剥离操作之前/之后比较压缩数据以查看图像是否以某种方式发生了变化?如果是这样,那么查看压缩数据不是解决问题的方法。

    2) 如果您想在不更改图像文件的任何其他方面的情况下剥离元数据,那么您需要自己编写工具。遍历 PNG 块并重新组装一个新文件,同时跳过要删除的块实际上很简单。

    回答我的问题,我会更新我的答案以提供更多详细信息...

    【讨论】:

    • 感谢您的回答。 (写入输出文件)。我想知道这里使用了哪些参数(例如,哪个压缩级别、哪个历史缓冲区(或窗口大小)以及哪个压缩策略)来生成一个 PNG,该 PNG 将具有完全相同的二进制数据(至少在 IDAT 块中)由 Imagick 处理。
    • 如果使用 ImageMagick 创建原始文件,那么您可能会有机会。如果使用了其他工具,您可能无法生成相同的 PNG 文件。如果您只是剥离元数据,那么解压缩和重新压缩图像会严重浪费 CPU 时间。更糟糕的是 ImageMagick 通常是一组非常慢的函数。无法访问所有这些参数。 IM 使用 zlib 和一个参数(压缩级别)来生成它的 flate 数据。
    • 2) 我无意找到(或编写)一个可以做到这一点的工具,实际上我正在寻找相反的东西。我设法用 GD 做同样的事情,所以我知道它在这一点上并不“安全”(尽管必须有其他漏洞才能在利用中使用这样的 PNG)。但只有我不能用 Imagick 做同样的事情这一事实并不能证明这是不可能的。所以我问了这个问题以澄清理论上是否可行。
    • 不确定你在说什么。什么是 GD,你想做什么?
    • GD 是 PHP 的另一个图像库。在this blogpost 中描述了如何生成在 GD 函数中幸存的恶意 PNG。所以我试图为 Imagick 做同样的事情(或“证明”这是不可能的)。
    【解决方案2】:

    我想知道以下代码(PHP 文件上传的一部分)是否可以防止在 PNG 中隐藏 PHP 代码(例如 webshel​​l)

    您永远不需要考虑这一点。如果您担心人们将 webshel​​l 隐藏在上传到您服务器的文件中,那么您做错了。

    例如,通过 PHP 解析器提供这些文件....这是可以调用 webshel​​l 来攻击服务器的方式。

    来自 Imagick 自述文件:

    5) 永远不要直接提供用户直接通过 PHP 上传的任何文件,而是通过网络服务器提供它们,而不调用 PHP,或者使用 readfile 在 PHP 中提供它们。

    readfile 不执行该文件,它只是将其发送给最终用户而不调用它,因此完全防止了您似乎关心的攻击类型。

    【讨论】:

    • 是的,我完全同意你的看法。这就是我想在最后一段中暗示的内容。不过,我对这个主题有理论上的兴趣(实际应用可能是 CTF 和安全测试)。