# -*- coding: binary -*-

module Msf

###
#
# This module provides methods for brute forcing authentication
#
###

module Auxiliary::Web
	module Analysis
	end

	require 'msf/core/auxiliary/web/http'
	require 'msf/core/auxiliary/web/fuzzable'
	require 'msf/core/auxiliary/web/form'
	require 'msf/core/auxiliary/web/path'
	require 'msf/core/auxiliary/web/target'

	include Auxiliary::Report

	attr_reader :target
	attr_reader :http
	attr_reader :parent
	attr_reader :page

	def initialize( info = {} )
		super
	end

	# String id to push to the #checklist
	def checked( id )
		parent.checklist << "#{shortname}#{id}".hash
	end

	# String id to check against the #checklist
	def checked?( id )
		parent.checklist.include? "#{shortname}#{id}".hash
	end

	#
	# Called directly before 'run'
	#
	def setup( opts = {} )
		@parent = opts[:parent]
		@target = opts[:target]
		@page   = opts[:page]
		@http   = opts[:http]
	end

	# Should be overridden to return the exploits to use for this
	# vulnerability type as an Array of Strings.
	def self.exploits
	end

	# Must return a configuration Hash for the given exploit and vulnerability.
	def self.configure_exploit( exploit, vuln )
	end

	# Should be overridden to return the payloads used for this
	# vulnerability type as an Array of Strings.
	def payloads
	end

	def token
		"xssmsfpro"
	end

	#
	# Should be overridden to return a pattern to be matched against response
	# bodies in order to identify a vulnerability.
	#
	# You can go one deeper and override #find_proof for more complex processing.
	#
	def signature
	end

	#
	# Default #run, will audit all elements using taint analysis and log
	# results based on #find_proof return values.
	#
	def run
		auditable.each { |element| element.taint_analysis }
	end

	# Returns an Array of elements prepared to be audited.
	def auditable
		target.auditable.map do |element|
			element.fuzzer = self
			element
		end
	end

	# Checks whether a resource exists based on a path String.
	def resource_exist?( path )
		res = http.get( path )
		res.code.to_i == 200 && !http.custom_404?( path, res.body )
	end
	alias :file_exist? :resource_exist?

	# Checks whether a directory exists based on a path String.
	def directory_exist?( path )
		dir = path.dup
		dir << '/' if !dir.end_with?( '/' )
		resource_exist?( dir )
	end

	# Logs the existence of a resource in the path String.
	def log_resource_if_exists( path )
		log_resource( :location => path ) if resource_exist?( path )
	end
	alias :log_file_if_exists :log_resource_if_exists

	# Logs the existence of the directory in the path String.
	def log_directory_if_exists( path )
		dir = path.dup
		dir << '/' if !dir.end_with?( '/' )
		log_resource_if_exists( dir )
	end

	# Matches fingerprint pattern against the current page's body and logs matches
	def match_and_log_fingerprint( fingerprint, options = {} )
		return if (match = page.body.to_s.match( fingerprint ).to_s).empty?
		log_fingerprint( options.merge( :fingerprint => match ) )
	end

	#
	# Serves as a default detection method for when performing taint analysis.
	#
	# Uses the Regexp in #signature against the response body in order to
	# identify vulnerabilities and return a String that proves it.
	#
	# Override it if you need more complex processing, but remember to return
	# the proof as a String.
	#
	# response - Auxiliary::Web::HTTP::Response
	# element - the submitted element
	#
	def find_proof( response, element )
		return if !signature

		m = response.body.match( signature ).to_s
		return if !m || m.size < 1

		m.gsub( /[\r\n]/, ' ' )
	end

	def increment_request_counter
		parent.increment_request_counter
	end

	# Should be overridden and return an Integer (0-100) denoting the confidence
	# in the accuracy of the logged vuln.
	def calculate_confidence( vuln )
		100
	end

	def log_fingerprint( opts = {} )
		mode  = name
		vhash = [target.to_url, opts[:fingerprint], mode, opts[:location]].
			map { |x| x.to_s }.join( '|' ).hash

		parent.vulns[mode] ||= {}
		return if parent.vulns[mode].include?( vhash )

		location = opts[:location] ?
			page.url.merge( URI( opts[:location].to_s )) : page.url

		info = {
			:web_site   => target.site,
			:path		=> location.path,
			:query		=> location.query,
			:method		=> 'GET',
			:params		=> [],
			:pname		=> 'path',
			:proof		=> opts[:fingerprint],
			:risk		=> details[:risk],
			:name		=> details[:name],
			:blame		=> details[:blame],
			:category	=> details[:category],
			:description => details[:description],
			:owner	  => self
		}

		info[:confidence]  = calculate_confidence( info )
		parent.vulns[mode][vhash] = info

		report_web_vuln( info )

		opts[:print_fingerprint] = true if !opts.include?( :print_fingerprint )

		print_good "	FOUND(#{mode.to_s}) URL(#{location})"
		print_good "		 PROOF(#{opts[:fingerprint]})" if opts[:print_fingerprint]
	end

	def log_resource( opts = {} )
		mode  = name
		vhash = [target.to_url, mode, opts[:location]].
			map { |x| x.to_s }.join( '|' ).hash

		parent.vulns[mode] ||= {}
		return if parent.vulns[mode].include?( vhash )

		location = URI( opts[:location].to_s )
		info = {
			:web_site	 => target.site,
			:path		 => location.path,
			:query		 => location.query,
			:method		 => 'GET',
			:params		 => [],
			:pname		 => 'path',
			:proof		 => opts[:location],
			:risk		 => details[:risk],
			:name		 => details[:name],
			:blame		 => details[:blame],
			:category	 => details[:category],
			:description => details[:description],
			:owner		 => self
		}

		info[:confidence]  = calculate_confidence( info )
		parent.vulns[mode][vhash] = info

		report_web_vuln( info )

		print_good "	VULNERABLE(#{mode.to_s}) URL(#{target.to_url})"
		print_good "		 PROOF(#{opts[:location]})"
	end

	def process_vulnerability( element, proof, opts = {} )
		mode  = name
		vhash = [target.to_url, mode, element.altered].
			map{ |x| x.to_s }.join( '|' ).hash

		parent.vulns[mode] ||= {}
		return parent.vulns[mode][vhash] if parent.vulns[mode][vhash]

		parent.vulns[mode][vhash] = {
			:target		 => target,
			:method		 => element.method.to_s.upcase,
			:params		 => element.params.to_a,
			:mode		 => mode,
			:pname		 => element.altered,
			:proof		 => proof.to_s,
			:form		 => element.model,
			:risk		 => details[:risk],
			:name		 => details[:name],
			:blame		 => details[:blame],
			:category	 => details[:category],
			:description => details[:description]
		}

		confidence = calculate_confidence( parent.vulns[mode][vhash] )

		parent.vulns[mode][vhash].merge!( :confidence => confidence )

		if !(payload = opts[:payload])
			if payloads
				payload = payloads.select { |p|
					element.altered_value.include?( p )
				}.sort_by { |p| p.size }.last
			end
		end

		uri = URI( element.action )
		info = {
			:web_site	 => element.model.web_site,
			:path		 => uri.path,
			:query		 => uri.query,
			:method		 => element.method.to_s.upcase,
			:params		 => element.params.to_a,
			:pname		 => element.altered,
			:proof		 => proof.to_s,
			:risk		 => details[:risk],
			:name		 => details[:name],
			:blame		 => details[:blame],
			:category	 => details[:category],
			:description => details[:description],
			:confidence  => confidence,
			:payload	 => payload,
			:owner		 => self
		}

		report_web_vuln( info )

		print_good "	VULNERABLE(#{mode.to_s}) URL(#{target.to_url})" +
						" PARAMETER(#{element.altered}) VALUES(#{element.params})"
		print_good "		 PROOF(#{proof})"
	end

end
end
