我是如何捡到Jetty CVE的

前言

前段时间在分析Jetty漏洞时无意捡了个CVE,下面是当时分析该漏洞并挖掘到CVE的记录。

CVE-2021-28164

该漏洞影响9.4.37.v202102199.4.38.v20210224版本,攻击者可通过/%2e/WEB-INF/绕过WEB-INF下文件的访问限制,读取WEB-INF下的文件内容。目前该漏洞我仅在9.4.38.v20210224复现成功。

漏洞复现

首先使用IDEA创建一个maven的web项目,修改POM文件添加Jetty依赖,这个依赖使用阿里云的源是找不到的,可以修改为maven的官方源,并为maven设置socks代理来解决下载依赖的问题,下面是我的POM文件配置。

4.0.0

war

jetty_CVE-2021-28169

org.example

jetty_CVE-2021-28169

1.0-SNAPSHOT

org.eclipse.jetty

jetty-maven-plugin

9.4.38.v20210224

8082

/

</project>

加载依赖后,通过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文件