前言
前段时间在分析Jetty漏洞时无意捡了个CVE,下面是当时分析该漏洞并挖掘到CVE的记录。
CVE-2021-28164
该漏洞影响9.4.37.v20210219
,9.4.38.v20210224
版本,攻击者可通过/%2e/WEB-INF/
绕过WEB-INF下文件的访问限制,读取WEB-INF下的文件内容。目前该漏洞我仅在9.4.38.v20210224
复现成功。
漏洞复现
首先使用IDEA创建一个maven的web项目,修改POM文件添加Jetty依赖,这个依赖使用阿里云的源是找不到的,可以修改为maven的官方源,并为maven设置socks代理来解决下载依赖的问题,下面是我的POM文件配置。
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
加载依赖后,通过mvn jetty:run
命令运行jetty,访问/%2e/WEB-INF/web.xml
读取xml文件,至此漏洞复现成功。
漏洞分析
根据Jetty官网的描述,这个漏洞是由于servlet和url解析产生了不一致导致的。所以当使用/%2e
进行绕过时,一定会进入到servlet,由于在Jetty中所有HTTPservlet的请求都会过javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletRespons)
,所以可以在该函数打断点观察程序后续的执行逻辑。
接下来会进入到org.eclipse.jetty.servlet.DefaultServlet#doGet
,在该方法中又会调用org.eclipse.jetty.server.ResourceService#doGet
方法。
在org.eclipse.jetty.server.ResourceService#doGet
中会根据传入的路径获取文件内容。
当获取的内容为目录时,则列出目录结构,所以也可以通过/%2e/WEB-INF/
读取WEB-INF
下的目录
当正常读取到文件内容时,则将读取到的文件内容发送到客户端。
通过上面的分析,之所以能进行文件读取操作,是因为DefaultServlet的处理导致的,通过查阅文档,DefaultServlet映射路径为/
,主要作用为上下文提供对静态内容的处理。所以如果程序中将其他的servlet mapping设置为/
,访问 /%2e/WEB-INF/web.xml
将不会触发读取漏洞,因为请求不会交给DefaultServlet处理。
那么为什么直接访问/./WEB-INF/web.xml
不会触发漏洞呢?
在org.eclipse.jetty.server.Request#setMetaData
中会对请求的URI进行解析,在这个过程中会获取URL解码后的url并通过setPathInfo进行设置。
在org.eclipse.jetty.server.handler.ContextHandler#isProtectedTarget
中会判断请求的路径是否以/WEB-INF
开头,由于url解码后的url并不满足条件,因此绕过了此处黑名单的检测,才能顺利的执行到DefaultServlet的方法,否则会直接返回。
为什么通过/./WEB-INF/web.xml
可以顺利的请求到/WEB-INF/web.xml
文件呢?
这个取决于org.eclipse.jetty.server.handler.ContextHandler#getResource
方法,在该方法中通过canonicalPath
对路径进行处理,所有的/./
都会被转换为/
。
除了%2e编码绕过,能否通过其他方式绕过?
在获取org.eclipse.jetty.http.HttpURI#getDecodedPath
获取解码参数时,主要是通过获取_decodedPath
属性的值来实现的,而这个属性在org.eclipse.jetty.http.HttpURI#parse(org.eclipse.jetty.http.HttpURI.State, java.lang.String, int, int)
方法中别赋值,首先通过canonicalPath
对/./
或/../
形式的url进行转换,再通过decodePath
进行解码。
在decodePath
中还会将%u002e
,%2e;;;
转换为.
,因此也可以使用这种方式绕过。
我们知道,这个漏洞之所以使用url编码,是为了绕过canonicalPath
对/./
的转换,但经过我们上面的分析也可以通过/.;/WEB-INF/web.xml
来绕过,那么为什么不能使用这种方式绕过呢?
首先确实可以通过/.;/
来绕过canonicalPath方法,但是实际使用这种方式会爆400,这是为什么呢?
根据报错定位到org.eclipse.jetty.server.Request#setMetaData
,isAmbiguous
的返回结果是由_ambiguous
决定的,所以要找到哪里给_ambiguous
赋值。
在org.eclipse.jetty.http.HttpURI#checkSegment
中,当要处理的字符的前一个字符为.或者..时,会返回false,并给_ambiguous
赋值。
注意代码__ambiguousSegments.put("%2e", Boolean.TRUE);
,这里如果改为false,那么就无法使用/%2e/WEB-INF/web.xml
来绕过。
除了读取xml文件,能否读取WEB-INF下的class或者jsp文件?
当读取jsp文件时,并不会得到jsp文件的内容,因为HTTPServlet会将请求交给JettyJspServlet,会去解析jsp的内容,但使用这种方式也可以让我们直接去执行WEB-INF下的jsp文件。
至于class或者jar文件,可以直接读取内容,所以利用该漏洞读取源码。
漏洞修复
最后看下官方是如何修复这个漏洞,有无绕过的可能。
只有当ambiguous为true时才会执行上面的代码,但如果我们用%u002e
就可以绕过该限制。
联系jetty获取CVE
总结
通过对这个漏洞的分析,一方面觉得花费时间去跟进漏洞是值得的,另外身为一个做漏洞挖掘的,深入分析漏洞的原理是必要的,这也是能挖掘到新漏洞的前提。最后总结下这个漏洞的利用
- 目录遍历,枚举/WEB-INF下的目录结构
- 源码读取,除了可以读取配置文件,也可以读取class文件
- 执行/WEB-INF下的JSP文件