我最为熟悉的便是Apache了,先来研究它的文件解析漏洞。百度许久,又谷歌一番,最终发觉,Apache关于文件解析,似乎只有三种“漏洞”。之所以打引号是因为我觉得这三种“漏洞”都不是Apache的漏洞,只是其特性,而很多程序员不了解这种特性,故而写出有问题的代码,这才给黑客可趁之机,造成漏洞。但大家都称呼这是Apache的文件解析漏洞,我也只好随大流了。
1.多后缀名
先说第一种特性:多后缀名。这是怎么的一种鲜为人知的特性呢?原来是这样的,Apache认为,一个文件可以有多个后缀,如:werner.txt.png.mp3。这一文件,放在Windows里,毫无疑问,就是个mp3文件,Windows只认最后一个“.”及其后面的字符“mp3”,觉得该文件后缀为“.mp3”,这也是大多数操作系统、应用软件的处理方式、是正常人习惯。而在Apache中,则可能有所不同,如果有必要,Apache会从后(右)往前(左),一一辨别后缀。何时有必要?当Apache不认识某个后缀时,便有必要。如某文件名为:werner.mp3.html.qwe.arex,Apache在处理时,先读取最后一个后缀,为“.arex”,一看,这啥玩意啊,不认识,继续读取下一个后缀“.qwe”,一看,呀,这又是啥,还是不认识,继续读下一个后缀“.html”,一看,哦,这是个超文本标记语言文件,俗称网页文件,这回认识了,也就不继续读下一个后缀了。若是所有后缀都看完了没有一个认识怎么办?此时就会把该文件当做默认类型进行处理了,一般来说,默认类型是text/plain。据说在Apache的配置文件中搜索“DefaultType”就能看到默认类型的明确定义了,但我却不知为何,没有找到。
哪些后缀Apache认识,哪些不认识?有一个名为mime.types的文件,其中记录着Apache认识的后缀。在Ubuntu下,该文件位于/etc/mime.types,在Windows下,该文件位于C:/apache/conf/mime.types(类似这样的,注意Apache的安装路径)。该文件是一个一对多的映射表,定义了某一种文件类型,对应的几种后缀。除了该文件,在Apache的配置文件中,还可以用AddCharset语句添加映射,如:
AddCharset us-ascii .ascii .us-ascii
AddCharset ISO-2022-CN .iso2022-cn .cis
mime.types是个很长的文件,大概看了下,Apache认识的后缀比我多多了。节选部分如下所示:
application/java-archive jar application/m3g m3g application/java-vm class application/javascript js application/json json text/html html htm shtml text/x-diff diff patch video/x-flv flv video/x-la-asf lsf lsx video/x-mng mng video/x-ms-asf asf asx video/x-ms-wm wm
这一特性会带来什么问题呢?网站往往有上传文件的功能,但一定不想让用户上传程序,因为这很可能会危害网站安全,故而会检查上传文件的后缀名,若是.php,则拒绝上传(假设这是个php站)。此时用户只需上传文件evildoer.php.qwe,若是程序员不了解Apache的这一特性,编写的程序检查后缀时只看“.qwe”,而认为这不是程序文件,允许上传,则用户成功地绕过了上传时的安全检查,上传了php程序文件。该文件的最后一个后缀“.qwe”是Apache不认识的,故而Apache会以倒数第二个后缀“.php”为准,把该文件当做是php文件,解析执行。
这总是奏效的吗?按理来说,由于这是特性而不是漏洞,所以适用于所有版本的Apache。这一奇怪的特性,说不定正是Apache的自豪之处呢。但是,在我的测试中却发现,类似aaa.php.xxx的文件并不会被作为php程序执行,而是被当成文本文件,返回给浏览器,在浏览器中可以看到php源码,而不是执行结果。测试环境是Ubuntu14.04+Apache2.4.7+php5。
这是怎么回事?难道前面几百字都是废话,说的是错的?我们来做个实验。准备一个文件,内容随意,命名为test.jpg.aaa,放置在Apache中,然后在浏览器中访问它,结果如下图所示:
可见浏览器是将该文件作为图片处理的。浏览器为何认为test.jpg.aaa是图片呢?aaa可不是图片文件的后缀。这是因为服务器的响应HTTP头中的Content-Type字段值为image/jpeg,浏览器看到image/jpeg,便知这是图片文件。这说明服务器(此处即Apache)是把test.jpg.aaa当做图片的,也说明,前面分析的Apache的多后缀处理是没有错的。
那么aaa.php.xxx为何没有被作为php代码执行呢?我猜是这样的,当然只是我的猜测,实在是找不到相关资料,只好猜了。Apache看到文件aaa.php.xxx,按照多后缀名的解析规则,认为该文件是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,内容如下:
<FilesMatch ".+\.ph(p[345]?|t|tml)$"> SetHandler application/x-httpd-php </FilesMatch> <FilesMatch ".+\.phps$"> SetHandler application/x-httpd-php-source # Deny access to raw php sources by default # To re-enable it's recommended to enable access to the files # only in specific virtual host or directory Order Deny,Allow Deny from all </FilesMatch> # Deny access to files without filename (e.g. '.php') <FilesMatch "^\.ph(p[345]?|t|tml|ps)$"> Order Deny,Allow Deny from all </FilesMatch> # Running PHP scripts in user directories is disabled by default # # To re-enable PHP in user directories comment the following lines # (from <IfModule ...> to </IfModule>.) Do NOT set it to On as it # prevents .htaccess files from disabling it. <IfModule mod_userdir.c> <Directory /home/*/public_html> php_admin_flag engine Off </Directory> </IfModule>
阅读上示配置文件可知,被当做php程序执行的文件名要符合正则表达式:”.+\.ph(p[345]?|t|tml)”符号在正则中匹配结束,故而可知php本身确实是只看最后一个后缀的。就算Apache把某文件当php程序,php自己不认它,也是无用。
进一步试验,把php5.conf文件中刚刚提到的正则表达式的“$”换成“\.”,即:
".+\.ph(p[345]?|t|tml)\."
然后重启Apache使配置文件生效,再在浏览器中访问aaa.php.xxx,这次,aaa.php.xxx果然被当做php程序执行了,在浏览器中,看到的是程序执行结果而不是源码。这也从侧面验证了,我的猜测是正确的。测试完之后,一定要记得改回去。
2.罕见后缀
计算机世界自开天辟地以来,便自由多彩。还记得mime.types文件吗?在该文件中搜索“php”这三个字母,结果如下所示:
werner@Yasser:~$ cat /etc/mime.types | grep php #application/x-httpd-php phtml pht php #application/x-httpd-php-source phps #application/x-httpd-php3 php3 #application/x-httpd-php3-preprocessed php3p #application/x-httpd-php4 php4 #application/x-httpd-php5 php5
还记得正则表达式”.+\.ph(p[345]?|t|tml)$”吗,该正则表达式匹配的不仅仅有php,还有php3、php4、php5、pht和phtml。
好吧,原来不仅php,就连phtml、pht、php3、php4和php5都是Apache和php认可的php程序的文件后缀。我原本只知道“.php”的,真是大开眼界。这就好比,不仅py是Python程序文件的后缀,还有pyc和pyo也都是。写上传过滤规则的程序员是否博学多识,也知道这些知识呢?我想,大抵是不知道的。利用这些“罕见”的后缀名,也可能绕过安全检查,干些“坏事”。
我在Ubuntu14.04+Apache2.4.7中进行测试,先准备文件text.php,其内容是经典的Hello World:
<?php echo 'HELLO WORLD'; ?>
然后在浏览器中打开它,成功显示“HELLO WORLD”。再修改该文件后缀为各种后缀,进行测试。测试结果是,以php、phtml、pht、php3、php4和php5为后缀,能成功看到“HELLO WORLD”;以phps为后缀,会报403错误,Forbidden;以php3p为后缀,会在浏览器中看到源码。
3.妙用.htaccess
.htaccess是Apache的又一特色。一般来说,配置文件的作用范围都是全局的,但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。
需要注意Apache可能有多个配置文件,后加载的配置文件会覆盖先加载的配置文件中的配置。所以在某个配置文件中将AllowOverride设置成All,若是其后加载的某个配置文件中AllowOverride的设置是None,则也是没有用的。一般来说,先加载httpd.conf,再加载conf.d/中的配置文件,最后加载sites-enabled/中的配置文件。
这意味着,.htaccess并不总是有效的。而且不幸的是,在我的测试环境中.htaccess默认无效。好吧,为了测试,我只好将它改为有效。以下讨论均在.htaccess有效的前提下进行。
.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文件。
下面是一次测试,测试前已经打开Apache对.htaccess文件的支持。在网站根目录中准备如下文件树:
│
├── htaccess_test/
│ ├── .htaccess
│ ├── shell.jpg
│ ├── type.xxx
│ └── test/
│ ├── shell.jpg
│ └── type.xxx
├── shell.jpg
└── type.xxx
其中,文件.htaccess的内容为:
AddType application/x-httpd-php xxx <FilesMatch "shell.jpg"> SetHandler application/x-httpd-php </FilesMatch> 文件shell.jpg和type.xxx的内容相同,均为: <?php echo 'HELLO WORLD'; ?> 然后在浏览器中访问各文件,结果如下表所示: 访问路径 访问结果 /type.xxx <?php echo ‘HELLO WORLD’; ?> /shell.jpg 加载失败的图片 /htaccess_test/type.xxx HELLO WORLD /htaccess_test/shell.jpg HELLO WORLD /htaccess_test/test/type.xxx HELLO WORLD /htaccess_test/test/shell.jpg HELLO WORLD
这一测试结果和预期是相符的。
但是,按照前面的猜测,就算Apache认为type.xxx是php文件,php自己不这么认为,不也是不能执行的吗?这次怎么就能执行了,好生奇怪。但不管怎么说,我们都已经知道,当.htaccess文件有效时,用.htaccess文件可以很好地设置文件后缀的解析方式。