OFCMS-SQL注入-代码审计

OFCMS-SQL注入-代码审计
Att@ckxu1. 漏洞基本信息
- 漏洞名称:OFCMS SQL注入漏洞(CVE-2019-9615)
- 影响系统 / CMS名称:OFCMS
- 影响版本范围:V1.1.3 之前
- 漏洞类型:SQL注入
- 漏洞发现日期:2019-03-06
2. 漏洞描述
OFCMS是一款基于Java技术的内容管理系统。
OFCMS 1.1.3之前版本存在后台SQL注入漏洞。攻击者可利用漏洞发起admin/system/generate/create?sql= SQL注入攻击。
3. 漏洞影响版本
V1.1.3 之前
来源:https://www.seebug.org/vuldb/ssvid-97836
4. 网络测绘语法
fofa:
body="/static/assets/js/admin.js" |
5. 环境搭建与漏洞复现过程
使用环境:
- mysql 5.6
- jdk 1.8
- tomcat 8
项目地址:https://gitee.com/oufu/ofcms/releases/tag/V1.1.3
使用IDEA打开,等待Maven构建完成,项目目录如下
数据库配置
小皮开启MySQL
创建数据库ofcms
导入数据库文件
sql文件路径:doc\sql\ofcms-v1.1.3.sql
修改数据库配置文件
数据库配置文件路径:ofcms-V1.1.3\ofcms-admin\src\main\resources\dev\conf\db-config.properties
Tomcat配置
添加本地Tomcat,部署工件
发现启动后进入安装界面(手动配置无效)
解决办法:将数据库配置文件重命名为db.properties
搭建完成
后台地址:http://localhost:8080/admin
管理员账号:admin/123456
6. SQL注入原理及代码审计分析
路由分析
路由配置文件:ofcms-V1.1.3\ofcms-admin\src\main\java\com\ofsoft\cms\core\config\JFWebConfig.java
规定了路由
访问com.ofsoft.cms.admin.controller目录下的文件,需要加上/admin路径
比如我们打开admin目录下controller层的文件
实际上触发这段代码的路由是/admin/comn/service
漏洞分析
后台中有一个添加sql的功能点
输入123,发现触发了报错
跟踪路由
/admin/system/generate/create.json?sqlid=
代码中全局搜system/generate
找到对应的代码处
ofcms-admin\src\main\java\com\ofsoft\cms\admin\controller\system\SystemGenerateController.java
调用的是其中的create方法
第47行,调用getPara方法获取sql参数,sql参数是我们传入的内容,传入内容可控,并且第48行直接执行了update更新语句
getPara跟踪
ctrl跟踪getPara,跟踪到….repository\com\jfinal\jfinal\3.2\jfinal-3.2.jar!\com\jfinal\core\Controller.class
发现getPara方法是调用的JFinal框架的api
getPara是JFinal框架提供的基础方法,主要用于从HTTP请求中提取参数值
它只是简单地获取请求参数,不会自动进行任何安全过滤或转义处理
回到漏洞所在点
跟踪update
这里的Db.update方法实际上也是JFinal框架提供的api
Db.update(String sql)用于执行UPDATE、INSERT、DELETE等非查询类型的SQL语句,返回受影响的行数
第一次跟踪
继续跟踪MAIN.update方法
继续跟踪
继续跟踪var4 = this.update(this.config, conn, sql, paras);
这就是该方法实现的底层逻辑了
第247行,使用conn.prepareStatement(sql)创建预编译语句,这里的sql就是我们传入的原始SQL字符串
第248行,调用config.dialect.fillStatement(pst, paras)填充参数,但是这里的paras是一个空参数组,因此没有参数填充,在没有参数填充的情况下,我们传入的整个sql语句直接放行(整个sql语句都可控,不使用?占位符),所以这里的预编译其实并没有起到作用
创建了预编译,但是并没有使用预编译写法
第249行,调用pst.executeUpdate()执行更新操作,这是JDBC标准方法,执行INSERT、UPDATE或DELETE语句
总结:在获取参数以及参数传递到最终执行的过程中,没有对传入的参数进行过滤,并且最后执行时预编译并没有起到作用,导致存在SQL注入
漏洞验证
因为这里执行的是Db.update(sql);更新操作
我们需要构造update语句来进行注入,并且这里使用了
rendFailedJson(ErrorCode.get(“9999”), e.getMessage())方法会将异常信息发送给前端
e.getMessage()包含了数据库返回的详细错误信息
因此我们使用报错注入
并且,在JDBC执行过程中:
- MySQL解析SQL语句
- 验证表/列是否存在
- 发现表/列不存在,立即抛出MySQLSyntaxErrorException
异常被捕获并返回给前端
因此,为了使我们的XPath报错显示(也就是成功经过SQL解析阶段,进入函数执行阶段),我们构造的payload中要使用正确的表名/列名
去数据库中随便选一个表名列名
构造payload:
update of_cms_ad set ad_id=updatexml(1,concat(0x7e,(user())),0) where ad_id=1 |
sqlmap验证
sqli.txt:
POST /admin/system/generate/create.json?sqlid= HTTP/1.1 |
sqlmap:
python sqlmap.py -r sqli.txt --level=5 --risk=3 --dbms=mysql --batch --dbs |
7. 参考资料
https://blog.csdn.net/AsagiRiAsagi/article/details/123936621
https://www.seebug.org/vuldb/ssvid-97836
https://nvd.nist.gov/vuln/detail/CVE-2019-9615