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

Constant Summary collapse

ARP_FILTER =

Since:

  • 2.1.3

'arp src %<ipaddr>s and ether dst %<hwaddr>s'
MITM_FILTER =

Since:

  • 2.1.3

'((ip src %<target1>s and not ip dst %<local_ip>s) or' \
' (ip src %<target2>s and not ip dst %<local_ip>s) or' \
' (ip dst %<target1>s and not ip src %<local_ip>s) or' \
' (ip dst %<target2>s and not ip src %<local_ip>s))' \
' and ether dst %<local_mac>s'

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 1.

Returns:

  • (String, nil)

Raises:

  • (RuntimeError)

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

Since:

  • 2.1.3


78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/packetgen/utils.rb', line 78

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)
                  .add('ARP', sha: Config.instance.hwaddr(iface),
                              spa: Config.instance.ipaddr(iface),
                              tpa: ipaddr)

  capture = Capture.new(iface: iface, timeout: timeout, max: 1, filter: ARP_FILTER % { ipaddr: ipaddr, hwaddr: my_hwaddr })
  cap_thread = Thread.new { capture.start }

  sleep 0.1
  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


31
32
33
34
35
36
# File 'lib/packetgen/utils.rb', line 31

def self.arp_cache
  return self.cache_from_arp_command if File.exist?('/usr/sbin/arp')
  return self.cache_from_ip_command if File.exist?('/usr/bin/ip')

  {}
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


123
124
125
126
127
128
129
# File 'lib/packetgen/utils.rb', line 123

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

.cache_from_arp_commandObject

Since:

  • 2.1.3


39
40
41
42
43
44
45
46
47
48
49
# File 'lib/packetgen/utils.rb', line 39

def self.cache_from_arp_command
  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

.cache_from_ip_commandObject

Since:

  • 2.1.3


52
53
54
55
56
57
58
59
60
61
62
# File 'lib/packetgen/utils.rb', line 52

def self.cache_from_ip_command
  raw_cache = `ip neigh`

  cache = {}
  raw_cache.split("\n").each do |line|
    match = line.match(/^(\d+\.\d+\.\d+\.\d+) dev (\w+) lladdr (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})/)
    cache[match[1]] = [match[3], match[2]] if match
  end

  cache
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


158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/packetgen/utils.rb', line 158

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

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

  cfg = Config.instance
  my_mac = cfg.hwaddr(options[:iface])
  capture = Capture.new(iface: options[:iface],
                        filter: MITM_FILTER % { target1: target1, target2: target2, local_ip: cfg.ipaddr(options[:iface]), local_mac: my_mac })

  spoofer.start_all
  mitm_core(capture, target1, target2, my_mac, &proc)
  spoofer.stop_all
end

.mitm_core(capture, target1, target2, my_mac) ⇒ Object

Since:

  • 2.1.3


176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/packetgen/utils.rb', line 176

def self.mitm_core(capture, target1, target2, my_mac)
  mac1 = arp(target1)
  mac2 = arp(target2)

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

    l2.src = my_mac
    l2.dst = if (iph.src == target1) || (iph.dst == target2)
               mac2
             else # (iph.src == target2) || (iph.dst == target1)
               mac1
             end
    modified_pkt.to_w(capture.iface)
  end
end