0x01 前言
分享一下之前记得笔记吧,关于使用Srping 5.x StandardServletMultipartResolver文件上传解析器绕waf的学习。
0x02 代码分析
参考以下信息。
下断点在checkMultipart方法。
忽略大小写,判断是否以multipart/开头。
调用 resolveMultipart() 方法对请求的数据进行解析。
首先进入
StandardMultipartHttpServletRequest#parseRequest方法。
主要关注以下框选的部分。
第一部分:
进入FileItemIteratorImpl#init方法,
这里将contentType转为小写判断是否以multipart/开头。
然后获取长度、编码、boundary等。
进入FileUploadBase#getBoundary,lowerCaseNames属性设置为为true,
跟踪ParameterParser#parse方法。
发现不仅可以采用分号进行分割逗号也可以。
继续跟踪parse重载方法,parseToken与parseQuotedToken分别用于获取参数名与参数值。
而且这两个方法都调用了getToken。
继续分析,这里通过hasEncodedValue校验paramName,如果是boundary*这种,走RFC2231Utility.decodeText解析,如果不是,进入MimeUtility.decodeText判断是否包含=?,如果包含继续解析不包含直接返回文本。
这两个不多说了,简单先看下例子,就明白啥意思了。
最后还会将参数名转为小写,并返回。
所以这里的就可以构造如下:
Content-Type: multiparT/1111,,,,, BounDary= =?utf-8?B?LS0tLTk5OQ==?=
Content-Type: multiparT/1111,,,,, BounDary*= utf-8'11'%2d%2d%2d%2d%39%39%39
回到FileItemIteratorImpl#findNextItem方法,这里有个很有意思的点,首先代码判断是否currentFieldName为空,如果为空,会调用getFieldName获取fieldName,然后判断contentType是否为Multipart/mixed开头,如果是,会将currentFieldName设置为fieldName,然后再次获取设置boundary,再次循环。
此时currentFieldName不为null,走else,获取判断filename,设置headers,然后正常走流程。
这里注意一下,与boundary处理不同,只能以;分割。
例子:
------999
Content-DispOsition: fOrm-Data; nAme="file"
Content-Type: Multipart/miXed;BounDary==?utf-8?B?LS0tLTg4OA==?=
1
------888
Content-DispOsition: fOrm-Data;;;;;;;;;;;;;
;;;;;;;;;;;;;;;filename="test4.txt"
Content-Type: text/plain
aaaaaa
------888--
------999--
看到getFileName会以;作为分隔符,那可不可以传入类似test.jsp;.jpg。
可以看到这里设置的headers并不是getFileName方法处理过的。
在这里通过getItemIterator拿出headers,添加到items,最后赋值给var30并返回。
然后这里将items添加到了parts中。
最后从parts中取出headers。
所以使用StandardServletMultipartResolver解析器是不可以的。
第二部分:
如果传入的不是"filename*"进入下面的if,然后判断传入的是否是"filename"且filename是否为null,如果是,会将value值传给filename,这里也就是说如果存在两个filename是取第一个的。
但是如果传入的是"filename*"会进入下方代码解析,以"'"号分割,前面部分调用了trim所以可以添加空白字符,然后覆盖原来的filename值,但限制了UTF_8与ISO_8859_1。
例子:
------999
Content-DispOsition: fOrm-Data; nAme="file"
Content-Type: Multipart/miXed;BounDary==?utf-8?B?LS0tLTg4OA==?=
1
------888
Content-DispOsition: fOrm-Data;;;;;;;;;;;;;
;;;;;;;;;;;;;;;filename="test4.txt";;;;; ;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;; filename*=
utf-8
'11'%74%65%73%74%32%32%32%2e%74%78%74
;;;;;;;;;
Content-Type: text/plain
aaaaaa
------888--
------999--
第三部分:
这里判断如果filename不为null,且为开头为=?,末尾为?=,调用MimeDelegate.decode进行MIME解码,格式也是为"=?utf-8?B?LS0tLTg4OA==?=,=?gbk?Q?=2d=2d=2d=2d=38=38=38?=",支持QP编码以及Base64,但是这里实际是调用javax.mail库的MimeUtility,默认是没有引入这个依赖的,需要手动引入才行。
例子:
-----------------------------40941428474650107364239774674
Content-DispOsition: fOrm-Data
;;;;;;;;;;; nAme=" file ";;;;filename==?utf-8?B?dGVzdC50eHQ=?=;;;;; ;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;; filename=
utf-8
'11'124243665572%33%34.txt ;;;;;;;;;
Content-Type: text/plain
11111111111
-----------------------------40941428474650107364239774674--
0x03 小密圈
最后送你一张优惠券,欢迎加入小密圈,好朋友。
原文始发于微信公众号(小黑说安全):从解析器的角度探究绕WAF姿势
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论