漏洞概要 关注数(24) 关注此漏洞
缺陷编号: WooYun-2014-81208
漏洞标题: cmseasy的SQL注射漏洞(附分析和exp)
相关厂商: cmseasy
漏洞作者: Noxxx
提交时间: 2014-10-29 16:18
公开时间: 2015-01-27 16:20
漏洞类型: SQL注射漏洞
危害等级: 高
自评Rank: 20
漏洞状态: 厂商已经确认
漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 help@wooyun.org
Tags标签: 无
漏洞详情
披露状态:
2014-10-29: 细节已通知厂商并且等待厂商处理中
2014-10-29: 厂商已经确认,细节仅向厂商公开
2014-11-01: 细节向第三方安全合作伙伴开放(绿盟科技、唐朝安全巡航、无声信息)
2014-12-23: 细节向核心白帽子及相关领域专家公开
2015-01-02: 细节向普通白帽子公开
2015-01-12: 细节向实习白帽子公开
2015-01-27: 细节向公众公开
简要描述:
cmseasy sql注射漏洞
详细说明:
先看 manage_act.php 174行
如果 session中没有 from这个的话就设置front类中$from这个为值,我们追追他的$from怎么产生的。
在 front_class.php 312-313
看了下,好像没有对 $_SERVER['HTTP_REFERER']做转义处理,系统默认GPC也是不对_SERVER处理的,导致我又可以注射了。
之前发了个已经说到了他session保存在数据库中,有出注入基本就可以控制它整个系统了..(http://**.**.**.**/bugs/wooyun-2014-076542)
----------
不多说了.上exp,先注册一个号 然后
正当我觉得很顺利的时候,发现webscan360拦截了我,
新版本中 白名单已经失效了..白名单是二维数组,原先是用foreach遍历2次,新版本却只遍历一次,所以永远也不能找到对应的白名单。
还有个蛋疼的地方是 post拦截规则加上了 |' 出现单引号就拦截 由于webscan360对referre用的就是post拦截规则,(这样直接拦截单引号,对用户体验也不够好,比如搜索单引号就拦截了,)
单引号不能使用,想了想
结构是这样的,哪我 把2222 换成 ";openid|s:1:"2 能不能闭合呢.
结果处理成这样了
打印了 SESSION 发现闭合失败,15个字符包含其中,那我使用 转义符 "\" 把双引号转义掉,他这个字符就会少一个
提交
我们继续闭合,我们需要让18:""; 这部分自成一个数据就好了。
经过我一番测试后 终于让他解析成功。
分析补充:
标题:php某函数使用不当导致的漏洞
Cmseasy 使用了session_set_save_handler了,其作用是 把session存到数据库中,而代替 文件
而在我研究中 发现,使用session_set_save_handler 不当就很会出现问题,而任意操纵session,很可怕!
session_set_save_handler php官网的介绍
write(string $sessionId, string $data)
在会话保存数据时会调用 write 回调函数。 此回调函数接收当前会话 ID 以及 $_SESSION 中数据序列化之后的字符串作为参数。 序列化会话数据的过程由 PHP 根据 session.serialize_handler 设定值来完成。
序列化后的数据将和会话 ID 关联在一起进行保存。 当调用 read 回调函数获取数据时,所返回的数据必须要和 传入 write 回调函数的数据完全保持一致。
PHP 会在脚本执行完毕或调用 session_write_close() 函数之后调用此回调函数。 注意,在调用完此回调函数之后,PHP 内部会调用 close 回调函数。
//写 将 $_SESSION 中数据序列化 存入数据库中.
read(string $sessionId)
如果会话中有数据,read 回调函数必须返回将会话数据编码(序列化)后的字符串。 如果会话中没有数据,read 回调函数返回空字符串。
在自动开始会话或者通过调用 session_start() 函数手动开始会话之后,PHP 内部调用 read 回调函数来获取会话数据。 在调用 read 之前,PHP 会调用 open 回调函数。
read 回调返回的序列化之后的字符串格式必须与 write 回调函数保存数据时的格式完全一致。 PHP 会自动反序列化返回的字符串并填充 $_SESSION 超级全局变量。 虽然数据看起来和 serialize() 函数很相似, 但是需要提醒的是,它们是不同的。 请参考: session.serialize_handler。
// 反序列化数据库中的 session 然后返回。
Cmseasy中是这样的 :
__construct 构造函数
session_start();
$this->refresh(session_id());
Refresh -> gc //目的就是看时间差来判断 session过期了没有,如果过期了就删除掉这条session数据
读取的时候 会先从数据库中读取出来 然后 return $result ['data']; 然后 反序列化( session_decode() ) $result ['data'];数据并填充 $_SESSION 超级全局变量,
之后在调用 write 用把 $_SESSION数据序列化( SESSION_ENCODE()) ,的数据写入数据库,并更新时间“update_time”(表示自己还在活动中)。
使用出错就出错在 write 没有把数据进行转义处理,而导致的解析出错。
我们来看
(我先自己在他的框架内做的测试
$_SESSION[‘TEST’] = $_POST[‘a’] //我自己测试方便去掉了 实体化。
)
我们提交 一个 “ \ ”他所对应的sql中就是TEST|s:2:"\\"; 插入数据库中 就会变成TEST|s:2:"\"; 因为 \是转义符啊。而它php自己处理的session序列化值却不认这个符号 把他当作普通字符串来序列化。
显然 按照它的流程来的话,读取了这个值就会出现无法反序列化的情况。
数据会变成 TEST|N; 空值,这个一个bug 导致了问题的出现。
现在我们来尝试闭合它,来创建其他的值。
提交 |N;\ 为什么提交这个?因为 |N;来满足他后面的闭合 用 转义符让他的结构出错。
TEST|s:5:"|N;\"; -> null
我们自己加一个值呢?加个 ooo 值吧
居然成功解析掉了,
在 archive_act.php中。有一段讲搜索记录存入session中的代码。
256-258
我知道cmseasy全局都实体化了。中间测试fuzz费劲,,最后成功了,但是只能使用 int类型的,
还有个地方提下,
manage_act.php 174行
如果 session中没有 from这个的话就设置front类中$from这个为值,我们追追他的$from怎么产生的。
在 front_class.php 312-313
看了下,好像没有对 $_SERVER['HTTP_REFERER']做转义处理,系统默认GPC也是不对_SERVER处理的。
(新版本中 白名单已经失效了..白名单是二维数组,原先是用foreach遍历2次,新版本却只遍历一次,所以永远也不能找到对应的白名单。
还有个蛋疼的地方是 post拦截规则加上了 |' 出现单引号就拦截 由于webscan360对referre用的就是post拦截规则,(这样直接拦截单引号,对用户体验也不够好,比如搜索单引号就拦截了,))
这里的和 上面做了转义处理没做实体化处理的同理
Referer: |N;openid|s:1:\"2\"\ 这样即可
这个有好几个利用 比如 user_act.php 中的 edit_action函数内的userid 任意修改密码,再比如 respond_action 函数中的openid 注册管理员。
最后附上几个 测试的代码
下了分php的源码但是不怎么明白..怎么执行的。最后都调用来的 php_var_unserialize ,。。
最后说一下 这个也算是php的一个小bug把。我查看php官网上session_set_save_handler 函数 说明 好像并没有看见说明安全性的问题...广大朋友要注意这一点了。。
漏洞证明:
见详细说明
解决方案 :
write 中转义data
漏洞证明:
给个exp:
登录状态
/cmseasy/index.php?case=manage&act=edit&manage=archive&id=1
Referer: |N;\openid|s:1:\"2\"
利用方法很多,用到session地方都可以伪造,参考 http://**.**.**.**/bugs/wooyun-2014-076542,
修复方案:
转义啊
版权声明:转载请注明来源 Noxxx@乌云
漏洞回应
厂商回应:
危害等级:中
漏洞Rank:8
确认时间:2014-10-29 19:16
厂商回复:
感谢提交,我们将会在后面对整个程序的数据查询重写,希望白帽子能够给我们更多的意见和建议。
最新状态:
暂无
漏洞评价:
对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值