开发者社区> 问答> 正文

文件上传漏洞防御——图片写马的剔除


最近回顾了一下CasperKid大牛在2011年11月发布的Upload Attack Framework,非常有感触,写得非常好,想深入了解这个漏洞的都推荐看看。
上传功能常见于图片的上传,例如博客头像设置,广告位图片上传等。
上传检测方法在paper中也写的比较明朗,这里总结一下:
1. 客户端使用JS对上传图片做检测,例如文件大小,文件扩展名,文件类型
2. 服务端检测,例如文件大小(免得拒绝服务),文件路径(避免0x00截断,目录遍历),文件扩展名(避免服务器以非图片的文件格式解析文件),文件类型(避免修改Content-Type为image/jpeg等),文件内容(避免图片写马)
上传检测绕过的方法,也总结一下:
1.客户端检测,相当于没有检测,可以使用HTTP代理例如burp绕过
2.服务端检测,一般采用白名单 黑名单的方式,但也极有可能出纰漏。例如大小写,不在名单内的特例,操作系统bt特性(windows系统会自动去掉文件名最后面的点和空格),0x00截断,服务器文件解析漏洞,最后还有图片写马绕过类型检测
本篇博客重点讲讲图片写马的检测。
我们知道PHP中文件类型的检测可以使用
1.$_FILES['uploaded']['type'];
2.getimagesize
两种方式来判断是否是正常图片,其实只要在不破坏图片文件格式的情况下,就可以绕过检测
例如使用以下命令,将正常图片与一句话php木马绑定在一起生成一个新的文件的方式
copy /b tangwei.jpg yijuhua.php tangweiyijuhua.jpg
我们查看新生图片的内容,在图片底端可以看到一句话木马写入,如下图所示
strings tangweiyijuhua.jpg



接下来我们演示这张图片是否能正常上传。
试验用到了两个脚本
1.upload.html 上传客户端
Choose an image to upload
2. upload.php 上传文件处理
这个脚本会检测文件后缀与文件类型,符合白名单jpeg格式的才允许上传,并打印出上传文件的基本信息及显示图片。
注意:红色字体部分可以先注释掉,下一步演示中会使用到
if (isset($_POST['upload'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['uploaded']['name'];
$filetype = $_FILES['uploaded']['type'];
$filesize = $_FILES['uploaded']['size'];
print "
File name : $filename
";
print "
File type : $filetype
";
print "
File size : $filesize
";
$tmpname = $_FILES['uploaded']['tmp_name'];
print "
Temp File path : $tmpname
";
$uploaddir='/var/www/upload/';
$target_path=$uploaddir.basename($filename);
// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);
print "
File extension : $fileext
";
$serverip = $_SERVER['SERVER_ADDR'];
//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path)){
print $target_path." successfully uploaded !
";
//显示上传的图片
print 'Origin image
';

//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
print "
new file name $newfilename
";
$newimagepath = $uploaddir.$newfilename;
imagejpeg($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
print 'New image
';

}else{
print "
Your image was not uploaded.
";
}
}else{
print "
Your image was not uploaded.
";
}
}else{
print "
Your image was not uploaded.
";
}
?>
进行上传操作,我们会发现写马后的图片也能正常生成预览,如下图所示


带有一句话木马的图片,如果配合文件解析攻击(将图片当成PHP或HTML等非图片格式来解析),就能起到webshell的作用。
所以,我们需要剔除掉图片中恶意代码部分内容, paper中说可以采取二次渲染,刚开始有点被这个名词吓到,后来才明白是啥意思,其实就是根据用户上传的图片,生成一个新的图片,然后删除用户上传的原始图片,将新图片存储到数据库中。这个过程,PHP开发会非常眼熟吧,这个不就是论坛头像设置功能中根据用户上传图片生成缩略图需求的代码实现大纲嘛。
接下来,我们试验一下重新生成的图片是否还包含恶意代码
让我们回到upload.php脚本的红色字体部分,代码功能是使用用户上传的图片生成新的图片,重新命名并在前端显示
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
print "
new file name $newfilename
";
$newimagepath = $uploaddir.$newfilename;
imagejpeg($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
print 'New image
';

关键函数imagecreatefromjpeg ,从jpeg生成新的图片(类似的还有imagecreatefromgif,imagecreatefrompng等),下图就是生成的新图片。


查看新图片的内容,会发现新生成的图片文件没有了绑定的一句话木马,算是成功去掉了图片写马了吧。
paper中说到了对于这种重新生成图片的防御方法,也有两种攻击方式
1. 利用数据二义性,构造符合图像数据格式的木马代码,在paper中只是个思路,不知道现在能不能正确构造出来这样的图像文件
2. 利用溢出,攻击生成新图片的函数,至于什么样的恶意图片会使得这个函数溢出,就不清楚了,希望大牛门指导
总结一下,对于文件上传的防御,做好以下几点,就投入与收益来看,应该足够了吧
1. 客户端初步检测文件大小,文件扩展名,文件类型
2. 服务端检测文件大小,文件扩展名(为了避免麻烦,可以替换上传文件的文件名,就是将文件路径与文件名都写死),文件类型
3.服务端根据用户图片生成新的图片存储到数据库

展开
收起
elinks 2013-05-07 08:15:36 12299 0
1 条回答
写回答
取消 提交回答
问答排行榜
最热
最新

相关电子书

更多
如何产生威胁情报-高级恶意攻击案例分析 立即下载
低代码开发师(初级)实战教程 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载

相关实验场景

更多