zentao 审计思考之定位漏洞
前言
前几天禅道出了一个全版本的 rce,今天闲下来稍微看了一遍,实际上,从漏洞发现到找到利用链,从我的角度出发,这应该算是一个定位漏洞函数,然后回溯构造利用链的过程。
参考:http://foreversong.cn/archives/1410
思考
先不谈漏洞,从路由出发,大致了解一下框架的基本路由。
这里的 requestFix 是-
,模块名和方法名是根据 url 中的moduleName-methodName
确定,模块名和方法名都经过正则的过滤,防止模块名通过../
目录跳转。
1 | public static function checkCode($var) |
setControlFile() 找到控制器文件。
1 | public function setControlFile($exitIfNone = true) |
最终调用的是/module/[moduleName]/control.php
中定义的类的 methodName 方法。
接着,通过反射取到我们传入的 moduleName 和 methodName 指定的方法的默认参数值,没有默认值的用_NOT_SET
占位。
接着从 url 中取实际参数值。
1 | if($this->config->requestType != 'GET') |
跟进 setParamsByPathInfo()。
这里的分割符 requestFix 和前面一样是-
,从 url 中取出参数值后,和默认值进行 merge。所以,实际的路由为/moduleName-methodName-参数1-参数2...
。最终,会调用 module 目录下指定模块文件夹的 control.php 文件中定义的类的方法。当然,根据不同的角色,是有权限控制的。
1 | // index.php line 67 |
下面,从原作者角度出发,原作者本身一开始就是想找 rce 的点,自然我们可以通过正则定位一下危险函数,比如 eval()、system() 等。而大部分面向对象的框架、cms,我们也可以定位一下 call_user_func()、call_user_func_array(),如果传入其中的参数可控,我们可以实现调用任意类的任意方法。
定位到 /module/api/control.php 的 getModel() 方法。
这个方法是在 module 目录下的 api 模块中,我们是可以直接路由到的,且参数也可控,
比如,我们访问/api-getModel-moduleName-methodName-test1=llfam,test2=ll
。
可以看到,进入 call_user_func_array() 函数的所有参数都可控,但是具体调用哪个类是由 loadModel() 方法决定,传入该方法的 moduleName 参数我们可控。
跟进 loadModel()。
这里有两个分支,如果在 loadedModels 变量中找到了[moduleName]Model
类,则返回该类,如果没找到,就调用 setModelFile() 根据 moduleName(可控)去 module 目录下找指定模块的 model.php 中定义的类,实例化后返回。
总结
整理一下审计的思路,跟一遍正常请求,熟悉基本路由,定位危险函数 call_user_func_array(),找到漏洞点 getModel(),因为这是一个路由可以定位到方法,所以参数完全可控,接着就是去构造利用链,关于具体利用方式,可以参考开头的文章,我就不多做分析了。
其实,我个人觉得这个洞并不是很难发现,因为以前没有审过禅道,一开始以为这个 getModel() 接口是新版本的新功能。但是,我发现在 11.4.1 中就已经存在这个问题了。其实,不光是前面说的 rce 相关的,还有写文件、包含、读文件等函数都可以去定位一遍,根据经验和回溯或许就能发现一些问题。