JAVA代码审计-XSS

前言

本篇记录一下java代码审计的XSS漏洞需要关注的点。

0x01 XSS漏洞分析

XSS产生

后台未对用户输入进行检查或过滤,直接把用户输入返回至前端。导致javascript代码在客户端任意执行。

代码举例

public void Message(HttpServletRequest req, HttpServletResponse resp) {
        // TODO Auto-generated method stub
        String message = req.getParameter("msg");

        try {
            resp.getWriter().print(message);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

 }

这里获取的msg字段原封不动返回出来,如果是js代码,也会解析执行,造成反射型xss漏洞;

image-20220201223237837

image-20220201223309393

防御XSS

根据漏洞原因,可以采用过滤输入的方法来进行防御:

  • 输入转义进行存储
  • 过滤特殊字符
  • 前段输入限制< `等特殊字符

具体防御采用的方式可以是:

  • 全局过滤器,设置filter:

    <filter>  
            <filter-name>XssSafe</filter-name>  
            <filter-class>XssFilter</filter-class>  
    </filter>  
    <filter-mapping>  
            <filter-name>XssSafe</filter-name>  
            <url-pattern>/*</url-pattern>  
    </filter-mapping>
    

    这里要注意的是,我们的配置是/*而不是/< url-pattern>/</url-pattern> 会匹配到/login这样的路径型url,不会匹配到模式为*.jsp这样的后缀型url,而< url-pattern>/*</url-pattern>会匹配所有url:路径型的和后缀型的url(包括/login,*.jsp,*.js*.html等)。

    然后编写过滤器的内容就行了,这个网上有写好的,可以直接拿来用,如下:

    //XssFilter实现:
    
    public class XssFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);
        }
    
    }
    //XssHttpServletRequestWrapper实现
    
    public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
    
        public XssHttpServletRequestWrapper(HttpServletRequest request) {
            super(request);
        }
    
        @SuppressWarnings("rawtypes")
        public Map<String,String[]> getParameterMap(){
            Map<String,String[]> request_map = super.getParameterMap();
            Iterator iterator = request_map.entrySet().iterator();
            while(iterator.hasNext()){
                Map.Entry me = (Map.Entry)iterator.next();
                String[] values = (String[])me.getValue();
                for(int i = 0 ; i < values.length ; i++){
                    values[i] = xssClean(values[i]);
                }
            }
    
            return request_map;
        }
         public String[] getParameterValues(String paramString)
          {
            String[] arrayOfString1 = super.getParameterValues(paramString);
            if (arrayOfString1 == null)
              return null;
            int i = arrayOfString1.length;
            String[] arrayOfString2 = new String[i];
            for (int j = 0; j < i; j++){
                arrayOfString2[j] = xssClean(arrayOfString1[j]);
            }
            return arrayOfString2;
          }
    
          public String getParameter(String paramString)
          {
            String str = super.getParameter(paramString);
            if (str == null)
              return null;
            return xssClean(str);
          }
    
          public String getHeader(String paramString)
          {
            String str = super.getHeader(paramString);
            if (str == null)
              return null;
            str = str.replaceAll("\r|\n", "");
            return xssClean(str);
          }
    
    
          private String xssClean(String value) {
            //ClassLoaderUtils.getResourceAsStream("classpath:antisamy-slashdot.xml", XssHttpServletRequestWrapper.class)
            if (value != null) {
                // NOTE: It's highly recommended to use the ESAPI library and
                // uncomment the following line to
                // avoid encoded attacks.
                // value = encoder.canonicalize(value);
                value = value.replaceAll("\0", "");
    
                // Avoid anything between script tags
                Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>",
                        Pattern.CASE_INSENSITIVE);
                value = scriptPattern.matcher(value).replaceAll("");
    
                // Avoid anything in a src='...' type of expression
                scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
                        Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                | Pattern.DOTALL);
                value = scriptPattern.matcher(value).replaceAll("");
                // Avoid anything in a href='...' type of expression
                scriptPattern = Pattern.compile("href[\r\n]*=[\r\n]*\\\"(.*?)\\\"",
                                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                            | Pattern.DOTALL);
                value = scriptPattern.matcher(value).replaceAll("");
    
    
                // Remove any lonesome </script> tag
                scriptPattern = Pattern.compile("</script>",
                        Pattern.CASE_INSENSITIVE);
                value = scriptPattern.matcher(value).replaceAll("");
    
                // Remove any lonesome <script ...> tag
                scriptPattern = Pattern.compile("<script(.*?)>",
                        Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                | Pattern.DOTALL);
                value = scriptPattern.matcher(value).replaceAll("");
    
                // Avoid eval(...) expressions
                scriptPattern = Pattern.compile("eval\\((.*?)\\)",
                        Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                | Pattern.DOTALL);
                value = scriptPattern.matcher(value).replaceAll("");
    
                // Avoid expression(...) expressions
                scriptPattern = Pattern.compile("expression\\((.*?)\\)",
                        Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                | Pattern.DOTALL);
                value = scriptPattern.matcher(value).replaceAll("");
    
                // Avoid javascript:... expressions
                scriptPattern = Pattern.compile("javascript:",
                        Pattern.CASE_INSENSITIVE);
                value = scriptPattern.matcher(value).replaceAll("");
    
                // Avoid vbscript:... expressions
                scriptPattern = Pattern.compile("vbscript:",
                        Pattern.CASE_INSENSITIVE);
                value = scriptPattern.matcher(value).replaceAll("");
    
                // Avoid onload= expressions
                scriptPattern = Pattern.compile("onload(.*?)=",
                        Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                | Pattern.DOTALL);
                value = scriptPattern.matcher(value).replaceAll("");
            }  
              return value; 
              }
    }
    
  • 使用工具类xssProtect

    下载对应jar包放入lib目录导入:https://code.google.com/archive/p/xssprotect/

    简单用法:

    protectedAgainstXSS(String html){StringReader reader = new StringReader(html); StringWriter writer = new StringWriter();
      try {
          // 从“ html”变量解析传入的字符串
          HTMLParser.process( reader, writer, new XSSFilter(), true );
    
          // 返回经过解析和处理的字符串
          return writer.toString();
      } catch (HandlingException e) {
      }
    }
    

    具体的使用方式可以参考:https://www.iteye.com/blog/liuzidong-1744023

  • 使用ESAPI可以防御大多数web攻击

    pom导入:

    <dependency>
        <groupId>org.owasp.esapi</groupId>
        <artifactId>esapi</artifactId>
        <version>2.2.1.1</version>
    </dependency>
    

    获取输入参数前进行一次实体编码:

     public void Message(HttpServletRequest req, HttpServletResponse resp) {
            // TODO Auto-generated method stub
            String message = req.getParameter("msg");
            String s = ESAPI.encoder().encodeForHTML(message);  //进行实体编码
    
            try {
                resp.getWriter().print(s);
    
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
    }
    

0x02 审计XSS漏洞

setAttribute

setAttribute方法用来在同一个request周期中保存变量使用,使用时再通过getAttribute方法取出。

@WebServlet("/demo")
public class xssServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("text/html");// 设置响应类型
        String content = request.getParameter("content");  //获取content传参数据
        request.setAttribute("content", content);  //content共享到request域
        request.getRequestDispatcher("/WEB-INF/pages/xss.jsp").forward(request, response);  //转发到xxs.jsp页面中

    }
}

jsp中:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    ${requestScope.content}
</head>
<body>

</body>
</html>

ModelAndView

ModelAndView可以返回参数到指定页面的request作用域中或者指定返回的页面名称。

@RequestMapping("/test")
public ModelAndView test(){
    ModelAndView mav=new ModelAndView("hello");
    mav.addObject("time", new Date());
    mav.getModel().put("name", "caoyc");
    
    return mav;
}

jsp中:

time:${requestScope.time} <br/>
name:${name }

我们可以通过观察ModelAndView中传入的参数是不是可控的或者有没有什么过滤来进行审计。

可以看到不论是利用哪种方法,最后都需要取出并输出,现在用的比较广泛的除了<%out.println(msg);%>就是el表达式,也就是说在审计xss中也可以从el表达式入手。

参考

https://xz.aliyun.com/t/6937

https://www.cnblogs.com/nice0e3/p/13655552.html

posted @   N0r4h  阅读(389)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示