shiro反序列化绕WAF之未知HTTP请求方法

0x01 背景

当下WAF对shiro的防护,确实比较严格。对rememberMe的长度进行限制,甚至解密payload检查反序列化class。本周我遇到一个场景,就是这种情况。使用之前的方法rememberMe=加密payload+==垃圾数据也失败了,这个方法之前有大佬分享过,我就不再赘述了。我最终使用未知HTTP请求方法解决战斗。

被WAF拦截

0x02 过程

当时我的思考是shiro的payload在header上,如何修改request header可以导致waf解析不出来,但是后端中间件正常解析呢?

第一步,先构造出先绕WAF,哪怕改成不合法的数据包。
第二步,在绕WAF的数据包基础上修正,让后端中间件可以解析。

我把被拦截的包发送的repeater模块,尝试切换http版本,添加垃圾header头等等方法均没绕过。在修改GET方法为XXX这样的未知HTTP请求方法时,发现WAF不在拦截,但是后端报错了。

未知HTTP请求方法可以过WAF

接下来验证下后端是否真正处理了rememberMe。我先请求去掉rememberMe,response对应的rememberMe消失了

删除rememberMe进行测试

然后再加上rememberMe,repseone的remeberMe又回来了。这说明后端正常处理rememberMe,这么绕WAF没问题!

添加rememberMe进行测试

最后将之前注入内存webshell的payload修改下请求方法,成功下Web权限。

0x03 原理

方法简单粗暴,不难推断WAF是通过正常的http方法识别HTTP数据包的。但是为何后端中间件依然能拿到rememberMe的结果呢?

于是我在本地代码org.apache.shiro.web.mgt.CookieRememberMeManager#getRememberedSerializedIdentity处下了断点。

调试shiro rememberMe流程

通过XXX方法发送数据包,调试发现request.getCookies可以获取到rememberMe值,而且如下方法均可正常使用。说明未知HTTP请求方法不影响各类参数的读取。

1
2
3
request.getHeader
request.getParameter // 只能读url提交的参数,body提交的没有解析
request.getInputStream // 读request body

那对三大组件的调用是否有影响呢?继续翻阅Tomcat源码,我发现Listener被调用是受行为事件影响,Filter是受请求路径影响,而Servlet是受请求路径HTTP请求方法影响。一旦遇到未知方法,Servlet不再进入业务代码,直接返回一个http.method_not_implemented报错。具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//javax.servlet.http.HttpServlet#service
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
.....
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}

所以得到一个结论就是 未知Http方法名绕WAF这个姿势,可以使用在Filter和Listener层出现的漏洞,同时WAF不解析的情况