2010年冬天,析漏我在一个政府站点的析漏IIS 6.0服务器上,用一条URL把asp的析漏源码读了出来。当时还以为中了彩票,析漏后来才知道这叫IIS解析漏洞——一个让无数渗透测试新手第一次拿到webshell的析漏经典问题。
IIS解析漏洞不是析漏单个bug,更像是析漏一组因为文件名解析逻辑不一致导致的"特性"。最出名的析漏有两个:IIS 6.0的目录解析和分号解析。目录解析的析漏原理很直白,服务器拿到/test.asp/1.jpg这种路径时,析漏IIS 6.0会先把test.asp当做一个目录,析漏然后交给asp.dll去处理。析漏asp.dll一看路径里有.asp,析漏管你后面跟的析漏是jpg还是txt,统统按asp脚本执行。析漏于是你上传一个叫shell.asp的文件夹,里面放张图片马,访问/shell.asp/logo.jpg,代码就跑起来了。
分号解析更离谱。shell.asp;.jpg这种文件名,IIS 6.0只认分号前面的.asp,后面的;.jpg被直接忽略。上传的时候服务器允许jpg,执行的时候又按asp走。两个逻辑各干各的,中间的空档就是漏洞。
IIS 7.0和7.5也没能幸免,不过玩法变了。FastCGI模式下,shell.jpg/.php这种路径,PHP的FastCGI进程会把shell.jpg当成PHP文件解析。微软后来出了补丁,但配置不当的服务器照样中招。我见过一个案例,管理员打了KB补丁,结果因为php.ini里cgi.fix_pathinfo没关,漏洞依然存在。补丁不是万能药,配置才是。
为什么这些漏洞能活这么久?根子上是IIS处理请求时的"双重身份"问题。HTTP.sys负责接收请求,把URL映射到物理路径;asp.dll或php-cgi负责执行脚本。两个组件对文件名的理解不一致,一个看扩展名,一个看路径片段。这种设计在2000年代很常见,当时没人想到有人会构造/a.asp/b.jpg这种畸形路径。
防御其实不复杂。IIS 6.0时代,微软推荐用URLScan过滤带分号和特殊字符的请求。更彻底的办法是升级到IIS 7以上,开启请求筛选,拒绝包含;和.asp/的URL。PHP那边,把cgi.fix_pathinfo设为0,再配好security.limit_extensions,基本能堵死。但现实是,很多老系统跑在Windows Server 2003上,升级意味着重写代码,管理员宁愿加个WAF糊弄过去。
我去年扫过一个教育单位的站,IIS 6.0还在跑,目录解析漏洞原封不动。问他们为什么不升级,回答是"教务系统只支持IE6,升级就崩"。技术债这东西,有时候比漏洞本身更难缠。
说到底,IIS解析漏洞是特定历史阶段的产物。它教会我们一件事:安全不能依赖单个组件的"聪明",当两个系统对同一份数据有不同解释时,攻击面就出现了。现在的新框架很少犯这种错,但类似的逻辑不一致——比如Nginx的fastcgi_split_path_info配置错误——依然在制造新的解析漏洞。换汤不换药。