无意中看到ch1ng师傅的文章觉得很有趣,不得不感叹师傅太厉害了,但我一看那长篇的函数总觉得会有更骚的东西,所幸还真的有,借此机会就发出来一探究竟,同时也不得不感慨下RFC文档的妙处,当然本文针对的技术也仅仅只是在流量层面上waf的绕过
1 | Avoid including the "\" character in the quoted-string form of the filename parameter, as escaping is not implemented by some user agents, and "\" can be considered an illegal path character. |
简单做个总结如果首位是"
(前提条件是里面有\
字符),那么就会去掉跳过从第二个字符开始,并且末尾也会往前移动一位,同时会忽略字符\
,师傅只提到了类似test.\war
这样的例子
还是在org.apache.catalina.core.ApplicationPart#getSubmittedFileName
当中,一看到这个将字符串转换成map的操作总觉得里面会有更骚的东西(这里先是解析传入的参数再获取,如果解析过程有利用点那么也会影响到后面参数获取),不扯远继续回到正题
首先它会获取header参数Content-Disposition
当中的值,如果以form-data
或者attachment
开头就会进行我们的解析操作,跟进去一看果不其然,看到RFC2231Utility
瞬间不困了
1 | Asterisks ("*") are reused to provide the indicator that language and character set information is present and encoding is being used. A single quote ("'") is used to delimit the character set and language information at the beginning of the parameter value. Percent signs ("%") are used as the encoding flag, which agrees with RFC 2047. |
结合注释可以看到标准格式@param encodedText - Text to be decoded has a format of {@code <charset>'<language>'<encoded_value>}
,分别是编码,语言和待解码的字符串,同时这里还适配了对url编码的解码,也就是fromHex
函数,具体代码如下,其实就是url解码
1 |
|
同样的我们也可以进行套娃结合上面的filename=""y\4.\w\arK"
改成filename="UTF-16BE'Y4tacker'%00%22%00y%00%5C%004%00.%00%5C%00w%00%5C%00a%00r%00K"
在此基础上我发现还可以做一些新的东西,其实就是对org.apache.tomcat.util.http.fileupload.ParameterParser#parse(char[], int, int, char)
函数进行深入分析
在获取值的时候paramValue = parseQuotedToken(new char[] {separator });
,其实是按照分隔符;
分割,因此我们不难想到前面的东西其实可以不用"
进行包裹,在parseQuotedToken最后返回调用的是return getToken(true);
,这个函数也很简单就不必多解释
既然调用parse
解析参数时可以不被包裹,结合getToken函数我们可以知道在最后一个参数其实就不必要加;
了,并且解析完通过params.get("filename")
获取到参数后还会调用到org.apache.tomcat.util.http.parser.HttpParser#unquote
那也可以基于此再次变形
通过查询官方文档,可以发现从Servlet3.1开始,tomcat新增了对此的支持,也就意味着简单通过javax.servlet.http.HttpServletRequest#getParts
即可,简化了我们文件上传的代码负担(如果我是开发人员,我肯定首选也会使用,谁不想当懒狗呢)
早上起床想着昨晚和陈师的碰撞,起床后又看了下陈师的星球,看到这个不妨再试试Spring是否也按照了RFC的实现呢(毕竟Spring内置了Tomcat,可能会有类似的呢)
而spring处理文件上传逻辑的具体关键逻辑在org.springframework.web.multipart.support.StandardMultipartHttpServletRequest#parseRequest
,抄个文件上传demo来进行测试分析
其中关于org.springframework.web.multipart.support.StandardMultipartHttpServletRequest#parseRequest
的调用也有些不同
也是随便来个新的springboot2.6.4的,来看看spring5的,小版本间差异不测了,经过测试发现spring5和spring4之间也是有版本差异处理也有些不同,同样是在parseRequest
很明显可以看到这一行filename.startsWith("=?") && filename.endsWith("?=")
,可以看出Spring对文件名也是支持QP编码
如果是filename*
后面的处理逻辑就是else分之,可以看出和我们上面分析spring4还是有点区别就是这里只支持UTF-8/ISO-8859-1/US_ASCII
,编码受限制
1 | attr-char = ALPHA / DIGIT |