Pikachu靶场通关笔记(35) — Unsafe Fileupload (MIME Type Check)

49次阅读
没有评论

1. 漏洞概述

在上个关卡中,我们绕过了前端 JS 验证。这次我们面对的是 服务端验证 (Server-side Check)
表面上看,服务器似乎会对上传的文件进行检查,但关键在于它 检查的是什么。如果服务器仅仅依靠 HTTP 请求头中的 Content-Type (即 MIME 类型) 来判断文件类型,那么攻击者完全可以伪造这个字段,实现欺骗。

MIME (Multipurpose Internet Mail Extensions) 是用于标识文档类型的标准。例如:

  • .jpg -> image/jpeg
  • .php -> application/octet-streamtext/x-php

2. 闯关实操 (MIME Bypass)

第一步:初步测试
直接上传 3.php 一句话木马。
结果:上传失败,提示文件类型不符合要求。这说明服务端确实有检测机制。

Pikachu 靶场通关笔记(35) — Unsafe Fileupload (MIME Type Check)

第二步:Burp Suite 抓包伪造
既然是检测 MIME 类型,我们就可以利用 Burp Suite 进行“中间人欺骗”。

  1. 准备: 选择本地的 3.php 文件准备上传。
  2. 拦截: 开启 Burp Suite 拦截功能,点击“开始上传”。
  3. 分析: 查看抓到的数据包,找到 Content-Type 字段。
    • 默认情况下,上传 PHP 文件时,浏览器会自动将其标记为 Content-Type: application/octet-stream (或者是 application/x-php)。
  4. 修改: 将该字段修改为服务器允许的图片类型,例如:
    Content-Type: image/jpg
    (注意:文件名 filename="3.php" 不需要修改,因为我们要的是 PHP 后缀来执行)
  5. 放行: 点击 Forward 发送数据包。
Pikachu 靶场通关笔记(35) — Unsafe Fileupload (MIME Type Check)

第三步:验证与连接
网页提示“文件上传成功”,并返回路径 uploads/3.php
此时使用哥斯拉 (Godzilla) 连接该 URL,成功获得 Shell。

Pikachu 靶场通关笔记(35) — Unsafe Fileupload (MIME Type Check)

3. 源码分析

根据泄露的后端源码,我们可以看到问题的根源:

if(isset($_POST['submit'])){
    // 定义允许的 MIME 类型白名单
    $mime=array('image/jpg','image/jpeg','image/png');

    $save_path='uploads';
    // upload_sick 内部逻辑仅仅是对比 $_FILES['uploadfile']['type'] 是否在 $mime 数组中
    $upload=upload_sick('uploadfile',$mime,$save_path);

    if($upload['return']){$html.="... 文件上传成功...";} else {$html.="... 错误...";}
}

核心漏洞点:
PHP 中的 $_FILES['file']['type'] 变量,其值是 直接从 HTTP 请求头的 Content-Type 字段读取的
也就是说,代码逻辑等同于:
如果你告诉我 (HTTP Header) 你是一张图片,那我就相信你是一张图片。

它并没有深入去检查文件本身的内容(比如文件头部的二进制数据),所以我们只需修改 Header 就能骗过服务器。


4. 深度思考与防御修复

Content-Type 是客户端(浏览器)生成的,属于用户可控数据。在安全原则中,“所有来自客户端的数据都是不可信的”。任何可以通过抓包修改的数据,都不能作为安全验证的唯一依据。

防御修复方案:
要真正判断一个文件是不是图片,必须检查文件的 内容 ,而不是 标签

  1. 检查文件头 (Magic Number/File Signature):
    每种文件格式在文件开头都有特定的二进制标识。例如 JPG 是 FF D8 FF,PNG 是 89 50 4E 47PHP 修复示例 (使用 Fileinfo 扩展):
    $finfo = finfo_open(FILEINFO_MIME_TYPE); // 获取文件真实的 MIME 类型 (通过读取文件内容)
    $mime = finfo_file($finfo, $_FILES['uploadfile']['tmp_name']);
    finfo_close($finfo);
    $allowed_mimes = ['image/jpeg', 'image/png'];
    if (!in_array($mime, $allowed_mimes)) {die("非法文件类型!"); }
  2. 二次渲染 (Secondary Rendering):
    对于图片文件,可以使用图像处理库(如 GD 库)将上传的图片重新压缩或重新生成一遍。因为 Webshell 代码通常隐藏在图片空隙中,重绘图片往往会破坏恶意代码的结构。
  3. 结合后缀名检查:
    既检查 MIME,也检查后缀名,且必须是白名单机制。
正文完
 0
评论(没有评论)