Java SpringBoot框架代码审计四 - XSS

XSS我们都知道是由于字符不过滤或过滤不完善导致的,也都知道修复方法。但是在程序中哪个位置进行字符过滤,是很多渗透测试人员不了解的。不了解字符过滤在程序中的所处的位置,是不能在代码审计中找到XSS的。

XSS代码审计过程

我们先从开发者的视角来看,假设一个系统有100个可输入字符的功能点,现在我需要做XSS防护,那么我是不是要在这100个功能点前都做一遍XSS字符过滤呢?都做一遍的话开发可能得累死,而且每个功能点还需要重新进行测试,整个过程下来消耗大量的人力。

就算一个一个功能点加完字符过滤,那以后项目有新需求,重新添加功能怎么办呢?又给每个功能添加XSS字符过滤?那别的工作还要不要做了。

所以XSS修复并不是单独进行的,往往是通过Filter或者Interceptor来全局处理。Filter或Interceptor在请求进入Controller前,对请求内容进行处理。这样可以做到统一的字符处理,不用在每个功能点前单独对用户传入的字符进行XSS过滤。

在代码审计中,找XSS漏洞第一件事是先判断程序中是否存在Filter或者Interceptor。

一个理想的XSS流程处理的简略图如下,红色表示XSS payload未被转义,绿色表示已转义。

payload-request-path

回到项目结构中来,我们发现其中没有配置任何filter,只有interceptor(拦截器)。Filter和interceptor做的事很相似,但他们的触发时机不同,且interceptor只在Spring中生效,具体差别可以搜索引擎中搜索查看。

在项目代码中查找发现不存在filter,只有三个interceptor,分别是:AdminLoginInterceptorNewBeeMallCartNumberInterceptorNewBeeMallLoginInterceptor。从文件名来判断就知道不存在XSS相关的拦截器。

可以判断该应用程序中很可能存在XSS。

do-not-exist-filter

我们先来实际测试一下XSS,在首页搜索框中输入XSS payload "><script>alert(document.domain)</script>

xss-first-try

查看返回结果,咦奇怪,怎么XSS payload没有触发呢?

xss-first-try-response

抓包看一下,确定是XSS payload中的"<>被转义了

burp-xss-first-try

我们下断点调试看一下,传进来的keyword确实是"><script>alert(document.domain)</script>,传入Web系统的过程中没有转义处理,那问题出在哪里呢?

debug-xss-first-try

其实转义出现在输出到模版的过程中,系统使用了thymeleaf作为模版引擎,可以处理HTML,XML,JavaScript,CSS。thymeleaf有自己的XSS转义方法,thymeleaf模版在对th:text标签进行渲染的时候,默认对特殊字符进行了转义,所以我们输入的XSS payload是在输出时被转义的。

/src/main/resources/templates/mall/search.html:33

thymeleaf-transfer

此时XSS payload传递路径如下,红色表示XSS payload未被转义,绿色表示已转义。

xss-request-path

弄明白XSS payload被转义的原理之后,我们可以转换思路:

  1. 找thymeleaf未作转义的输出,th:utext不会将字符转义

发现以下模版中存在th:utext,所以这两个功能点存在XSS。其中第二个,detail.html因为显示的是商品信息,商品信息使用富文本编辑器,不能简单的转义处理,需要考虑正常的html标签传输,所以作者在这里使用th:utext来显示商品详细。

search-not-transferred

  1. 找不经过thymeleaf的输出

在后台,商品信息编辑中输入XSS payload,点击保存

not-through-thymeleaf

发现触发我们输入的XSS payload

xss-exploit

我们可以抓包看一下,商品信息使用jqGrid传输,将返回的json显示到网页中,该过程没有对字符串进行转义,导致XSS漏洞产生。

burp-xss-second-try

除以上举例之外,系统还存在多处XSS,在此就不一一列举,大家有兴趣的话可以自己试着找找。

XSS 修复

预防存储型和反射型XSS的代码有两种常见方法:

  1. 对HTML做充分转义

转义一般在filter或interceptor中做,如何添加xss filter网上有很多资料可以参考,比如 https://juejin.im/post/5d079e555188251ad81a28d9 ,就不在此赘述。

XSS难以消灭其中一个因素是功能需求与转义相背,比如filter转义把json中的双引号转义导致json无法解析;富文本编辑器就不能转义正常用户所需的html标签,需要额外对允许输入的标签做一个白名单过滤;大于小于号的比较,输入1<2,转义就变成了1 &lt; 2导致功能失效等。

修复过程中得结合考虑实际的业务场景。

  1. 改成纯前端渲染,避免直接输出html字符,把代码和数据完全分隔开,但是又需要注意DOM XSS的问题。

其他

除了存储型和反射型XSS,还有DOM型XSS,因为它属于前端JavaScript的范畴,和后端Java代码没有关系,所以我没在本文中提及。

HttpOnly属性:SpringBoot默认为会话cookie开启httponly,审计时检查是否存在server.servlet.session.cookie.http-only属性,如果存在的话检查其是否被改为false

CSP:在响应头中添加X-Content-Security-Policy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.addHeaderWriter(new StaticHeadersWriter("X-Content-Security-Policy","script-src 'self'"))
// ...
}
}

参考资料

https://tech.meituan.com/2018/09/27/fe-security.html

https://blog.csdn.net/heweimingming/article/details/79993591

https://juejin.im/entry/59bcdb34f265da06461884a4

https://juejin.im/post/5d079e555188251ad81a28d9