【java安全】Tomcat waf绕过(charset方式)

在一次hvv中应用存在表达式注入漏洞,但是payload被waf拦截,尝试了chunked无果,于是尝试寻找tomcat绕过waf的方法,最后发现该方式居然可以过雷池。

应用是以tomcat启动的,绕过waf最常用的就是编码,如何构造数据使得tomcat可以解析而waf无法解析从而放行?

tomcat基础

需要先了解tomcat是如何解析http内容的

tomcat的典型架构如下,一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但是可以有多个Connectors

image

多个 Connector 和一个 Container 就形成了一个 Service,有了 Service 就可以对外提供服务

具体解析http请求的是在Connector中,Connector用于接受请求并将请求封装成Request和Response,然后交给Container进行处理,Container处理完之后在交给Connector返回给客户端。

Connector结构图

image

connector 拿到发来的请求,交给 processor 处理,一般来说我们的 processor 是 org.apache.coyote.http11.Http11Processor,用来处理 HTTP1.1 协议

其中比较关键的几个方法

org.apache.coyote.http11.Http11Processor#service 		处理的入口
org.apache.coyote.http11.Http11Processor#prepareRequest 解析请求

解析完了之后把 request 传给 jsp,jsp 拿到的 request 是层层嵌套如下

org.apache.catalina.connector.RequestFacade
org.apache.catalina.connector.Request
org.apache.coyote.Request

最后一个 Request ,就是上面 Http11Processor 直接解析出的 request,它有一个 org.apache.tomcat.util.http.Parameters 类的成员

当 jsp 里面调用 request.getParameter 时,就会层层调用到这个 Parameters 类的 processParameters 方法

image

里面会按 & 和 = 分割,把 body 里面的参数值和参数名称解析出来

image

详细阅读代码,发现参数名和参数值都会被指定的 charset 解码,这里就给了我们用 charset 绕 WAF 的机会

private void processParameters(byte bytes[], int start, int len, Charset charset)

image

这个 charset 其实是来自这个类的成员,默认是 ISO_8859_1

找一下这个 charset 在哪里赋值了,很容易找到它其实来自org.apache.catalina.connector.Request#parseParameters

image

这个函数比较复杂,既处理 application/x-www-form-urlencoded 又处理 multipart/form-data 的编码问题

既解析 charset,又解析 chunk

我们只看我们关心的 ,这里调用 getCharset 方法,设置给了Parameters 类的 charset 成员,修改了默认 charset

跟进getCharset方法,里面层层调用getCharset,不断往下

image

找到核心代码:org.apache.coyote.Request#​getCharsetFromContentType()

image

看一下这个方法怎么获取的,逻辑很简单,就是字符操作尝试获取charset=xxx中的xxx

image

这里其实就可以进行混淆比如

Content-Type: application/x-www-form-urlencoded;;AAAAAAAAcharset=     "  Utf16-lE  " ;AAAAAAAAAAAA

image

混淆后也是可以正确获取值

后面会通过charsetname别名从缓存里获取charset全名

\org\apache\tomcat\util\buf\CharsetCache.class

image

比较复杂,但是知道名字或者别名就能获取到了,具体支持的 charset 可以看下面两个变量,支持非常多种charset

org.apache.tomcat.util.buf.CharsetCache#INITIAL_CHARSETS
org.apache.tomcat.util.buf.CharsetCache#LAZY_CHARSETS

image

测试

写一个简单的jsp测试一下:获取参数t并输出

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    String t = request.getParameter("t");
    out.println(t);
%>
</body>
</html>

使用IBM 875编码

image

使用python发包,可以看到编码后的数据服务端成功解码

image

绕过雷池

测试一下是否能够绕过雷池,首先是没有编码的攻击请求,直接被拦截了

image

编码后的请求,成功绕过

image

不加混淆也是可以绕过的

image

备注

以上测试是在Tomcat 8.5.90, 在Tomcat9中org.apache.coyote.Request#getCharsetFromContentType()方法改用parseMediaType​解析ComtentType的各个部分,因此混淆方法会失效

image

核心是使用HttpParser.readToken​解析字符串(token流式处理

image