禅道小范围版本通杀代码审计
前段时间朋友打了一个站,发现资产包含禅道15.x版本,朋友说随后发给我了一份分析文章,但是并没有poc,所以着手代码审计一下。
一开始我基本上没看过禅道的源码,首先我看别人文章说的15-18有不同利用链的越权和rce,当前版本是15.0.rc2,rce估计都是在登录后才能操作功能点,所以先复现网上说的权限绕过漏洞。
下载15.0.rc2源码,然后在本地打开,老样子,直接debug调试。首先在这个源码中,有部分方法可以在用户未登录的情况下进行调用,允许未授权调用的方法在isOpenMethod() 函数中,先全局搜索下isOpenMethod() 函数。
首先看到这个函数带有判断,未登录用户能访问xxx接口,登录用户且登录账户不为guest能访问xxx接口。那我们全局搜索下isLogon函数。
这个代码的意思是,返回当前登录session的用户并且用户名不为guest。
那么好,我如果想访问剩下的接口,如何让这个函数成功调用,可以想到session覆盖这个东西,我可以调用一个能够覆盖session的方法,让session有user这个key,这个key也可以为空,只要不等于guest就可以。
由于这个程序session设置的写法为$this->session->set(),所以全局搜索带有$this->session->set并且能够可控变量的方法。
看着有好几个,找了这么多,看到了captcha方法可以控制session的key。
那我们看看怎么引用,直接构造url。
禅道的get路由构造是这样的:
index.php?m=misc&f=captcha&sessionVar=user
对应以下路径。
其实在很多情况下会遇到很多静态路由,禅道设置伪静态后会启用静态路由,静态路由的构造和get的不一样,去掉了key并且用横杠连接。
例如: index.php/misc-captcha-user.json
后缀为json的会输出json格式,后缀为html的话会输出html格式。
路由格式在程序中的助手函数中的createLink方法写的很清晰。
访问路由,会出现验证码,有的可能不会,但是不影响覆盖session。
接着我们访问那些需要登录的接口,就访问tutorial模块的wizard方法,这个模块是禅道的新手教程模块,正常来说访问这个接口会跳转到登录页。但是现在访问会空白,说明成功覆盖session。
那好既然成功绕过权限了,但是我并不想着急找rce漏洞,我想看看能不能访问用户列表,看用户列表中能不能查看用户密码。
用户模块里可能会泄露一些用户帐密的信息。todo方法允许将用户查出来,然后直接将对象渲染到页面,如果直接访问该方法,会让重新登陆。
todo方法最后以$this->display();输出到浏览器上,进一步看下这个方法。
继续看parse方法。
如果viewType == 'json',则渲染json格式。不是的话默认渲染方法,适用于viewType = html的时候。
getViewType这个方法的主要功能是确定当前请求应该使用哪种视图类型,这个就不带单独分析了。
禅道的新手教程模块路由允许fetch一部分模块的方法,就是当跳板,那么我们可以通过这个wizard调到todo方法,从而实现权限绕过访问用户数据。那我们构造可访问的url:index.php?m=tutorial&f=wizard&module=user&method=todo¶ms=dXNlcklEPTI=&t=json 其中dXNlcklEPTI=为base64加密,因为todo方法中params传入的是一个base64字符串,解密字符串为userID=2,直接在浏览器中访问。
成功输出MD5password,这个password可以直接登录,抓包替换就行,原因是因为先前段MD5加密密码然后再请求后端,后端检测到密码长度为32位,直接和数据库进行对比登录。
成功获取到密码,那么就能直接登录后台了。
问题来了,我看网上公开的poc都不能用,疑似版本太低缺少高版本可利用的功能漏洞点,看网上有说文件包含的,简单看了下没找到直接利用的poc,主要是也想自己审计审计。全局搜索php相关文件包含函数。
经过漫长的寻找,找到了一个可控变量的方法,里面有include方法。
是一个助手函数,helper::import()看看有没有被引用。
也是经过漫长的寻找,找到了api下的getMethod方法,参数可控,继续看看有没有被引用。
找到了,api下的dedug方法,传入的是base64加密的文件,先看代码。
传入$filePath和extendControl跳到getMethod方法,进入此方法。
先获取$filePath的文件夹,如果传入的是xx/xxx/xxxx/1.php,则输出xx/xxx/xxxx,先debug一下看看是不是这样。debug方法打断点,浏览器访问debug方法接口。
用之前获取的密码登录,然后访问url http://localhost/www/index.php?m=api&f=debug&filePath=RDpccGhwc3R1ZHlfcHJvXFdXV1xtb2R1bGVccGhwaW5mby5waHA=&action=extendControl 其中RDpccGhwc3R1ZHlfcHJvXFdXV1xtb2R1bGVccGhwaW5mby5waHA=为D:\phpstudy_pro\WWW\module\phpinfo.php
先base64解码,然后因为action=extendControl进入getMethod方法。
$fileName先获取当前文件的文件夹,$className获取当前文件的文件夹的上一层的名称,即为www,下一句代码是一个条件判断,用于检查指定的类www是否存在,如果不存在则尝试进入helper::import方法。
进入到此方法,$file为文件夹绝对路径,但是传入的是文件夹不是文件,导致下一步返回false,退出程序,导致不能文件包含,那怎么办?
将之前的D:\phpstudy_pro\WWW\module\phpinfo.php改成D:\phpstudy_pro\WWW\module\phpinfo.php\123,那么getMethod中$fileName会读取成D:\phpstudy_pro\WWW\module\phpinfo.php,传入到helper::import的$file就是D:\phpstudy_pro\WWW\module\phpinfo.php,phpinfo.php会被判断成文件,然后进行文件包含。
现在$filename变成了D:\phpstudy_pro\WWW\module\phpinfo.php
直接包含成功。那么就该找上传点了,上传任意文件,将命令写入文件上传。
先看头像上传功能。
上传带有php代码的图片。
上传完剪切直接点x关闭,要不然会导致php代码删减。
f12找到该图片路径,但是不是绝对路径,我们再看看程序中有没有泄露绝对路径的功能点。
发现后台中的这两个功能点存在绝对路径泄露。直接拼接刚刚找到的图片路径。
D:/phpstudy_pro/WWW/www/data/upload/1/202504/09181014014632h8/任意名称
可以看到已经出现phpinfo的内容了,放到浏览器再访问下。
http://localhost/www/index.php?m=api&f=debug&filePath=RDovcGhwc3R1ZHlfcHJvL1dXVy93d3cvZGF0YS91cGxvYWQvMS8yMDI1MDQvMDkxODEwMTQwMTQ2MzJoOC8xMQ==&action=extendModel
成功RCE,后续抽空再看看其他功能点和和高版本。
评论