某道全版本rce漏洞分析
2019-09-11 10:05:39 Author: xz.aliyun.com(查看原文) 阅读量:165 收藏

禅道项目管理软件是一款国产的,基于LGPL协议,开源免费的项目管理软件,它集产品管理、项目管理、测试管理于一体,同时还包含了事务管理、组织管理等诸多功能,是中小型企业项目管理的首选,基于自主的PHP开发框架──ZenTaoPHP而成,第三方开发者或企业可非常方便的开发插件或者进行定制。
厂商官网:https://www.zentao.net/
而此次发现的漏洞正是ZenTaoPHP框架中的通用代码所造成的的,因此禅道几乎所有的项目都受此漏洞影响。

漏洞代码位于module/control.php中的getModule函数,这里由于禅道路由的特性,会将所有符合pathinfo格式的参数给赋值到对应变量中,因此这里的moduleName,methodName,params参数都是用户可控的。

跟进这里的函数,其中param参数经过了explode函数,又经过了strpos和parse_str函数,简单来说这里其实就是参数赋值的作用,假如params对应位置的参数为users=admin,那么经过这里的处理后,params中就会传递users变量。
接着进入到loadModel函数,这里一开始踩了一个坑点,以为appName默认为空的话,在调用其module的时候会报错,从而进入不了42行中的代码,但是在动态调试后发现尽管appName为空,但是同样可以调用所有的module,也就意味着禅道module中的所有功能的model文件我们都能够直接越权调用,这主要是因为上图第50行中使用call_user_func_array来调用model文件中的函数。

这里以通过该漏洞获取管理员密码来走下动态审计的流程,把几个比较关键的点说一下。。
poc:http://127.0.0.1/zentaopms_11.6/www/api-getModel-user-getRealNameAndEmails-users=admin
在index.php的第67行会进入到用户模块的鉴权函数,禅道对各个模块都做了权限校验,很好的防止了用户的越权操作,因此这块比较关键,如果任意用户都能够调用api下的getModel函数,那么在后面的rce操作中就表示任意用户都能够做到。

这里首先获得moduleName和methodName,首先会判断这是否是一个开放的接口,但是这个接口并不是开放接口,所以进入到hasPriv的鉴权函数。

这里测试的用户是项目经理,可以看到rights信息包含用户可调用的control函数,可以看到api下的getmodel函数是true,所以也就表示当前用户是可以调用getmodel函数的。

鉴权完毕后开始调用函数,跟进这个loadModule函数

最终进入到call_user_func_array函数,这类的module为api,methodName为getmodel,注意看这里的param其实已经定义成了符合getmodel的参数名,在这段代码之前有一个初始化的过程,会从getmodel函数中取出传参变量名,然后将url中的参数给赋值到变量名里,那么这里继续走,使用call_user_func_array来调用getModel函数

这里传参就比较明显了,利用getModel函数作为跳板,继续调用user目录下的model.php文件中的getRealNameAndEmails函数,其中params为一个数组,那么继续跟进call_user_func_array函数

跟到这里,最终会去查询admin用户的信息,返回到前台,这里使用的角色是项目经理,所以是一种越权漏洞。

回到那个鉴权的问题,怎么去判断这个getModel函数是否是任意用户都能够调用的,后来发现在数据库里其实存放了对应的用户组,以及各个组可以使用的module和method。

这里可以看到正常的用户应该在前10个组里,虽然不能完全说是任意用户可控,但是感觉在实际场景中,能够登陆的用户一般都应该在前10个组里,所以这里说是任意用户可控感觉问题也不是很大。。

下面是三个比较危害级别比较高的漏洞,这里简单介绍下审计流程。

禅道的注入在9版本当时也出现过,原理是limit后的参数采用了直接拼接,但是现在这个版本的禅道对所有的sql语句都做了转义处理,并且get参数都有严格的限制,不能出现"()"等特殊符号,所以说要想要通过get去注入获得数据,会显得非常困难,另外要提到的一点就是禅道很多参数都会有base64的解码,这其实也是一个比较不错的突破口,但是这里发现了一个更明显的sql注入漏洞。
漏洞代码位于module/api/model.php文件的sql函数

这里其实是由很多限制的,比如说只能用select语句,并且其中可能还不能出现“()”这样的符号,因此这里只能采用最古老的的select语句去读数据库信息,虽然简单但是十分有效。
漏洞url:http://127.0.0.1/zentaopms_11.6/www/api-getModel-api-sql-sql=select+account,password+from+zt_user

这里简单说下禅道目前最新版所采用的pathinfo模式,首先通过传参确定进入的control文件为api,对应的method为getModel,接着开始对参数进行赋值,其中moduleName为api,methodName=sql,最后的param为sql=select+account,password+from+zt_user,那么调用了call_user_func_array函数后,会进入到api目录下的model文件,对应调用其中的sql函数,并通过赋值,将sql变量赋值为select+account,password+from+zt_user,最后执行query语句,造成数据库泄露。

漏洞代码位于module/file/model.php中的parseCSV函数

这里通过上面的思路同样可以对fileName进行赋值,最终返回读取的文件数据
漏洞poc为:http://127.0.0.1/zentaopms_11.6/www/api-getModel-file-parseCSV-fileName=/etc/passwd

一开始想要找的就是rce漏洞,所以通过寻找rce漏洞找到了禅道框架的漏洞,也是一个比较意外的收获吧。这里想要rce其实是文件写入+文件包含组合getshell的,所以只要我们都能够在model文件中找到这两类功能函数就能够达到我们的目的,另外这里可能有人会提出疑问,都能够写文件了,难道不能直接getshell嘛?这里写入的文件名没有经过base64解密,前面也说了禅道对于GET的参数其实是有多重限制的,像“.”也明显不在白名单规则里,所以最终放弃了这条路。
首先是文件写入漏洞,漏洞代码在module/editor/model.php中的save函数

这里对文件写入内容作了一定黑名单的限制,这个绕过比较简单就不多说了,懂的人都懂,另外会对filePath做可写限制,因此这里如果是linux服务器部署的话,推荐写在/tmp目录下,毕竟没有什么限制,而且物理路径比较可控,如果想要../来跳转目录写入,这个是会失败的。

文件包含这个点是在module/api/model.php的getMethod函数

这里传入filePath,最终经过了helper::import函数,这个函数跟进就会发现其实就是import函数,那么回到上面的代码,这里filePath就是我们写入的文件名,但是需要注意的是这里包含的是filePath的dirname,也就是说直接输入文件路径,最终包含的文件路径的上一级目录名,因此这里需要加上一层路径。

最终poc如下,需要给物理路径加上一层才能包含成功。

上文都是在禅道开源版的11.6最新版本测试的,后来在开源版的9版本上测试了一把,发现禅道代码几乎没有改变,至少rce这几个接口的代码都是都没变的

虽然不是pathinfo模式,但是传参过程其实是一样的,这里也是文件写入+文件包含组合起来getshell。
后来又想看看禅道其他产品的代码怎样?这里又下了禅道专业版的最新版本发现同样的接口,所以也就会产生同样的漏洞。。最后推测禅道全版本全产品线应该都有相似的问题。。
如果想要修复这个漏洞,最简单的办法就是删除这个getModel接口。。


文章来源: http://xz.aliyun.com/t/6239
如有侵权请联系:admin#unsafe.sh