##
# 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
	include Msf::Auxiliary::Scanner
	include Msf::Exploit::Capture

	def initialize
		super(
			'Name'         => 'BNAT Scanner',
			'Description'  => %q{
					This module is a scanner which can detect Broken NAT (network address translation)
				implementations, which could result in a inability to reach ports on remote
				machines. Typically, these ports will appear in nmap scans as 'filtered'/'closed'.
				},
			'Author'       =>
				[
					'bannedit',
					'Jonathan Claudius <jclaudius[at]trustwave.com>',
				],
			'License'      => MSF_LICENSE,
			'References'   =>
				[
					[ 'URL', 'https://github.com/claudijd/BNAT-Suite'],
					[ 'URL', 'http://www.slideshare.net/claudijd/dc-skytalk-bnat-hijacking-repairing-broken-communication-channels'],
				]
		)

		register_options(
				[
					OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "21,22,23,80,443"]),
					OptString.new('INTERFACE', [true, "The name of the interface", "eth0"]),
					OptInt.new('TIMEOUT', [true, "The reply read timeout in milliseconds", 500])
				],self.class)

		deregister_options('FILTER','PCAPFILE','RHOST','SNAPLEN')

	end

	def probe_reply(pcap, to)
		reply = nil
		begin
			Timeout.timeout(to) do
				pcap.each do |r|
					pkt = PacketFu::Packet.parse(r)
					next unless pkt.is_tcp?
					reply = pkt
					break
				end
			end
			rescue Timeout::Error
		end
		return reply
	end

	def generate_probe(ip)
		ftypes = %w{windows, linux, freebsd}
		@flavor = ftypes[rand(ftypes.length)]
		config = PacketFu::Utils.whoami?(:iface => datastore['INTERFACE'])
		p = PacketFu::TCPPacket.new(:config => config)
		p.ip_daddr = ip
		p.tcp_flags.syn = 1
		return p
	end

	def run_host(ip)
		open_pcap

		to = (datastore['TIMEOUT'] || 500).to_f / 1000.0

		p = generate_probe(ip)
		pcap = self.capture

		ports = Rex::Socket.portspec_crack(datastore['PORTS'])

		ports.each_with_index do |port,i|
			p.tcp_dst = port
			p.tcp_src = rand(64511)+1024
			p.tcp_seq = rand(64511)+1024
			p.recalc

			ackbpf = "tcp [8:4] == 0x#{(p.tcp_seq + 1).to_s(16)}"
			pcap.setfilter("tcp and tcp[13] == 18 and not host #{ip} and src port #{p.tcp_dst} and dst port #{p.tcp_src} and #{ackbpf}")
			capture_sendto(p, ip)
			reply = probe_reply(pcap, to)
			next if reply.nil?

			print_status("[BNAT RESPONSE] Requested IP: #{ip} Responding IP: #{reply.ip_saddr} Port: #{reply.tcp_src}")
		end

		close_pcap
	end

end
