前言
这段时间结合了upload-lab看了下文件上传漏洞,下面我想做一些总结
文件上传漏洞的介绍
文件上传漏洞是一种比较容易获得webshell的漏洞利用方式,多见于有图片上传功能的位置,通过上传一句话木马连接到服务器,以备后续操作。
检验文件上传漏洞的一些思路
客户端校验 禁用或修改js或者不用浏览器上传即可
服务端校验
- 黑名单
- 尝试上传.htaccess 添加AddType 将合法后缀名解析为php等
- 尝试使用等效后缀名 例如php替换为php3,php5等等
- php: php5,php4,php3,php2,html,htm,phtml,pht,pHp,pHp5,pHp4,pHp3,pHp2,Html,Htm,pHtml
- asp: asp,aspx,asa,asax,ascx,ashx,asmx,cer,aSp,aSpx,aSa,aSax,aScx,aShx,aSmx,cEr
- jsp: jspa,jspx,jsw,jsv,jspf,jtml,jSp,jSpx,jSpa,jSw,jSv,jSpf,jHtml
- 利用操作系统特性例如win下,后缀名不予许有.,空格,::$DATA等等
- 利用服务器解析特性
- 双后缀名绕过
- 白名单
- Content-type校验
- %00截断和0x00截断
- move_uploaded_file函数忽略/.
- 内容校验
- 文件开始的内容校验
- JPEG;.JPE;.JPG,”JPGGraphicFile”(FFD8FFFE00)
- gif,”GIF89A”(474946383961)
- zip,”ZipCompressed”(504B0304)
- doc;.xls;.xlt;.ppt;.apr,”MSCompoundDocumentv1orLotusApproachAPRfile”(D0CF11E0A1B11AE1)
- 一次渲染
- 二次渲染
- 文件开始的内容校验
- 黑名单
逻辑 主要是竞争上传
upload-labs 部分实例
pass 1-2
js校验和content type校验
pass 3
等价后缀绕过
1 | $deny_ext = array('.asp','.aspx','.php','.jsp'); |
显然可以用php5等后缀绕过
pass 4 上传.htaccess
1 | $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",...); |
没有过滤.htaccess ,上传.htaccess
1
AddType application/x-httpd-php .jpg
或者
1
AddHandler application/x-httpd-php .jpg
pass 5
1 | $is_upload = false; |
显然没有过滤后缀名的大小写,尝试Php,可以得到phpinfo.Php
还有一种过waf的方法就是双写::$DATA,可以得到xxx.php
pass 6
1 | $is_upload = false; |
审计代码发现比起上一关的代码减少了对后缀名前后的空格的过滤,那么修改后缀名为phpinfo.php(空格) 即可
pass 7
1 | $is_upload = false; |
比较这一题的代码和上一题的代码主要有两个不通点,一个自然是过滤了空格,另外一个则是在拼接img_path时,$file_name并没有采取随机数重命名的方式而采用了原名,这样就给双后缀名绕过提供了机会。因为$file_ext是最后一个.开始到结束的字符串,而这题中原文件名并没有被处理,如果上一部分包含了.php等后缀名便可被解析。
尝试phpinfo.php.
这里还有一种方法就是利用apache的解析漏洞,如果文件后缀名apache无法解析,那么apache就会从已解析的后缀名往左解析例如phpinfo.php.xxx无法解析,那么apache便会解析.php,然而我试了下好像没有用。
另外一种方法就是00截断,这种方法我想待会一起讨论
pass 8
1 | $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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"); |
显然这题没有过滤::$DATA,那么就用 ::$DATA绕过即可
pass 9
1 | $is_upload = false; |
这一题没有重写文件名,故可以采用phpinfo.php. .尝试绕过($file_ext=(空格))
pass 10
1 | $is_upload = false; |
看到str_ireplace 方法的时候就可以想到双写绕过了,故phpinfo.phphpp过
pass 11-12
这两题的共同特点是通过%00截断过waf
抓包或者审计源码可以发现
1 | $ext_arr = array('jpg','png','gif'); |
是白名单加上传递参数定义存储文件路径
这里save_path可以被修改,如果传入/upload/phpinfo.php%00.jpg
在move_upload_path函数中%00会产生截断字符串的效果,从而实际写入路径变为/upload/phpinfo.php绕过成功
00截断的要求有两个,一个是php版本小于5.3.4,另外一个是php的magic_quotes_gpc必须关闭,因为它会过滤get和post的请求中的\
%00在路径中被解析后,字符串会变为phpinfo.php\000.jpg,而\000在c语言中是终止符,因为php的底层实现四c语言,所以会产生这样的结果。
%00在get,post,甚至cookie都有效
如果需要在文件命中使用截断,则需要使用修改.php后的字节,例如.php(0x00).jpg格式(输入的时候不需要括号,可以在bp中hex内改)
pass 13-15
内容检查图片码
制作方法
cat xxx.jpg xxx.php> xxx.php
上传的时候需要修改后缀名和mime type
pass 16
二次渲染
gif上传之后和自己制作的图片码比较,将代码插入没有变化的位置。
png和jpg上传都是采用了现成脚本
参考了这篇文章 https://xz.aliyun.com/t/2657#toc-4
pass 17
竞争上传,主要的问题是使用move_uploaded_file之后rename,rename耗时较多,故上传线程开到5-6,然后浏览器多访问几次就成功。
pass 18
这段代码的逻辑其实是和17很相似,核心的问题是在move()和rename()这里
1 | //index.php |
竞争上传,多线程上传phpinfo.php.7z到apache上,可利用apache解析漏洞解析php
pass 19-20
move_uploaded_file() 忽略/.
上传phpinfo.php/. move_uploaded_file生成的新文件变为phpinfo.php
1 | //19 |
这题主要考了两个点,一个是move_uploaded_file忽略/.,另外一个是逻辑漏洞
逻辑漏洞主要是在$file_name=reset($file).’/‘.$file[count($file)-1];
取file数组的第一个元素和file的最后一个元素形成路径。而count函数的值代表了数组非空元素的个数
代码主要的问题是处在上传save_name为数组时,检查ext时是检测save_name的最后一个元素,现在比较理想的情况是构造一个可以解析的文件后缀
大概是xxx.php.xxx, 这就要求了$file[count($file)-1]和end($file)代表了不同的元素,那么传入两个元素save_name[0]=phpinfo.php
save_name[2]=.jpg,刚好能满足要求。
1 | //20 |
补充
可以写.htacccess或者.user.ini情况下
(XNUCA2019Qualifier的Ezphp)
1 |
|
这里主要是通过在.htaccess或者.user.ini配置文件中添加php配置来辅助文件上传姿势。(这里默认允许上传这些文件,或者配置文件可写)
首先要明确这些php配置可被设定范围
可以参考文档 https://www.php.net/manual/zh/configuration.changes.modes.php
.htaccess 文件能写的配置范围是PHP_INI_PERDIR和PHP_INI_ALL,能够使用php_value和php_flag指令来设置配置。
这道题的配置文件比较特殊
1 | ServerAdmin webmaster@localhost |
通过php_admin_flag 设置engine使得不解析除了index之外的文件,而且这里的php_admin_flag是不能在.htaccess中设置的,所以直接写php shell或者.htaccess木马都是没用的,而且index.php也是不能修改的。但是经过研究发现,这里的.htaccess文件可以写,应该是要在这上面做文章。
使用errorlog和includepath写入文件
第一步,通过error_log配合include_path在tmp目录生成shell。
1
2
3
4php_value error_log /tmp/fl3g.php
php_value error_reporting 32767
php_value include_path "+ADw?php eval($_GET[1])+ADs +AF8AXw-halt+AF8-compiler()+ADs"
# \(注释掉\n防止.htaccess报错)第二步,通过include_path和utf7编码执行shell。
1
2
3
4php_value include_path "/tmp"
php_value zend.multibyte 1
php_value zend.script_encoding "UTF-7"
# \
使prec正则表达式失效。
1
2php_value pcre.backtrack_limit 0
php_value pcre.jit 0然后使用php伪协议写入 auto_prepend_file来包含.htaccess即可。
使用\来拼接被过滤字符。
写session,这里参考 https://lihuaiqiu.github.io/2019/09/23/XNUCA2019-Ezphp/
而.user.ini可以写的配置范围是PHP_INI_ALL和PHP_INI_USER。(但是.user.ini中可以设置auto_append_file或者auto_prepend_file,不知道为啥我看文档只写了这两个)
- 通过auto_append_file或者auto_prepend_file包含图片马,加载图片中的内容(有include的效果)。