最近碰到几个漏洞都是在获取HTTP请求中URI时,使用 getRequestURI()
进行权限校验,而非 getServletPath()
导致。
漏洞描述:当应用存在静态资源目录,比如 /css/
目录,在权限校验时一般会选择放行,即不校验权限。研发同学用 getRequestURI()
获取URI后,判断是否包含 /css/
字符串,如果包含则不校验权限。此时如果URI为 /css/../hello
,用 getRequestURI()
获取的URI是 /css/../hello
,包含 /css/
字符串,所以不校验权限。但是此时后端的路由为 /hello
,导致权限绕过。
并且今天刚看到shiro的一个新CVE,也是因为 getRequestURI()
导致, 相关Github Commit记录 。
@RequestMapping(value = "/hello") @ResponseBody private String index(HttpServletRequest request, HttpServletResponse response) { String uri = request.getRequestURI(); String serlvetPath = request.getServletPath(); return "uri: " + uri + "/t" + "serlvetPath: " + serlvetPath; }
利用 ../
进行权限绕过。
当用curl发起 ../
的请求,curl会默认rebuid该请求,所以加了 --path-as-is
让其保持原请求。
curl -v 'localhost:8080/css/../hello' --path-as-is curl -v 'localhost:8080/css/%2e%2e/hello' --path-as-is
分别返回
uri: /css/../hello serlvetPath: /hello uri: /css/%2e%2e/hello serlvetPath: /hello
利用 ..;
进行权限绕过:
curl -v 'localhost:8080/css/..;/hello' --path-as-is curl -v 'localhost:8080/css/..;name=joychou/hello' --path-as-is curl -v 'localhost:8080/css/%2e%2e;name=joychou/hello' --path-as-is
分别返回:
uri: /css/..;/hello serlvetPath: /hello uri: /css/..;name=joychou/hello serlvetPath: /hello uri: /css/%2e%2e;name=joychou/hello serlvetPath: /hello
让研发同学使用从getRequestURI改为getServletPath,研发同学考虑到稳定性还是存在顾虑。网上搜索确实二者会有结果不一致的情况,但未提及是什么情况。
我们用单独打个war包,放在tomcat的webapps目录下运行,看到这种情况二者的结果不一样。
顾名思义,在getServletPath()方法的注释中:
Returns the part of this request's URL that calls the servlet.
源码就没必要看了,因为URI是什么, getRuquestURI()
就返回什么,并且不会进行URL解码。
Tomcat的 postParseRequest()
方法会对HTTP请求进行预处理,我们关心的逻辑:
parsePathParameters() normalize()
先来看看 parsePathParameters()
的处理逻辑:
curl -v 'localhost:8080/css/..;name=joychou/hello' --path-as-is
判断是否存在分号
获取;和/之间的内容name=joychou,对其用=分隔,并添加到addPathParameter方法中,并从URI中去掉 ;name=joychou
内容。这种用法一般用来获取sessionid,比如 http://xx/index.html;jsessionid=1234
。
可以看到,新的URI已经变成了 /css/../hello
接着就是对URI进行标准化(normalize),先对URI进行URLDecode,如果存在 /../
,将其返回到上一级目录,即/css/../hello处理为/hello,并将新的Path设置为servletPath。
前几天有个业务方反馈发布回滚了,原因是因为HTTP请求的GET参数中存在 [xx]
字符串,升级docker后tomcat版本发生改变,tomcat对URL处理逻辑改变,导致 []
字符串被认为是不安全字符。所以业务方在升级tomcat存在稳定性的风险,一定要做好相关测试。tomcat该安全性功能的判断,直接解析HTTP请求内容,不会对URL进行URL解码。
IS_NOT_REQUEST_TARGET
变量控制HTTP请求的URI,使其不允许出现 空格"#<>/^
{|}`和非可见字符 (ASCII码0-31和127)。
IS_QUERY_RELAXED
控制HTTP请求内容中第一行出现的参数,使其不允许出现 空格"#<>[/]^
{|} 和非可见字符 (ASCII码0-31和127)。
IS_QUERY_RELAXED`数组中值为false表示非法字符,一共有46个非法字母(128-82个合法字符)。
82个合法字符如下:
扩展的小彩弹:
tomcat提供一个 tomcat.util.http.parser.HttpParser.requestTargetAllow
参数属性,该参数只能在 {}|
中任选,设置后允许设置的字符出现在整个URL中(包括URI+HTTP请求内容中第一行出现的参数)。
什么是HTTP请求内容中第一行的参数?
简单理解就是POST的参数就不是第一行参数。因为POST的参数位置在HTTP请求内容Header的下面。
本文由JoyChou 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Mar 25, 2020 at 11:08 am