0×00 漏洞描述
MyBB是国际上非常优秀的免费论坛软件,最大的特色是简单但是功能却出奇的强大,支持多国语言,可以分别设置前台后台的语言。
MyBB作为一个开源项目,拥有活跃的社区环境,项目的管理,发展由社区志愿者支持。用户量广泛,在github上的项目拥有609 个star,79个releases。
MyBB <= 1.8.20存在一处从存储的XSS到RCE组合利用漏洞,攻击者可以先通过xss获得管理员权限,再通过rce达到远程代码执行。这套利用流程不仅隐蔽而且利用难度低,只要私信给mybb管理员发出一条包含payload的消息即可。
该漏洞由RIPS 团队安全研究人员Simon Scannell发现并纰漏。但截止目前,并未公布利用poc
我们试图从Simon Scannell简单的漏洞披露中深入分析该漏洞,并尝试还原出poc
首先分析下mybb xss漏洞
0×01 XSS漏洞
此漏洞为mybb对BBCode的错误解析而造成的
首先了解下BBCode:
BCode是Bulletin Board Code的缩写,属于轻量标记语言(Lightweight Markup Language)的一种,如字面上所表示的,它主要是使用在BBS、论坛、Blog等网络应用上。BBcode的语法通常为 [标记] 这种形式,即语法左右用两个中括号包围,以作为与正常文字间的区别。系统解译时遇上中括号便知道该处是BBcode,会在解译结果输出到用户端时转换成最为通用的HTML语法。
下图举个简单的例子:
在BBcode中给关键词加链接可以用下面的代码:
[url=https://www.xiongbenxiongbenxiong.com/1.html]熊本熊本熊[/url]
然而,BBcode只是一种标记语言,并没有一个共同的标准。各个BBS、论坛、Blog等网络应用程序可能会有自己独创的BBcode。在实际场景中,不同的程序会在后台使用不同的方式将BBcode解析成html来进行页面渲染
接下来看下mybb程序对BBCode的解析流程
举一个例子,使用BBcode格式分别插入的一条视频链接与一条url链接
Mybb程序会对BBcode进行解析,以便转化为浏览器可识别的html语言
Mybb首先会对video链接进行解析(注意:这与video和url的排列顺序无关,在程序的后台,会首先处理video,后处理url)
经过video解析器解析后的格式如下
接着mybb对BBCode格式的url进行解析,经过url解析器解析后的格式如下
此时,mybb以将BBcode语言解析成为html语言,浏览器渲染页面,显示嵌入的video与超链接中的url
如果,我们将两个BBCode格式的语句进行嵌套呢?
像如下图这样的形式
按照我们的推测,会进行如下的解析:
首先,解析video
解析之后的格式如上图
然后,解析url,也就是下图红框处的结构
解析之后的结果如下图
注意上图红框这里,src中的值,被href引入的双引号闭合,而onmusemove成功逃逸出来,意味着alert会在鼠标移动时,alert将会被执行
事实上,以上的流程,在mybb中是否成立。正式因为上文猜测中的嵌套解析,造成了此次xss漏洞
接下来跟下代码
首先,我们给管理员(admin)账号发一条如下图bbcode语言的私信
这条私信会原封不动的被写入数据库中
当管理员查看私信时,这条message会被从数据库中取出,并解析为html语言进行渲染
程序调用private.php执行上述操作,跟进private.php
在997行处,程序将留言从数据库中取出,并赋值$pm
在11789行处,将$pm传入build_postbit方法
跟入build_postbit方法
在该方法774行处,将message传入parse_message,这里的message即为我们构造的BBCode语句
跟入parse_message方法
在parse_message方法中201行处,调用parse_mycode对$message进行处理
跟入parse_mycod方法
如上图,第一个红框处,会对bbcode中的video结构进行解析,而第二个红框处,会对url,email等结构进行解析。正因为这里的解析的先后顺序,决定了构造poc时的嵌套顺序
首先来看video解析
这里使用了preg_replace_callback,对匹配到的内容使用mycode_parse_video_callback回调进行替换
跟入mycode_parse_video_callback
可见mycode_parse_video_callback将$matches[1], $matches[2]传入mycode_parse_video方法
跟入mycode_parse_video方法
如下图,首先使用parse_url对传入的url($matches[2])进行解析
解析后的parsed_url值如下图
接着,将$parsed_url['fragment']赋值与$ fragment
随后,进入switch语句,根据不同视频来源,分情况解析。我们这里构造的是youtbe来源,进入该分支
在下图中,将$ fragment中的!v=置空,赋值与$id
此时id值为test[url=onmousemove='alert(1337)']
随后,由于担心xss攻击,程序在这里对$id进行html特殊字符转义处理,如下图
接着,从templates中取出对应video的模板,如下图$templates get方法
根据get方法,看下取出模板的形式
上图为最终生成html的模板,此处的$id即为上文中case分支中赋值的$id
最终,将$id值填充的模板中,结果如下图
回到preg_replace_callback处,$message替换后的结果如下图
此时$message=<iframe width=”560″ height=”315″ src=”//www.youtube.com/embed/test[url=onmousemove='alert(1337)']” frameborder=”0″ allowfullscreen></iframe>[/url]
Video的解析到此结束了,此时html语句,是不是和上文流程中猜测的形式一样呢?
接下来,仍然使用preg_replace_callback对url进行替换
如下图可见,使用#\[url=((?!javascript)[a-z]+?://)([^
"<]+?)\](.+?)\[/url\]#si正则进行匹配,使用
mycode_parse_url_callback1回调对$message进行替换
此处的处理与上文中的video极其相似,这里就不重复说明了
在url解析结束后,messgae值如下图
可见,如上文流程分析一致,href中引入的双引号,将src闭合了,onmousemove得以逃逸
最终,xss产生
利用给管理员私信的这个功能,通过xss漏洞,可以获取管理员cookie,从而获得后台权限
接下来分析下rce漏洞
0×02 RCE漏洞
Mybb后台提供导入theme功能
可以选择本地theme进行导入,上传的theme格式许为xml格式,形式如下
这里的导入的xml文件存储了该theme下所有包含的css文件,下面展示其中两个片段
简单来说,相当于是把这个theme里包含的所有css都存储到xml一个结构里
在上传xml格式的theme时,程序后台会解析该xml,如下图代码
随后,将解析的xml树中的内容,例如css名称,内容、id等写入数据库
上图即为解析上传的xml格式的theme,将解析出的css的信息入库的操作
入库后,数据库中内容如上图
在解析xml内的css数据的name时,后台程序会校验css的后缀是否合法
可见,程序要求xml结构中,css文件后缀应为.css。但其校验方式仅仅检测css名称后四位是否为.css
当我们构造css数据时,可以使得css文件名为aaaaaaaaaaaaaaaaaaaaaaaaaa.php.css,在css文件内容处插入payload,如下图
在xml解析后,$stylesheet['attributes']['name']的值为aaaaaaaaaaaaaaaaaaaaaaaaaa.php.css,成功绕过检测
当进行入库操作时,如下图代码
此时将要插入表中name字段的值为aaaaaaaaaaaaaaaaaaaaaaaaaa.php.css,由于mysql默认字段长度为30字符,而我们的name长度为34字符,所以,.css会被截断,最终插入表中name字段的值为aaaaaaaaaaaaaaaaaaaaaaaaaa.php。
与此同时,aaaaaaaaaaaaaaaaaaaaaaaaaa.php.css中的内容,会插入到stylesheet字段中,我们构造的payload随同aaaaaaaaaaaaaaaaaaaaaaaaaa.php.css中的内容,一同写入数据库。
程序在执行xml数据从解析到入库的操作的同时,也会在本地生成对应的文件,如下图
这里生成文件名仍是.css后缀。原因很好理解:这里生成的文件名由解析上传的xml文件中的css name值决定的,如下图
因此后缀为.css
接下来在后台页面中,查看我们刚刚导入的theme
可以看到我们grq themes里的css列表中,后缀已经变为php
这是为什么呢?原因很简单,因为此处展示的内容,是从数据库中查询得到,此时数据库内容如下图
数据库由于30字符最大长度的截断,已经把入库的文件名后的.css截断了,此时数据库中name的后缀变为php
这时,点击Stylesheets in grq 列表中的aaaaaaaaaaaaaaaaaaaaaaaaaa.php,进入编辑页面
再次查看后台文件夹,可见生成了一个.php后缀文件,如下图
这里的原理也很简单。点击编辑css文件时,程序会从数据库中读取该文件的name以及内容,
并判断在指定目录中是否存在,若不存在,则用数据库中取出的name为文件名,生成文件,并将数据库中取出的内容写入该文件中
由于此时库中的name为aaaaaaaaaaaaaaaaaaaaaaaaaa.php,所以在指定位置生成了aaaaaaaaaaaaaaaaaaaaaaaaaa.php文件
下面跟下php文件生成的流程与代码
在点击aaaaaaaaaaaaaaaaaaaaaaaaaa.php,进入其编辑页面这个操作
此时发送请求如下
后台代码执行如下:
首先进入
\admin\modules\style\themes.php
此时的action为edit_stylesheet,进入上图分支
接着从数据库中查询该条数据,执行的sql语句如下
查询结果如上图
最后将库中内容写入文件,文件名即为库中name字段,文件内容即为库中stylesheet字段
如上图,构造的payload被写入aaaaaaaaaaaaaaaaaaaaaaaaaa.php
打开aaaaaaaaaaaaaaaaaaaaaaaaaa.php,可见payload成功写入
访问该文件
Phpinfo执行成功
0×03 漏洞分析时发现的问题
1. sql_mode默认值问题
笔者在尝试构造与复现rce漏洞时,当构造34字符长度文件名,以便通过mysql最大长度截断机制截断时,出现的如下问题:
Data too long~
笔者使用的是Wamp环境,在查看安装的mysql配置时,发现默认sql_mode为STRICT_ALL_TABLES,如下图:
STRICT_ALL_TABLES:
对所有引擎的表都启用严格模式。(STRICT_TRANS_TABLES只对支持事务的表启用严格模式)。
在严格模式下,一旦任何操作的数据产生问题,都会终止当前的操作。对于启用STRICT_ALL_TABLES选项的非事务引擎来说,这时数据可能停留在一个未知的状态。这可能不是所有非事务引擎愿意看到的一种情况,因此需要非常小心这个选项可能带来的潜在影响。
在严格模式下,超长的字符串,不能被截断入库,因此这里可能会影响到该漏洞的利用
2.urldecode的必要性
在xss漏洞中,漏洞作者原文有一段描述如下
漏洞作者在他的文章中这样描述
“通常,由于正则表达式过滤器可以防止此类攻击,因此无法在其他bbcodes中注入bbcodes。但是,对于呈现 bbcodes的回调方法会在应嵌入的视频的URL上调用urldecode()。视频URL被urldecoded,这将允许绕过正则表达式保护并通过URL编码注入。”
从作者的描述来看,正是因为此处的urldecode存在,导致被url编码后的payload可以绕过正则防护,从而造成注入。根据作者描述,构建的流程如下:
1.Payload->正则过滤器->检测不通过->失败
2.url编码后的payload->正则过滤器->检测通过->urldecode->还原为明文payload->注入成功
但是在我实际分析中,发现根本不需要对payload进行URL编码,正如上文我构造的pyaload,无需编码仍可触发漏洞。而且,也没有在urldecode执行前,发现正则过滤器
3.RCE漏洞的入口
关于RCE漏洞的入口,作者原文描述如下:
在此处,作者多次提到create与choose the filename字眼,一度让我认为漏洞存在于创建与修改Theme处,如下图
或是修改Template Name处,如下图
在尝试寻找rce入口时,发现一处有意思的地方,当修改templates为aaaaaaaaaaaaaaaaaaaaaaaaaa.php.css时
保存后,程序会将.css截断,并提示后缀问题,如下图
在跟踪此处代码时发现,在修改template name时,程序是会检查文件名长度并截断的,控制template name长度为30字符,如下图
这里可以印证,漏洞入口并不在修改名字和创建template处,经过后来的分析与寻找,才找到上文中真正的入口点,也就是Import a Theme处。但是从这个过程可见,mybb对文件名长度还是有一定限制的,只是防护的有疏漏,忘记检查导入的xml结构中的name字段长度。
0×04 漏洞修复
xss漏洞
针对xss漏洞,使用encode_url方法替换原来的htmlspecialchars_uni,如下图
回顾上文,在漏洞利用中,id中的内容为嵌套的url bbcod代码
当id被url编码后,之后在通过正则解析url bbcod代码时,该处被编码后的内容就不会被正则匹配到,从而防止嵌套解析造成xss漏洞
RCE漏洞
针对rce漏洞,使用my_substr方法限制name长度为30字符,如下图