##
# 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/socket/range_walker'

class Metasploit3 < Msf::Auxiliary

	# Exploit mixins should be called first
	include Msf::Exploit::Remote::HttpClient
	# Scanner mixin should be near last
	include Msf::Auxiliary::Scanner
	include Msf::Auxiliary::Report

	def initialize
		super(
			'Name'          => 'Squid Proxy Port Scanner',
			'Description'   => %q{
				A misconfigured Squid proxy can allow an attacker to make requests on his behalf.
					This may give the attacker information about devices that he cannot reach but the
					Squid proxy can. For example, an attacker can make requests for internal IP addresses
					against a misconfigurated open Squid proxy exposed to the Internet, therefore performing
					an internal port scan. The error messages returned by the proxy are used to determine
					if the port is open or not.

					Many Squid proxies use custom error codes so your mileage may vary. The open_proxy
					module can be used to test for open proxies, though a Squid proxy does not have to be
					open in order to allow for pivoting (e.g. an Intranet Squid proxy which allows
					the attack to pivot to another part of the network).
			},
			'Author'	       => ['willis'],
			'References'	 =>
				[
							'URL','http://wiki.squid-cache.org/SquidFaq/SecurityPitfalls'
				],

			'License'	=> MSF_LICENSE
		)

		register_options(
			[
				OptString.new('RANGE', [true, "IPs to scan through Squid proxy", '']),
				OptString.new('PORTS', [true, "Ports to scan; must be TCP", "21,80,139,443,445,1433,1521,1723,3389,8080,9100"]),
				OptBool.new('MANUAL_CHECK',[true,"Stop the scan if server seems to answer positively to every request",true]),
				OptString.new('CANARY_IP',[true,"The IP to check if the proxy always answers positively; the IP should not respond.","1.2.3.4"])
			], self.class)

	end

	def run_host(target_host)

		begin
				iplist = Rex::Socket::RangeWalker.new(datastore['RANGE'])
				dead = false
				portlist = Rex::Socket.portspec_crack(datastore['PORTS'])

				vprint_status("[#{rhost}] Verifying manual testing is not required...")

				manual = false
				#request a non-existent page first to make sure the server doesn't respond with a 200 to everything.
				res_test = send_request_cgi({
					'uri'          => "http://{datastore['CANARY_IP']}:80",
					'method'       => 'GET',
					'data'  =>      '',
					'version' => '1.0',
					'vhost' => ''
				}, 10)

				if res_test and res_test.body and (res_test.code == 200)
					print_error("#{rhost} likely answers positively to every request, check it manually.")
					print_error("\t\t Proceeding with the scan may increase false positives.")
					manual = true
				end


				iplist.each do |target|
					next if manual and datastore['MANUAL_CHECK']

					portlist.each do |port|
						next if dead

						vprint_status("[#{rhost}] Requesting #{target}:#{port}")
						if port==443
							res = send_request_cgi({
								'uri'          => "https://#{target}:#{port}",
								'method'       => 'GET',
								'data'  =>      '',
								'version' => '1.0',
								'vhost' => ''
								}, 10)
						else
							res = send_request_cgi({
								'uri'          => "http://#{target}:#{port}",
								'method'       => 'GET',
								'data'  =>      '',
								'version' => '1.0',
								'vhost' => ''
							}, 10)
						end

						if res and res.body

							if res.code == 200 or res.body =~ /Zero/ or res.code == 404 or res.code == 401
								print_good("[#{rhost}] #{target}:#{port} seems OPEN")
								report_service(:host => target, :port => port, :name => "unknown", :info => res.body )
							end
							if res.body =~ /No route to host/
								dead = true
								print_error("[#{rhost}] #{target} is DEAD")
							end

							print_status("[#{rhost}] #{target}:#{port} blocked by ACL") if res.body =~ /Access control/

							if res.body =~ /Connection refused/ or res.body =~ /service not listening/
								report_host(:host => target)
								print_good("[#{rhost}] #{target} is alive but #{port} is CLOSED")
							end
						end
					end
					dead = false
				end

				rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
				rescue ::Timeout::Error, ::Errno::EPIPE

		end
	end
end
