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'
ARP_PATH =

Since:

  • 2.1.3

'/usr/sbin/arp'
IP_PATH =

Since:

  • 2.1.3

'/usr/bin/ip'
ARP_LINE_RE =

Since:

  • 2.1.3

/\((\d+\.\d+\.\d+\.\d+)\) at (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})(?: \[ether\])? on (\w+)/.freeze
IP_LINE_RE =

Since:

  • 2.1.3

/^(\d+\.\d+\.\d+\.\d+) dev (\w+) lladdr (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})/.freeze

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



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/packetgen/utils.rb', line 88

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



41
42
43
44
45
46
# File 'lib/packetgen/utils.rb', line 41

def self.arp_cache
  return self.cache_from_arp_command if File.exist?(ARP_PATH)
  return self.cache_from_ip_command if File.exist?(IP_PATH)

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



133
134
135
136
137
138
139
# File 'lib/packetgen/utils.rb', line 133

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_command(raw_cache = nil) ⇒ Object

Since:

  • 2.1.3



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/packetgen/utils.rb', line 49

def self.cache_from_arp_command(raw_cache=nil)
  raw_cache ||= `#{ARP_PATH} -an`

  cache = {}
  raw_cache.split("\n").each do |line|
    match = line.match(ARP_LINE_RE)
    cache[match[1]] = [match[2], match[4]] if match
  end

  cache
end

.cache_from_ip_command(raw_cache = nil) ⇒ Object

Since:

  • 2.1.3



62
63
64
65
66
67
68
69
70
71
72
# File 'lib/packetgen/utils.rb', line 62

def self.cache_from_ip_command(raw_cache=nil)
  raw_cache ||= `#{IP_PATH} neigh`

  cache = {}
  raw_cache.split("\n").each do |line|
    match = line.match(IP_LINE_RE)
    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



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/packetgen/utils.rb', line 168

def self.mitm(target1, target2, options={}, &block)
  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, &block)
  spoofer.stop_all
end

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

Since:

  • 2.1.3



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/packetgen/utils.rb', line 186

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