Module: PacketGen::Utils

Defined in:
lib/packetgen/utils.rb,
lib/packetgen/utils/arp_spoofer.rb

Overview

Collection of some network utilities.

This module is not enabled by default. You need to:

require 'packetgen/utils'

Author:

  • Sylvain Daubert

Since:

  • 2.1.3

Defined Under Namespace

Classes: ARPSpoofer

Class Method Summary collapse

Class Method Details

.arp(ipaddr, options = {}) ⇒ String?

Get MAC address from an IP address, or nil if this IP address is unknown on local network.

Parameters:

  • ipaddr (String)

    dotted-octet IP address

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :iface (String)

    interface name. Default to PacketGen.default_iface

  • :no_cache (Boolean)

    if true, do not query local ARP cache and always send an ARP request on wire. Default to false

  • :timeout (Integer)

    timeout in seconds before stopping request. Default to 2.

Returns:

  • (String, nil)

Raises:

  • (RuntimeError)

    user don’t have permission to capture packets on network device.

Since:

  • 2.1.3



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/packetgen/utils.rb', line 46

def self.arp(ipaddr, options={})
  unless options[:no_cache]
    local_cache = self.arp_cache
    return local_cache[ipaddr].first if local_cache.key? ipaddr
  end

  iface = options[:iface] || PacketGen.default_iface
  timeout = options[:timeout] || 1
  my_hwaddr = Config.instance.hwaddr(iface)
  arp_pkt = Packet.gen('Eth', dst: 'ff:ff:ff:ff:ff:ff', src: my_hwaddr)
  arp_pkt.add('ARP', sha: Config.instance.hwaddr, spa: Config.instance.ipaddr,
                     tpa: ipaddr)

  capture = Capture.new(iface: iface, timeout: timeout, max: 1,
                        filter: "arp src #{ipaddr} and ether dst #{my_hwaddr}")
  cap_thread = Thread.new do
    capture.start
  end

  arp_pkt.to_w(iface)
  cap_thread.join

  return if capture.packets.empty?

  capture.packets.each do |pkt|
    break pkt.arp.sha.to_s if pkt.arp.spa.to_s == ipaddr
  end
end

.arp_cacheHash

Get local ARP cache

Returns:

  • (Hash)

    key: IP address, value: array containing MAC address and interface name

Since:

  • 2.1.3



22
23
24
25
26
27
28
29
30
31
32
# File 'lib/packetgen/utils.rb', line 22

def self.arp_cache
  raw_cache = `/usr/sbin/arp -an`

  cache = {}
  raw_cache.split(/\n/).each do |line|
    match = line.match(/\((\d+\.\d+\.\d+\.\d+)\) at (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})(?: \[ether\])? on (\w+)/)
    cache[match[1]] = [match[2], match[4]] if match
  end

  cache
end

.arp_spoof(target_ip, spoofed_ip, options = {}) ⇒ void

Note:

This method is provided for test purpose.

This method returns an undefined value.

Do ARP spoofing on given IP address. Call to this method blocks. For more control, see ARPSpoofer class.

Parameters:

  • target_ip (String)

    target IP address

  • spoofed_ip (String)

    IP address to spoof

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :mac (String)

    MAC address used to poison target ARP cache. Default to local MAC address.

  • :for_seconds (Integer, nil)

    number of seconds to do ARP spoofing. If not defined, spoof forever.

  • :interval (Float, Integer)

    number of seconds between 2 ARP packets (default: 1.0).

  • :iface (String)

    interface to use. Default to PacketGen.default_iface

Raises:

  • (RuntimeError)

    user don’t have permission to capture packets on network device.

Since:

  • 2.1.3



91
92
93
94
95
96
97
# File 'lib/packetgen/utils.rb', line 91

def self.arp_spoof(target_ip, spoofed_ip, options={})
  interval = options[:interval] || 1.0
  as = ARPSpoofer.new(timeout: options[:for_seconds], interval: interval,
                      iface: options[:iface])
  as.start(target_ip, spoofed_ip, mac: options[:mac])
  as.wait
end

.mitm(target1, target2, options = {}) {|pkt| ... } ⇒ void

Note:

This method is provided for test purpose.

This method returns an undefined value.

Man in the middle attack. Capture all packets between two peers on same local network.

Examples:

Change ID in packets

PacketGen::Utils.mitm('192.168.0.1', '192.168.0.45') do |pkt|
  if pkt.ip.src == '192.168.0.1'
    # 192.168.0.1 -> 192.168.0.45
    pkt.ip.id = 1
  else
    # 192.168.0.45 -> 192.168.0.1
    pkt.ip.id = 2
  end
  pkt
end

Parameters:

  • target1 (String)

    IP address of first peer to attack

  • target2 (String)

    IP address of second peer to attack

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :interval (Float, Integer)

    number of seconds between 2 ARP packets (default: 1.0).

  • :iface (String)

    interface to use. Default to PacketGen.default_iface

Yield Parameters:

  • pkt (Packet)

    captured packets between target1 and target2

Yield Returns:

  • (Packet)

    packet to send to target1 or 2. This may be modified received packet

Raises:

  • (RuntimeError)

    user don’t have permission to capture packets on network device.

Since:

  • 2.2.0



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/packetgen/utils.rb', line 126

def self.mitm(target1, target2, options={})
  options = { iface: PacketGen.default_iface }.merge(options)

  mac1 = arp(target1)
  mac2 = arp(target2)

  spoofer = Utils::ARPSpoofer.new(options)
  spoofer.add target1, target2, options
  spoofer.add target2, target1, options

  my_mac = Config.instance.hwaddr(options[:iface])
  my_ip = Config.instance.ipaddr(options[:iface])
  capture = Capture.new(iface: options[:iface],
                        filter: "((ip src #{target1} and not ip dst #{my_ip}) or" \
                                " (ip src #{target2} and not ip dst #{my_ip}) or" \
                                " (ip dst #{target1} and not ip src #{my_ip}) or" \
                                " (ip dst #{target2} and not ip src #{my_ip}))" \
                                " and ether dst #{my_mac}")

  spoofer.start_all
  capture.start do |pkt|
    modified_pkt = yield pkt
    iph = modified_pkt.ip
    l2 = modified_pkt.is?('Dot11') ? modified_pkt.dot11 : modified_pkt.eth

    if (iph.src == target1) || (iph.dst == target2)
      l2.dst = mac2
    elsif (iph.src == target2) || (iph.dst == target1)
      l2.dst = mac1
    else
      next
    end
    modified_pkt.to_w(options[:iface])
  end
end