本文笔者根据CVE官网的一纸通告,分析了Dedecms <v5.7.89的一个未公开POC的sql注入漏洞(CVE-2022-23337),并且经过分析和测试,得出了可用POC。
0x00 漏洞概述
根据CVE官网的通告,2022年1月18日收录了Dedecms v5.7.87的一个sql注入漏洞,漏洞点存在于article_coonepage_rule.php
,影响版本为v5.7.87
。
有且仅有的一个参考链接已经失效无法访问,估计作者不再公开文章了。经过一番查找,发现网上没有任何相关的poc和文章,于是决定分析一下缘由并构造利用POC。
0x01 环境
1.1 影响版本
在官网查看更新日志,发现v5.7.88
并未有对article_coonepage_rule.php
的相关更新描述和补丁,该漏洞在v5.7.89
才修复
往前查看更新日志,直到v5.7.42
该漏洞文件都未记录有更改,可以下载旧版本的进行复现
1.2 环境搭建
https://github.com/dedecms/DedeCMS/releases
笔者下载的是V5.7.80 UTF-8正式版
,从官方更新日志上看,v5.7.88
也是可行的。
复现环境中其他组件版本:
PHP 5.6.9nts
Apache 2.4.39
MySQL 5.7.26
解压后访问 /install
文件夹按照步骤进行安装即可
后台地址:http://your-ip/DedeCMS-5.7.80/dede/
0x02 漏洞形成
2.1 漏洞点
查看 /dede/article_coonepage_rule.php
,搜索 ids
只存在$action
为del
的代码中
从以上代码上看是通过传入id
从数据库中删除对应的note
,判断$action
为del
后还有另一个动作,即通过传入的id
中是否含有逗号来判断是用户操作是单个删除还是多选删除
两种删除的sql语句不尽相同,单个删除的时候用单引号将id括起,多选的时候用括号。目前看来没有对传入的id进行处理便直接拼接,笔者尝试构造请求
直接盲注没有成功
2.2 远程Debug
DeBug看看后端代码如何执行。
- 用phpstudy和phpstorm远程调试,搭建过程不赘述了,参考网上教程。
- 在如图位置打断点,开启debug侦听,发送payload
- 首先来到正则匹配判断
可以看到单引号被addslashes
了,闭合不了
F7跟进到 \DedeSqli::ExecuteNoneQuery
这是一个不返回结果的SQL语句,所以联合注入也不会回显结果
继续跟进到 Init
函数,获取数据库连接参数信息
Open
函数连接数据库
后续判断数据库版本后进入查询,返回 TRUE
给Open
函数
继续步入,跟进 SetQuery
函数
这一步主要是用配置文件中的数据库前缀(安装cms时指定)格式化代码中的数据表前缀
接着注意到有一个SQL语句安全检查代码
跳转看看
粗略看一下做了很多检查,可以防止绝大多数的sql注入攻击了
遗憾的是,这里的sql语句并没有进到检查,原因是初始化的时候 $safeCheck
为 false
,并且在此函数中没有赋为 true
所以直接跳过这段 CheckSql
后面一个ExecTime
函数计算执行时间,mysqli_query
函数返回 false
ExecuteNoneQuery
返回 false
最后回到 dede/article_coonepage_rule.php
查询 co_onepage
表中的数据并渲染
过程中传入id后执行删除操作,页面显示的内容为独立的代码从数据库中查出,用户不可控,所以没有对应的注入回显,只能将思路转到报错和盲注
0x03 POC构造探索
- 后续试了用宽字节和其他字符对单引号进行闭合都没有成功,但是转念一想,就算能闭合引号,但是长的注入语句一般都会有逗号出现,出现了逗号从后端代码逻辑上来说相当于是多选删除,就进入了另一条用括号包裹的sql语句,也就不需要上一步的闭合引号操作了
3.1 报错注入?
由于是白盒,直接到后台构造报错语句
可以看到这个表有6个字段
模仿后端sql语句构造联合查询报错extractvalue(0x0a,concat(0x0a,(select+database())))
试一下发数据
传入后端语句也是跟在phpmyadmin
查询时一样的
可惜并没有报错回显
3.2 盲注!
- 那就试试盲注了,先构造试一下
ids=(SELECT%20*%20FROM%20(SELECT(SLEEP(5)))ItPM)
结果没有延迟返回
- sqlmap开跑,指定level和注入类型,dump数据库
python2 sqlmap.py -r ../../dede1.txt -p ids --level 5 --technique T --dbs
在这之前用的 level2、3、4都没成功,还组合了tamper脚本进行payload编码试图绕过,结果最后发现是要用 level 5 才能跑出来
最后是用ELT
跑出来的盲注
附上验证payload
/DedeCMS-5.7.80/dede/article_coonepage_rule.php?action=del&ids=ELT(3337>3336,SLEEP(5))
数据库
ids=ELT(ORD(MID((SELECT DISTINCT(IFNULL(CAST(schema_name AS NCHAR),0x20)) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 1,1),1,1))!=116,SLEEP(3))
其他类推
0x04 修复
看一下后续版本修复的代码
将传入的$ids
转换为变量,防止字符拼接,另外对多选删除部分的代码用 explode
函数转换成数组再遍历,将各个值分隔开,防止拼接、闭合等注入
0x05 扩展——payload中涉及到的sql函数
- ELT
用法: ELT(N,str1,str2,str3,...)
如果N =1返回str1,如果N= 2返回str2,等等。如果参数的数量小于1或大于N,返回NULL。
- ORD
用法: ORD(str)
如果该str
是多字节(指的是multi-byte character set即MBCS,字符的大小是可变的;一个MBCS编码包含一些一个字节长的字符,而另一些字符大于一个字节的长度),MySQL ORD() 返回最左边字符的代码。如果最左边的字符不是多字节字符,ORD() 将返回与 ASCII() 函数相同的值。
- MID
用法: SELECT MID(column_name,start[,length]) FROM table_name
MID 函数用于从文本字段中提取字符,start
起始值为1。
- DISTINCT
用法: SELECT DISTINCT column_name FROM table_name
用于返回唯一不同的值。
- IFNULL
用法: IFNULL(expression_1,expression_2);
IFNULL
函数是MySQL控制流函数之一,它接受两个参数,如果第一个参数不是NULL
,则返回第一个参数。 否则,IFNULL
函数返回第二个参数。
- CAST
用法: CAST (expression AS data_type)
CAST函数用于将某种数据类型的表达式显式转换为另一种数据类型。
0x06 相关链接
- 本文作者: 3r1cCheng
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/1851
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!