S2-045 漏洞学习笔记

漏洞公告

Problem It is possible to perform a RCE attack with a malicious Content-Type value. If the Content-Type value isn’t valid an exception is thrown which is then used to display an error message to a user.

描述中说明了,漏洞是通过 content-type 注入的,关键在于对错误信息并没有做妥善的处理。

又看到概述中说

Summary Possible Remote Code Execution when performing file upload based on Jakarta Multipart parser.

我有个疑问就是这不是文件上传的过程中出现的问题吗?那为什么 POC 直接使用 GET 请求,也不用上传文件就搞定了呢。

因为不熟悉 Struts 框架,所以我找文档简单的了解了一下。Big Picture

Big Picture

从这图来看所有请求都会在 FilterInterceptor 间流转,最终到达相应的 Action 来处理。

大致流程

请求首先在 StrutsPrepareAndExecuteFilter 中被进一步封装:

request = prepare.wrapRequest(request);

wrapRequest 出自 PrepareOperations:

prepareOperation

然后进一步观察 request = dispatcher.wrapRequest(request); 中的 dispatcher:

dispatcher

然后我明白了我之前的疑问,因为 POC 开头那句 #nike='multipart/form-data' 就是针对 content_type.contains("multipart/form-data") 的。而之前也提及了所有请求都会在 FilterInterceptor 间流转,最终到达相应的 Action 来处理。所以 GET 请求也会经过文件上传的 Filter

然后是 MultiPartRequest mpr = getMultiPartRequest();

getMultiPartRequest

这个方法就是选择文件上传解析类用的了。如果没有配置默认就是 org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest 了。

回到 Dispatcher 中的 wrapRequest 方法。看到 request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup);

进一步查看 MultiPartRequestWrapper 类:

MultiPartRequestWrapper

进一步查看 MultiPartRequestWrapper 调用的 JakartaMultiPartRequest:

JakartaMultiPartRequest

MultiPartRequestWrapper 会通过调用JakartaMultiPartRequest.java的parse进行解析请求, 一旦出错了就会调用 53 行的 buildErrorMessage 方法:

buildErrorMessage

然后查看官方的修改记录https://github.com/apache/struts/commit/b06dd50af2a3319dd896bf5c2f4972d2b772cf2b

S2-045

主要是去掉了 LocalizedTextUtilfindText 方法。查看了这个方法的注释,发现关键点:

S2-045-KEY

于是乎最终在 FileUploadInterceptor 中调用 LocalizedTextUtilfindText 方法,该方法获取了 buildErrorMessage 中生成的内容,并最终执行了恶意的ONGL表达式。