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


class Metasploit3 < Msf::Auxiliary

	# Exploit mixins should be called first
	include Msf::Exploit::Remote::DCERPC

	include Msf::Auxiliary::Report

	# Scanner mixin should be near last
	include Msf::Auxiliary::Scanner

	def initialize
		super(
			'Name'        => 'Hidden DCERPC Service Discovery',
			'Description' => %q{
				This module will query the endpoint mapper and make a list
			of all ncacn_tcp RPC services. It will then connect to each of
			these services and use the management API to list all other
			RPC services accessible on this port. Any RPC service found attached
			to a TCP port, but not listed in the endpoint mapper, will be displayed
			and analyzed to see whether anonymous access is permitted.
			},
			'Author'      => 'hdm',
			'License'     => MSF_LICENSE
		)

		deregister_options('RHOST', 'RPORT')
	end

	# Obtain information about a single host
	def run_host(ip)
		begin

			epm = dcerpc_endpoint_list()
			if(not epm)
				print_status("Could not contact the endpoint mapper on #{ip}")
				return
			end

			eports = {}

			epm.each do |ep|
				next if !(ep[:port] and ep[:prot] and ep[:prot] == "tcp")
				eports[ep[:port]] ||= {}
				eports[ep[:port]][ep[:uuid]+'_'+ep[:vers]] = true
			end

			eports.each_pair do |eport, servs|

				rport = eport
				print_status("Looking for services on #{ip}:#{rport}...")

				ids = dcerpc_mgmt_inq_if_ids(rport)
				return if not ids

				ids.each do |id|
					if (not servs.has_key?(id[0]+'_'+id[1]))
						print_status("\tHIDDEN: UUID #{id[0]} v#{id[1]}")

						conn = nil
						bind = nil
						call = nil
						data = nil
						error = nil
						begin
							connect(true, { 'RPORT' => eport })
							conn = true

							handle = dcerpc_handle(id[0], id[1], 'ncacn_ip_tcp', [eport])
							dcerpc_bind(handle)
							bind = true

							res = dcerpc.call(0, NDR.long(0) * 128)
							call = true

							if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
								data = dcerpc.last_response.stub_data
							end

						rescue ::Interrupt
							raise $!
						rescue ::Exception => e
							error = e.to_s
						end

						if (error and error =~ /DCERPC FAULT/ and error !~ /nca_s_fault_access_denied/)
							call = true
						end

						status = "\t\t"
						status << "CONN " if conn
						status << "BIND " if bind
						status << "CALL " if call
						status << "DATA=#{data.unpack("H*")[0]} " if data
						status << "ERROR=#{error} " if error

						print_status(status)
						print_status("")

						## Add Report
						report_note(
							:host   => ip,
							:proto  => 'tcp',
							:port   => datastore['RPORT'],
							:type   => "DCERPC HIDDEN: UUID #{id[0]} v#{id[1]}",
							:data   => status
						)

					end
				end
			end

		rescue ::Interrupt
			raise $!
		rescue ::Exception => e
			print_status("Error: #{e}")
		end
	end


end
