原文地址:https://www.cnblogs.com/cainiao-chuanqi/articles/14198190.html
文件上传漏洞
实验环境:
Windows 7 X64
Phpstudy 2018
PHP 5.4.45
Apache 2.4.23
靶机项目地址:https://github.com/c0ny1/upload-labs
Pass01 — JS限制
开启burp抓包,尝试上传eval.php,点击上传后页面直接提示该文件不允许上传,而burp并没有截取到数据包,判断为JS限制。
F12将JS限制代码找出来,将代码复制到console,修改允许上传的后缀为php再运行
点击上传后显示上传成功,在下方回显的地方右键复制图片地址即可获取上传后的图片路径。
为了直观查看上传的文件有没有被执行,在上传的文件中均是写的phpinfo()。复制图片地址后进行访问,页面出现phpinfo()。
除了使用console以外,还可以在浏览器中直接禁用JS,这样在上传的时候直接没有限制,可以传任意文件。
Pass02 — Content-Type限制
分析源码,在对上传文件的检测中只对文件的Content-Type做了限制,只要为源码中所限定的类型即可成功上传。
对于该类型只需要burp抓包修改Content-Type为白名单内的类型即可。
上传成功后,复制图片地址进行访问,成功执行。
在此补充几个常见的文件类型对应的content-ytpe:
HTML文档标记:text/html
普通ASCII文档标记:text/html
JPEG图片标记:image/jpeg
GIF图片标记:image/gif
js文档标记:application/javascript
xml文件标记:application/xml
Pass03 — 黑名单绕过
分析源码,后台对上传的文件限制较多,凡是黑名单中的内容全都不都上传,且不能用大小写、::DATA、末尾空格等措施绕过。
但如果在Apache配置文件中有.+.ph(p[345]?|t|tml)此类的正则表达式,或是在apache配置文件中存在类似于:AddType application/x-httpd-php .php .phtml .php3这样的语句。就可以将php3、php4、php5、phtml后缀的文件解析为php运行。
因此,php3、phtml后缀文件就可以成为黑名单之外的可进行解析的文件。在本关中可以尝试使用这些后缀进行绕过。
因为我的实验环境是在Windows环境下,在本关中需要到apache的配置文件中将
AddType application/x-httpd-php .php .phtml .php3 前的#号去掉,再重启apache服务即可成功解析phtml以及php3后缀的文件
Pass04 — .htaccess
分析源码,发现较Pass03来说,将php3、phtml等后缀的文件也纳入了黑名单范围。
在此就需要引进另一个概念:.htaccess文件
一般来说,配置文件的作用范围都是全局的,但Apache提供了一种很方便的、可作用于当前目录及其子目录的配置文件——.htaccess(分布式配置文件)
要想使.htaccess文件生效,需要两个条件:
一是在Apache的配置文件中写上:
AllowOverride All
若这样写则.htaccess不会生效:
AllowOverride None
二是Apache要加载mod_Rewrite模块。加载该模块,需要在Apache的配置文件中写上:
LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so
若是在Ubuntu中,可能还需要执行命令:
sudo a2enmod rewrite
配置完后需要重启Apache。
.htaccess文件可以配置很多事情,如是否开启站点的图片缓存、自定义错误页面、自定义默认文档、设置WWW域名重定向、设置网页重定向、设置图片防盗链和访问权限控制。但我们这里只关心.htaccess文件的一个作用——MIME类型修改。如在.htaccess文件中写入:
AddType application/x-httpd-php xxx
就成功地使该.htaccess文件所在目录及其子目录中的后缀为.xxx的文件被Apache当做php文件。
另一种写法是:
<FilesMatch "shell.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
该语句会让Apache把shell.jpg文件解析为php文件。
因此,该关卡可以尝试利用.htaccess进行绕过。
为验证的确是.htaccess文件生效,我先将eval.jpg上传,eval.jpg实际是由eval.php改后缀而来,其中包含的是phpinfo()的代码。
上传eval.php后,尝试访问图片
服务端并没有将文件当作php执行。
接下来在.htaccess中写入以下代码,再上传文件
AddType application/x-httpd-php jpg
Pass05 — 后缀大小写绕过
分析源码,相比于之前的对于后缀统一转为小写的防护措施,在本关卡中并未出现,因此可以考虑通过文件后缀的大小写来绕过检测
上传eval.php,通过burp抓包修改为eval.phP,放行数据包后,上传成功
Pass06 — 空格绕过(Windows环境下)
在开始本关的实验之前,先来解释一下为什么可以使用空格来进行上传绕过。
在windows环境下,xx.jpg[空格] 或xx.jpg.
这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点,但在数据包进行传输的时候,空格是被允许传送的。因此可以通过抓包软件,在文件名后加一个空格或者点,当数据包到达检测机制时,".php"
和 “.php.” 或 “php
“是不等同的,因此绕过了黑名单限制。当服务端为Windows时,上传成功后,空格和点都会被windows自动消除,这样就可以使其恢复为正常的”.php”
分析源码,发现对于文件后缀中的空格的限制消失了,本关尝试在后缀之后再追加一个空格来绕过。
burp抓包,在eval.php后追加一个空格,放行数据包后上传成功
Pass07 — 后缀后加.绕过(Windows环境下)
分析源码,该关卡和Pass06相比,Pass06是没有做后缀去空格处理,本关卡是没有做后缀去.处理。只需要将Pass06绕过时加的 " " 换成 “.” 即可
Pass08 — ::$DATA(Windows环境下)
开始本关实验之前,依然要引入一个概念:
当php在windows环境的时候,如果文件名+ “::D A T A " 会 把 " : : DATA" 会把 "::DATA"会把"::DATA” 之后的数据当成文件流处理,不会检测后缀名.且保持"::$DATA"之前的文件名
分析源码,在源码中并未对::D A T A 加 以 限 制 , 结 合 服 务 端 为 W i n d o w s 服 务 器 的 前 提 下 , 可 利 用 : : DATA加以限制,结合服务端为Windows服务器的前提下,可利用::DATA加以限制,结合服务端为Windows服务器的前提下,可利用::DATA进行绕过
上传eval.php,burp抓包,在eval.php后添加::$DATA后放行数据包。
Pass09 — 点空格点绕过(Windows环境下)
分析源码,从第5行开始,各行代码的意思分别为:黑名单、提取文件名、删除文件名末尾的点、找出文件后缀、将文件后缀替换为小写、去除文件后缀中的::$DATA、首位去空。经过一系列处理后,文件后缀不在黑名单列表内便进行拼接(15行)
对此,我们可以采用"eval.php. ."来进行绕过,将payload带入处理程序进行执行一次:
> 6:$file_name=eval.php. ; //删去末尾空格
> 7:$file_name=eval; //提取文件名字
> 8:$file_ext=php. ; //提取后缀
> 9:$file_ext=php. ; //将后缀转换为小写
> 10:$file_ext=php. ; //去除后缀中的::$DATA
> 11:$file_ext=php.; //除去后缀前后的空格
经过所有处理后,文件的后缀名变成了".php.",再进入下面的拼接处理,拼接完成后的文件为"eval.php.",再根据Windows去除点的特性,服务端收到的文件为"eval.php",成功绕过并执行。
访问上传的文件:
Pass10 — 双写后缀绕过
分析源码,发现在第八行的代码将黑名单中的后缀进行了替换,只要是黑名单中的后缀全部都会被替换为空,这样即使成功的上传了eval.php也会由于后缀被替换变为eval. 无法执行。
我们可以尝试使用后缀双写来绕过限制,即将eval.php改为eval.pphphp,处理机制会将后缀中识别出的php直接替换为空,这样剩下的ph和p自然便组合到了一起,变为eval.php.
利用burp抓包实现该过程。
成功上传后,访问上传的文件,成功解析。
Pass11 — 白名单绕过-%00截断
开始本关之前,先来了解一下什么是00截断:
事实上0x00,%00这两类截断都是属于同种原理,%00在url解码后为空字符,0X00即16进制的00,只是表示和用法不同而已
无论0x00还是%00,最终被解析后都是一个东西:chr(0)
chr()是一个函数,这个函数是用来返回参数所对应的字符的,也就是说,参数是一个ASCII码,返回的值是一个字符,类型为string。
那么chr(0)就很好理解了,对照ASCII码表可以知道,ASCII码为0-127的数字,每个数字对应一个字符,而0对应的就是NUT字符(NULL),也就是空字符,而截断的关键就是这个空字符,当一个字符串中存在空字符的时候,在被解析的时候会导致空字符后面的字符被丢弃。
举例说明:
当我们在GET场景上传一个名为eval.php%00.jpg的文件,后台程序收到此文件后,开始检测其名称,当检测到%00后产生截断,不再继续解析%00之后的内容。于是,系统上存储的实际为eval.php。
如何利用%00以及0x00截断:
1.php版本要小于5.3.4,5.3.4及以上已经修复该问题
2.magic_quotes_gpc需要为OFF状态
当打开magic_quotes_gpc时,所有的 '(单引号),"(双引号),(反斜线)和 NULL字符(%00)都会被自动加上一个反斜杠进行转义。
3.文件路径可控(即本关卡),比如我可以修改路径拼接的path时,比如抓到的包中存在path: uploads/,就可以直接把路径构造成uploads/xxx.php%00,先构造一个存在截断字符的后缀“等着”真正的文件名,或者后缀名,因为不管它是啥,都会被截断而丢弃
一般来说,在GET传参时,由于url中的内容会进行url编码,而%00在经过解码之后便是空字符导致截断。
而在在POST传参时, POST 中 %00 不会被 url 解码,所以只能通过 burpsuite 修改 hex 值为 00 进行截断,这种情况会在接下来的Pass12进行举例。
了解了两种截断之后,我们来开始本关的实验。
分析源码,第五行定义了允许上传的白名单,第五行取出上传的文件的后缀,如果后缀在白名单范围内便将图片存储在客户端指定的save_path下,文件名为10-99的两位随机数+日期.后缀
源码中出现了客户端可控的save_path和拼接上传的文件,传输方式为GET,我们考虑用%00进行截断。
将phpstudy中的apache版本切换至5.2.17,将magic_quotes_gpc切换至OFF状态。
尝试上传eval.php,burp抓包修改数据。将GET请求中的/upload/路径构造为/upload/eval.php%00进行截断等待处理过的数据进行拼接。
放行数据包,显示上传成功,尝试访问上传的文件,发现报错。
查看文件地址,发现截断位置之后为实际的经过处理之后的文件,但因为截断之后的数据并不会被存储。所以这里应当直接访问截断之前的/upload/eval.php。
Pass12 — 白名单绕过(0X00截断)
分析源码,与Pass11不同的是,本关的传参方式变为了POST,上传路径依然可控,所以考虑0X00截断绕过。
尝试上传eval.php,通过burp抓包修改数据。在修改数据的时候有两种修改方法。
1.通过16进制hex进行修改:
在上传路径/upload/后构造 “/upload/eval.php空格” 注意,这个空格只是为了方便寻找插入0X00的阶段位置,使用其他任何字符亦可,只要自己知道对应的16进制编码即可。
点击Hex,找到对应的空格对应的十六进制数据0x20,将其修改为00
修改完成后,然会Raw下查看,eval.php后的空格已经变为空字符。
放行数据包,显示上传成功,访问方法同Pass11.
2.通过url解码:
同样的利用burp抓包,在/upload/后构造/upload/eval.php%00,选中%00后,右键选择convert selection-URL-URL-decode
将%00进行url解码后即变成了空字符,可以产生截断。其原理便是在POST传参中并不会自动进行url解码,我们将%00手动解码后便可产生截断。
放行数据包后文件成功上传,访问方法同Pass11。
Pass13 — 图片一句话木马
从本关开始,在检测程序中都有对应的函数来检测上传的文件是否为一个真实的图片文件,且需要利用文件包含漏洞或者文件解析漏洞来使上传的文件进行解析。分析源码:
本关中定义了一个检测文件真实类型的功能,通过读取文件开头的两个字节来判断文件的真实类型(一般的文件前两个字节信息都是表明自己文件类型),所以之前直接修改文件后缀以及修改Content-Type的方法便不能生效了。
在此,引进图片一句话木马这个概念:
图片一句话木马即携带一句话木马的图片,外表上是一个正常的图片,但图片的编码中携带了一句话木马,当存在含文件包含或者文件解析漏洞的时候,便可以将图片解析为可执行文件,执行其中的一句话木马。
图片一句话木马的制作:
copy a.jpg/b+ b.php c.jpg
/b,即二进制,该命令表示以二进制格式合并a.jpg和b.php。其中a.jpg为正常图片,b.php中写的一句话木马,c.jpg为生成的图片一句话木马
直接上传图片一句话木马,上传后访问该图片。
在正常情况下,图片并不会被解析为可执行文件,所以我们需要通过文件包含或者文件解析漏洞来实现对图片木马的利用。但靶机总并没有提供文件包含的相关文件,所以我们自己来写一个简单的文件包含文件。
注:文件包含环境需要PHP5.3以上,不然运行文件包含漏洞会出错。
恢复PHP版本为5.4.45,在靶机的/upload/目录下新建一个include.php,代码如下:
<?php
$file = $_GET[ 'file' ] ;
include ($file);
?>
访问include.php,并用GET方式传递file=xxxx.jpg。这个xxxx.jpg便是正常上传的图片木马的地址。
可以看到访问成功,页面上方的乱码为图片的编码,一直执行到图片编码末尾构造的代码才显示出phpinfo界面
Pass14 — getimagesize()
getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。
如果不是真实的图像,则会获取信息失败,所以要绕过该检测方式也需要用到图片一句话木马。方法同Pass13,需要配合文件包含漏洞或文件解析漏洞。
Pass15 — exif_imagetype()
exif_imagetype — 用于判断一个图像的类型,读取一个图像的第一个字节并检查其签名。如果发现了恰当的签名则返回一个对应的常量,否则返回 FALSE。
如果不是真实的图像,则会获取信息失败,所以要绕过该检测方式也需要用到图片一句话木马。方法同Pass13,需要配合文件包含漏洞或文件解析漏洞。
值得注意的是本关卡需要开启php_exif拓展,否则上传后页面会出错。
Pass16 — 二次渲染
后面的这机关就不分析代码了,确实太长,没学过PHP也看不懂,菜。
总的来说,本关中采取了如下几种限制:
1.文件后缀名
2.Content-Type
3.使用函数imagecreatefromgif、imagecreatefromjpeg、imagecreatefrompng来判断是否为gif、jpg、png图片
4.对于判断是图片的文件进行二次渲染
二次渲染,就是根据用户上传的图片,新生成一个图片,将原始图片删除,将新图片添加到数据库中。比如一些网站根据用户上传的头像生成大中小不同尺寸的图像。在二次渲染的过程中,我们图片中所写的木马也可能会被渲染掉。
绕过二次渲染的核心思想便是先上传一张图片,再将上传完成后的图片下载下来,对比渲染前后图片的编码变化,将我们的代码写在未被渲染的区域便可绕过二次渲染。
由于不同格式文件的特性,在进行绕过二次渲染时,选用gif的图像最容易成功,对于jpg和png这两种图片需要用脚本进行改写,也不在进行尝试。
以gif为例:
用winhex在eval.gif的最后写上phpinfo的代码
上传eval.gif后,可以正常访问gif。
尝试用文件包含漏洞来利用图片木马,失败。
将渲染后的gif文件下载到本地,通过对比软件对比渲染前后的十六进制数据。下图左为渲染前,右为选然后的文件。未标红的即为前后未发生变化的区域。
将木马写到该区域再次进行尝试上传并利用文件包含访问。
成功解析。
Pass17 — 条件竞争
分析源码,对于上传的文件处理是先保存到服务器,再对比拓展名,如果拓展名不属于白名单中的三种后缀,便由18行的操作执行unlink操作将保存的文件删除。
对此,我们可以运用条件竞争机制在程序执行删除之前访问上传的程序。这有什么用呢?对于我之前写的phpinfo()这样近乎无害的代码的确没什么用,如果换成下面的代码呢:
<?php fputs(fopen('info.php','w'),'<?php phpinfo();?>');?>
- 1
该代码的意思为,新建一个info.php,并向其中写入"<?php phpinfo();?>",这样一旦成功访问到了上传后的文件,便会生成一个新的info.php留在服务器,即使原来的文件被删除后我们依然可以访问新生成的文件。
接下来进行操作。
上传eval.php,用burp截取数据,将数据包发送到intruder。
在payload中设置payload为Null payloads,即没有payload,并将continue indefinitely,意为无限重复。设置好后 start attack ,这样burp会代替我去一直重复上传eval.php
burp开始发包后,另一边打开浏览器不停刷新尝试访问eval.php
好吧,手点麻了也没有成功访问到eval.php,低估了程序的处理速度。借鉴了其他师傅的方法,可以借助python自动访问eval.php,当成功访问到后再停止。
import requests
url = "http://192.168.159.128/upload-labs-master/upload/eval.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
返回OK,说明访问到了eval.php,对应的info.php也应该生成了,尝试访问info.php,成功。
Pass18 — 图片木马条件竞争
从做题的角度来看:
源码太长就不放了,源码中定义了一堆允许上传的文件白名单,当上传一个新文件上去后,先是将文件后缀跟白名单做了对比,然后检查了文件大小以及文件是否已经存在。文件上传之后又对其进行了重命名。
因此,只能利用图片木马结合文件包含,并且要在图片马违背删除之前访问到它,生成新的info.php,这跟Pass18变化不大,还是用burp重放上传图片马的包,再利用python访问文件包含图片马的链接,生成新的info.php。不再演示。
从过关的角度来看:
上传一个图片马,会返回重命名的地址,就按照我们之前的访问方式(Pass01)去访问利用图片即可。
Pass19 — %00截断,参考Pass11