claudijd/bnat

View on GitHub
bin/bnat-router

Summary

Maintainability
Test Coverage
#!/usr/bin/env ruby

require 'rubygems'
require 'packetfu'

puts "\nbnat-router v0.1\n\n"

inint = ARGV[0]
outint = ARGV[2]
clientip = ARGV[1]
serverip = ARGV[3]
bnatip = ARGV[4]

def usage
  puts "\nUsage: ruby bnat-router.rb <insideint> <clientip> <outsideint> <serverip> <bnatip>"
  puts "Example: ruby bnat-router.rb eth1 192.168.3.2 eth0 1.1.2.1 1.1.2.2\n\n"
  Process.exit
end

if ARGV.length != 5
  usage
end

def arp(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
    sleep 0.1 # Check for a response ten times per second.
  end
  #puts "#{target_ip} is-at #{target_mac}"
  return target_mac
end

clientmac = arp(clientip,inint)
puts "Obtained Client MAC: #{clientmac}"
servermac = arp(serverip,outint)
puts "Obtained Server MAC: #{servermac}"
bnatmac = arp(bnatip,outint)
puts "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(
  :config => "#{outint}",
  :start => true,
  :filter => "tcp and src #{bnatip}"
)
puts "Now listening on #{outint}..."
inpcap = PacketFu::Capture.new(
  :iface => "#{inint}",
  :start => true,
  :filter => "tcp and src #{clientip} and dst #{serverip}"
)
puts "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
      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]
      )
      puts "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 and
        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.recalc
      inj = PacketFu::Inject.new(
        :iface => "#{outint}",
        :config =>outconfig
      )
      inj.a2w(:array => [packet.to_s])

      #Double tap that SYN
      if packet.tcp_flags.syn == 1 and
        packet.tcp_flags.ack == 0

        sleep 0.75
        inj.a2w(:array => [packet.to_s])
      end

      puts "outpacket processed"
    end
  end
end

#Hold Process Open
fromout.join
fromin.join