packetfu 1.1.10 → 1.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/.gitignore +3 -0
  5. data/.travis.yml +8 -0
  6. data/CONTRIBUTING.md +47 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +1 -1
  9. data/README.rdoc +35 -30
  10. data/Rakefile +4 -4
  11. data/bench/octets.rb +9 -9
  12. data/examples/100kpackets.rb +12 -12
  13. data/examples/ackscan.rb +16 -16
  14. data/examples/arp.rb +35 -35
  15. data/examples/arphood.rb +36 -36
  16. data/examples/dissect_thinger.rb +6 -6
  17. data/examples/new-simple-stats.rb +23 -23
  18. data/examples/packetfu-shell.rb +25 -25
  19. data/examples/simple-sniffer.rb +9 -9
  20. data/examples/simple-stats.rb +23 -23
  21. data/examples/slammer.rb +3 -3
  22. data/gem-public_cert.pem +21 -0
  23. data/lib/packetfu.rb +149 -127
  24. data/lib/packetfu/capture.rb +169 -169
  25. data/lib/packetfu/config.rb +52 -52
  26. data/lib/packetfu/inject.rb +56 -56
  27. data/lib/packetfu/packet.rb +531 -528
  28. data/lib/packetfu/pcap.rb +579 -579
  29. data/lib/packetfu/protos/arp.rb +90 -90
  30. data/lib/packetfu/protos/arp/header.rb +158 -158
  31. data/lib/packetfu/protos/arp/mixin.rb +36 -36
  32. data/lib/packetfu/protos/eth.rb +44 -44
  33. data/lib/packetfu/protos/eth/header.rb +243 -243
  34. data/lib/packetfu/protos/eth/mixin.rb +3 -3
  35. data/lib/packetfu/protos/hsrp.rb +69 -69
  36. data/lib/packetfu/protos/hsrp/header.rb +107 -107
  37. data/lib/packetfu/protos/hsrp/mixin.rb +29 -29
  38. data/lib/packetfu/protos/icmp.rb +71 -71
  39. data/lib/packetfu/protos/icmp/header.rb +82 -82
  40. data/lib/packetfu/protos/icmp/mixin.rb +14 -14
  41. data/lib/packetfu/protos/invalid.rb +49 -49
  42. data/lib/packetfu/protos/ip.rb +69 -69
  43. data/lib/packetfu/protos/ip/header.rb +291 -291
  44. data/lib/packetfu/protos/ip/mixin.rb +40 -40
  45. data/lib/packetfu/protos/ipv6.rb +50 -50
  46. data/lib/packetfu/protos/ipv6/header.rb +188 -188
  47. data/lib/packetfu/protos/ipv6/mixin.rb +29 -29
  48. data/lib/packetfu/protos/tcp.rb +176 -176
  49. data/lib/packetfu/protos/tcp/ecn.rb +35 -35
  50. data/lib/packetfu/protos/tcp/flags.rb +74 -74
  51. data/lib/packetfu/protos/tcp/header.rb +268 -268
  52. data/lib/packetfu/protos/tcp/hlen.rb +32 -32
  53. data/lib/packetfu/protos/tcp/mixin.rb +46 -46
  54. data/lib/packetfu/protos/tcp/option.rb +321 -321
  55. data/lib/packetfu/protos/tcp/options.rb +95 -95
  56. data/lib/packetfu/protos/tcp/reserved.rb +35 -35
  57. data/lib/packetfu/protos/udp.rb +159 -123
  58. data/lib/packetfu/protos/udp/header.rb +91 -91
  59. data/lib/packetfu/protos/udp/mixin.rb +3 -3
  60. data/lib/packetfu/structfu.rb +280 -280
  61. data/lib/packetfu/utils.rb +292 -225
  62. data/lib/packetfu/version.rb +41 -41
  63. data/packetfu.gemspec +14 -3
  64. data/spec/arp_spec.rb +191 -0
  65. data/spec/eth_spec.rb +148 -0
  66. data/spec/icmp_spec.rb +97 -0
  67. data/spec/ip_spec.rb +78 -0
  68. data/spec/ipv6_spec.rb +81 -0
  69. data/spec/packet_spec.rb +61 -59
  70. data/spec/packet_subclasses_spec.rb +9 -10
  71. data/spec/packetfu_spec.rb +55 -62
  72. data/spec/sample3.pcap +0 -0
  73. data/spec/spec_helper.rb +44 -0
  74. data/spec/structfu_spec.rb +270 -271
  75. data/spec/tcp_spec.rb +76 -77
  76. data/spec/udp_spec.rb +32 -0
  77. data/spec/utils_spec.rb +95 -0
  78. data/test/all_tests.rb +14 -17
  79. data/test/func_lldp.rb +3 -3
  80. data/test/ptest.rb +2 -2
  81. data/test/test_capture.rb +45 -45
  82. data/test/test_eth.rb +70 -68
  83. data/test/test_hsrp.rb +9 -9
  84. data/test/test_inject.rb +18 -18
  85. data/test/test_invalid.rb +16 -16
  86. data/test/test_octets.rb +23 -21
  87. data/test/test_packet.rb +156 -154
  88. data/test/test_pcap.rb +172 -170
  89. data/test/test_structfu.rb +99 -97
  90. data/test/test_tcp.rb +322 -320
  91. data/test/test_udp.rb +78 -76
  92. metadata +108 -44
  93. metadata.gz.sig +2 -0
  94. data/spec/ethpacket_spec.rb +0 -74
  95. data/test/test_arp.rb +0 -135
  96. data/test/test_icmp.rb +0 -62
  97. data/test/test_ip.rb +0 -50
  98. data/test/test_ip6.rb +0 -68
@@ -1,233 +1,300 @@
1
1
  # -*- coding: binary -*-
2
2
  require 'singleton'
3
3
  require 'timeout'
4
+ require 'network_interface'
5
+
4
6
  module PacketFu
5
7
 
6
- # Utils is a collection of various and sundry network utilities that are useful for packet
7
- # manipulation.
8
- class Utils
9
-
10
- # Returns the MAC address of an IP address, or nil if it's not responsive to arp. Takes
11
- # a dotted-octect notation of the target IP address, as well as a number of parameters:
12
- #
13
- # === Parameters
14
- # :iface
15
- # Interface. Defaults to "eth0"
16
- # :eth_saddr
17
- # Source MAC address. Defaults to "00:00:00:00:00:00".
18
- # :ip_saddr
19
- # Source IP address. Defaults to "0.0.0.0"
20
- # :flavor
21
- # The flavor of the ARP request. Defaults to :none.
22
- # :timeout
23
- # Timeout in seconds. Defaults to 3.
24
- #
25
- # === Example
26
- # PacketFu::Utils::arp("192.168.1.1") #=> "00:18:39:01:33:70"
27
- # PacketFu::Utils::arp("192.168.1.1", :iface => "wlan2", :timeout => 5, :flavor => :hp_deskjet)
28
- #
29
- # === Warning
30
- #
31
- # It goes without saying, spewing forged ARP packets on your network is a great way to really
32
- # irritate your co-workers.
33
- def self.arp(target_ip,args={})
34
- iface = args[:iface] || :eth0
35
- args[:config] ||= whoami?(:iface => iface)
36
- arp_pkt = PacketFu::ARPPacket.new(:flavor => (args[:flavor] || :none), :config => args[:config])
37
- arp_pkt.eth_daddr = "ff:ff:ff:ff:ff:ff"
38
- arp_pkt.arp_daddr_mac = "00:00:00:00:00:00"
39
- arp_pkt.arp_daddr_ip = target_ip
40
- # Stick the Capture object in its own thread.
41
- cap_thread = Thread.new do
42
- target_mac = nil
43
- cap = PacketFu::Capture.new(:iface => iface, :start => true,
44
- :filter => "arp src #{target_ip} and ether dst #{arp_pkt.eth_saddr}")
45
- arp_pkt.to_w(iface) # Shorthand for sending single packets to the default interface.
46
- timeout = 0
47
- while target_mac.nil? && timeout <= (args[:timeout] || 3)
48
- if cap.save > 0
49
- arp_response = PacketFu::Packet.parse(cap.array[0])
50
- target_mac = arp_response.arp_saddr_mac if arp_response.arp_saddr_ip = target_ip
51
- end
52
- timeout += 0.1
53
- sleep 0.1 # Check for a response ten times per second.
54
- end
55
- target_mac
56
- end # cap_thread
57
- cap_thread.value
58
- end
59
-
60
- # Since 177/8 is IANA reserved (for now), this network should
61
- # be handled by your default gateway and default interface.
62
- def self.rand_routable_daddr
63
- IPAddr.new((rand(16777216) + 2969567232), Socket::AF_INET)
64
- end
65
-
66
- # Discovers the local IP and Ethernet address, which is useful for writing
67
- # packets you expect to get a response to. Note, this is a noisy
68
- # operation; a UDP packet is generated and dropped on to the default (or named)
69
- # interface, and then captured (which means you need to be root to do this).
70
- #
71
- # whoami? returns a hash of :eth_saddr, :eth_src, :ip_saddr, :ip_src,
72
- # :ip_src_bin, :eth_dst, and :eth_daddr (the last two are usually suitable
73
- # for a gateway mac address). It's most useful as an argument to
74
- # PacketFu::Config.new, or as an argument to the many Packet constructors.
75
- #
76
- # Note that if you have multiple interfaces with the same route (such as when
77
- # wlan0 and eth0 are associated to the same network), the "first" one
78
- # according to Pcap.lookupdev will be used, regardless of which :iface you
79
- # pick.
80
- #
81
- # === Parameters
82
- # :iface => "eth0"
83
- # An interface to listen for packets on. Note that since we rely on the OS to send the probe packet,
84
- # you will need to specify a target which will use this interface.
85
- # :target => "1.2.3.4"
86
- # A target IP address. By default, a packet will be sent to a random address in the 177/8 network.
87
- def self.whoami?(args={})
88
- unless args.kind_of? Hash
89
- raise ArgumentError, "Argument to `whoami?' must be a Hash"
90
- end
91
- if args[:iface].to_s =~ /^lo/ # Linux loopback more or less. Need a switch for windows loopback, too.
92
- dst_host = "127.0.0.1"
93
- else
94
- dst_host = (args[:target] || rand_routable_daddr.to_s)
95
- end
96
-
97
- dst_port = rand(0xffff-1024)+1024
98
- msg = "PacketFu whoami? packet #{(Time.now.to_i + rand(0xffffff)+1)}"
99
- iface = (args[:iface] || ENV['IFACE'] || Pcap.lookupdev || :lo ).to_s
100
- cap = PacketFu::Capture.new(:iface => iface, :promisc => false, :start => true, :filter => "udp and dst host #{dst_host} and dst port #{dst_port}")
101
- udp_sock = UDPSocket.new
102
- udp_sock.send(msg,0,dst_host,dst_port)
103
- udp_sock = nil
104
-
105
- my_data = nil
106
-
107
- begin
108
- Timeout::timeout(1) {
109
- pkt = nil
110
-
111
- while pkt.nil?
112
- raw_pkt = cap.next
113
- next if raw_pkt.nil?
114
-
115
- pkt = Packet.parse(raw_pkt)
116
-
117
- if pkt.payload == msg
118
-
119
- my_data = {
120
- :iface => (args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo").to_s,
121
- :pcapfile => args[:pcapfile] || "/tmp/out.pcap",
122
- :eth_saddr => pkt.eth_saddr,
123
- :eth_src => pkt.eth_src.to_s,
124
- :ip_saddr => pkt.ip_saddr,
125
- :ip_src => pkt.ip_src,
126
- :ip_src_bin => [pkt.ip_src].pack("N"),
127
- :eth_dst => pkt.eth_dst.to_s,
128
- :eth_daddr => pkt.eth_daddr
129
- }
130
-
131
- else raise SecurityError,
132
- "whoami() packet doesn't match sent data. Something fishy's going on."
133
- end
134
-
135
- end
136
- }
137
- rescue Timeout::Error
138
- raise SocketError, "Didn't receive the whoami() packet, can't automatically configure."
139
- end
140
-
141
- my_data
142
- end
143
-
144
- # This is a brute-force approach at trying to find a suitable interface with an IP address.
145
- def self.lookupdev
146
- # XXX cycle through eth0-9 and wlan0-9, and if a cap start throws a RuntimeErorr (and we're
147
- # root), it's not a good interface. Boy, really ought to fix lookupdev directly with another
148
- # method that returns an array rather than just the first candidate.
149
- end
150
-
151
- # Handles ifconfig for various (okay, two) platforms.
152
- # Will have Windows done shortly.
153
- #
154
- # Takes an argument (either string or symbol) of the interface to look up, and
155
- # returns a hash which contains at least the :iface element, and if configured,
156
- # these additional elements:
157
- #
158
- # :eth_saddr # A human readable MAC address
159
- # :eth_src # A packed MAC address
160
- # :ip_saddr # A dotted-quad string IPv4 address
161
- # :ip_src # A packed IPv4 address
162
- # :ip4_obj # An IPAddr object with bitmask
163
- # :ip6_saddr # A colon-delimited hex IPv6 address, with bitmask
164
- # :ip6_obj # An IPAddr object with bitmask
165
- #
166
- # === Example
167
- # PacketFu::Utils.ifconfig :wlan0 # Not associated yet
168
- # #=> {:eth_saddr=>"00:1d:e0:73:9d:ff", :eth_src=>"\000\035\340s\235\377", :iface=>"wlan0"}
169
- # PacketFu::Utils.ifconfig("eth0") # Takes 'eth0' as default
170
- # #=> {:eth_saddr=>"00:1c:23:35:70:3b", :eth_src=>"\000\034#5p;", :ip_saddr=>"10.10.10.9", :ip4_obj=>#<IPAddr: IPv4:10.10.10.0/255.255.254.0>, :ip_src=>"\n\n\n\t", :iface=>"eth0", :ip6_saddr=>"fe80::21c:23ff:fe35:703b/64", :ip6_obj=>#<IPAddr: IPv6:fe80:0000:0000:0000:0000:0000:0000:0000/ffff:ffff:ffff:ffff:0000:0000:0000:0000>}
171
- # PacketFu::Utils.ifconfig :lo
172
- # #=> {:ip_saddr=>"127.0.0.1", :ip4_obj=>#<IPAddr: IPv4:127.0.0.0/255.0.0.0>, :ip_src=>"\177\000\000\001", :iface=>"lo", :ip6_saddr=>"::1/128", :ip6_obj=>#<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>}
173
- def self.ifconfig(iface='eth0')
174
- ret = {}
175
- iface = iface.to_s.scan(/[0-9A-Za-z]/).join # Sanitizing input, no spaces, semicolons, etc.
176
- case RUBY_PLATFORM
177
- when /linux/i
178
- ifconfig_data = %x[ifconfig #{iface}]
179
- if ifconfig_data =~ /#{iface}/i
180
- ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
181
- else
182
- raise ArgumentError, "Cannot ifconfig #{iface}"
183
- end
184
- real_iface = ifconfig_data.first
185
- ret[:iface] = real_iface.split.first.downcase
186
- if real_iface =~ /[\s]HWaddr[\s]+([0-9a-fA-F:]{17})/i
187
- ret[:eth_saddr] = $1.downcase
188
- ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
189
- end
190
- ifconfig_data.each do |s|
191
- case s
192
- when /inet addr:[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
193
- ret[:ip_saddr] = $1
194
- ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
195
- ret[:ip4_obj] = IPAddr.new($1)
196
- ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
197
- when /inet6 addr:[\s]*([0-9a-fA-F:\x2f]+)/
198
- ret[:ip6_saddr] = $1
199
- ret[:ip6_obj] = IPAddr.new($1)
200
- end
201
- end # linux
202
- when /darwin/i
203
- ifconfig_data = %x[ifconfig #{iface}]
204
- if ifconfig_data =~ /#{iface}/i
205
- ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
206
- else
207
- raise ArgumentError, "Cannot ifconfig #{iface}"
208
- end
209
- real_iface = ifconfig_data.first
210
- ret[:iface] = real_iface.split(':')[0]
211
- ifconfig_data.each do |s|
212
- case s
213
- when /ether[\s]([0-9a-fA-F:]{17})/i
214
- ret[:eth_saddr] = $1
215
- ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
216
- when /inet[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
217
- ret[:ip_saddr] = $1
218
- ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
219
- ret[:ip4_obj] = IPAddr.new($1)
220
- ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
221
- when /inet6[\s]*([0-9a-fA-F:\x2f]+)/
222
- ret[:ip6_saddr] = $1
223
- ret[:ip6_obj] = IPAddr.new($1)
224
- end
225
- end # darwin
226
- end # RUBY_PLATFORM
227
- ret
228
- end
229
-
230
- end
8
+ # Utils is a collection of various and sundry network utilities that are useful for packet
9
+ # manipulation.
10
+ class Utils
11
+
12
+ # Returns the MAC address of an IP address, or nil if it's not responsive to arp. Takes
13
+ # a dotted-octect notation of the target IP address, as well as a number of parameters:
14
+ #
15
+ # === Parameters
16
+ # :iface
17
+ # Interface. Defaults to "eth0"
18
+ # :eth_saddr
19
+ # Source MAC address. Defaults to "00:00:00:00:00:00".
20
+ # :ip_saddr
21
+ # Source IP address. Defaults to "0.0.0.0"
22
+ # :flavor
23
+ # The flavor of the ARP request. Defaults to :none.
24
+ # :timeout
25
+ # Timeout in seconds. Defaults to 3.
26
+ #
27
+ # === Example
28
+ # PacketFu::Utils::arp("192.168.1.1") #=> "00:18:39:01:33:70"
29
+ # PacketFu::Utils::arp("192.168.1.1", :iface => "wlan2", :timeout => 5, :flavor => :hp_deskjet)
30
+ #
31
+ # === Warning
32
+ #
33
+ # It goes without saying, spewing forged ARP packets on your network is a great way to really
34
+ # irritate your co-workers.
35
+ def self.arp(target_ip,args={})
36
+ iface = args[:iface] || :eth0
37
+ args[:config] ||= whoami?(:iface => iface)
38
+ arp_pkt = PacketFu::ARPPacket.new(:flavor => (args[:flavor] || :none), :config => args[:config])
39
+ arp_pkt.eth_daddr = "ff:ff:ff:ff:ff:ff"
40
+ arp_pkt.arp_daddr_mac = "00:00:00:00:00:00"
41
+ arp_pkt.arp_daddr_ip = target_ip
42
+ # Stick the Capture object in its own thread.
43
+ cap_thread = Thread.new do
44
+ target_mac = nil
45
+ cap = PacketFu::Capture.new(:iface => iface, :start => true,
46
+ :filter => "arp src #{target_ip} and ether dst #{arp_pkt.eth_saddr}")
47
+ arp_pkt.to_w(iface) # Shorthand for sending single packets to the default interface.
48
+ timeout = 0
49
+ while target_mac.nil? && timeout <= (args[:timeout] || 3)
50
+ if cap.save > 0
51
+ arp_response = PacketFu::Packet.parse(cap.array[0])
52
+ target_mac = arp_response.arp_saddr_mac if arp_response.arp_saddr_ip = target_ip
53
+ end
54
+ timeout += 0.1
55
+ sleep 0.1 # Check for a response ten times per second.
56
+ end
57
+ target_mac
58
+ end # cap_thread
59
+ cap_thread.value
60
+ end
61
+
62
+ # Since 177/8 is IANA reserved (for now), this network should
63
+ # be handled by your default gateway and default interface.
64
+ def self.rand_routable_daddr
65
+ IPAddr.new((rand(16777216) + 2969567232), Socket::AF_INET)
66
+ end
67
+
68
+ # A helper for getting a random port number
69
+ def self.rand_port
70
+ rand(0xffff-1024)+1024
71
+ end
72
+
73
+ # Discovers the local IP and Ethernet address, which is useful for writing
74
+ # packets you expect to get a response to. Note, this is a noisy
75
+ # operation; a UDP packet is generated and dropped on to the default (or named)
76
+ # interface, and then captured (which means you need to be root to do this).
77
+ #
78
+ # whoami? returns a hash of :eth_saddr, :eth_src, :ip_saddr, :ip_src,
79
+ # :ip_src_bin, :eth_dst, and :eth_daddr (the last two are usually suitable
80
+ # for a gateway mac address). It's most useful as an argument to
81
+ # PacketFu::Config.new, or as an argument to the many Packet constructors.
82
+ #
83
+ # Note that if you have multiple interfaces with the same route (such as when
84
+ # wlan0 and eth0 are associated to the same network), the "first" one
85
+ # according to Pcap.lookupdev will be used, regardless of which :iface you
86
+ # pick.
87
+ #
88
+ # === Parameters
89
+ # :iface => "eth0"
90
+ # An interface to listen for packets on. Note that since we rely on the OS to send the probe packet,
91
+ # you will need to specify a target which will use this interface.
92
+ # :target => "1.2.3.4"
93
+ # A target IP address. By default, a packet will be sent to a random address in the 177/8 network.
94
+ def self.whoami?(args={})
95
+ unless args.kind_of? Hash
96
+ raise ArgumentError, "Argument to `whoami?' must be a Hash"
97
+ end
98
+ if args[:iface].to_s =~ /^lo/ # Linux loopback more or less. Need a switch for windows loopback, too.
99
+ dst_host = "127.0.0.1"
100
+ else
101
+ dst_host = (args[:target] || rand_routable_daddr.to_s)
102
+ end
103
+
104
+ dst_port = rand_port
105
+ msg = "PacketFu whoami? packet #{(Time.now.to_i + rand(0xffffff)+1)}"
106
+ iface = (args[:iface] || ENV['IFACE'] || default_int || :lo ).to_s
107
+ cap = PacketFu::Capture.new(:iface => iface, :promisc => false, :start => true, :filter => "udp and dst host #{dst_host} and dst port #{dst_port}")
108
+ udp_sock = UDPSocket.new
109
+ udp_sock.send(msg,0,dst_host,dst_port)
110
+ udp_sock = nil
111
+
112
+ my_data = nil
113
+
114
+ begin
115
+ Timeout::timeout(1) {
116
+ pkt = nil
117
+
118
+ while pkt.nil?
119
+ raw_pkt = cap.next
120
+ next if raw_pkt.nil?
121
+
122
+ pkt = Packet.parse(raw_pkt)
123
+
124
+ if pkt.payload == msg
125
+
126
+ my_data = {
127
+ :iface => (args[:iface] || ENV['IFACE'] || default_int || "lo").to_s,
128
+ :pcapfile => args[:pcapfile] || "/tmp/out.pcap",
129
+ :eth_saddr => pkt.eth_saddr,
130
+ :eth_src => pkt.eth_src.to_s,
131
+ :ip_saddr => pkt.ip_saddr,
132
+ :ip_src => pkt.ip_src,
133
+ :ip_src_bin => [pkt.ip_src].pack("N"),
134
+ :eth_dst => pkt.eth_dst.to_s,
135
+ :eth_daddr => pkt.eth_daddr
136
+ }
137
+
138
+ else raise SecurityError,
139
+ "whoami() packet doesn't match sent data. Something fishy's going on."
140
+ end
141
+
142
+ end
143
+ }
144
+ rescue Timeout::Error
145
+ raise SocketError, "Didn't receive the whoami() packet, can't automatically configure."
146
+ end
147
+
148
+ my_data
149
+ end
150
+
151
+ # Determine the default ip address
152
+ def self.default_ip
153
+ begin
154
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
155
+
156
+ UDPSocket.open do |s|
157
+ s.connect rand_routable_daddr.to_s, rand_port
158
+ s.addr.last
159
+ end
160
+ ensure
161
+ Socket.do_not_reverse_lookup = orig
162
+ end
163
+ end
164
+
165
+ # Determine the default routeable interface
166
+ def self.default_int
167
+ ip = default_ip
168
+
169
+ NetworkInterface.interfaces.each do |interface|
170
+ NetworkInterface.addresses(interface).values.each do |addresses|
171
+ addresses.each do |address|
172
+ next if address["addr"].nil?
173
+ return interface if address["addr"] == ip
174
+ end
175
+ end
176
+ end
177
+
178
+ # Fall back to libpcap as last resort
179
+ return Pcap.lookupdev
180
+ end
181
+
182
+ # Determine the ifconfig data string for a given interface
183
+ def self.ifconfig_data_string(iface=default_int)
184
+ # Make sure to only get interface data for a real interface
185
+ unless NetworkInterface.interfaces.include?(iface)
186
+ raise ArgumentError, "#{iface} interface does not exist"
187
+ end
188
+ return %x[ifconfig #{iface}]
189
+ end
190
+
191
+ # Handles ifconfig for various (okay, two) platforms.
192
+ # Will have Windows done shortly.
193
+ #
194
+ # Takes an argument (either string or symbol) of the interface to look up, and
195
+ # returns a hash which contains at least the :iface element, and if configured,
196
+ # these additional elements:
197
+ #
198
+ # :eth_saddr # A human readable MAC address
199
+ # :eth_src # A packed MAC address
200
+ # :ip_saddr # A dotted-quad string IPv4 address
201
+ # :ip_src # A packed IPv4 address
202
+ # :ip4_obj # An IPAddr object with bitmask
203
+ # :ip6_saddr # A colon-delimited hex IPv6 address, with bitmask
204
+ # :ip6_obj # An IPAddr object with bitmask
205
+ #
206
+ # === Example
207
+ # PacketFu::Utils.ifconfig :wlan0 # Not associated yet
208
+ # #=> {:eth_saddr=>"00:1d:e0:73:9d:ff", :eth_src=>"\000\035\340s\235\377", :iface=>"wlan0"}
209
+ # PacketFu::Utils.ifconfig("eth0") # Takes 'eth0' as default
210
+ # #=> {:eth_saddr=>"00:1c:23:35:70:3b", :eth_src=>"\000\034#5p;", :ip_saddr=>"10.10.10.9", :ip4_obj=>#<IPAddr: IPv4:10.10.10.0/255.255.254.0>, :ip_src=>"\n\n\n\t", :iface=>"eth0", :ip6_saddr=>"fe80::21c:23ff:fe35:703b/64", :ip6_obj=>#<IPAddr: IPv6:fe80:0000:0000:0000:0000:0000:0000:0000/ffff:ffff:ffff:ffff:0000:0000:0000:0000>}
211
+ # PacketFu::Utils.ifconfig :lo
212
+ # #=> {:ip_saddr=>"127.0.0.1", :ip4_obj=>#<IPAddr: IPv4:127.0.0.0/255.0.0.0>, :ip_src=>"\177\000\000\001", :iface=>"lo", :ip6_saddr=>"::1/128", :ip6_obj=>#<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>}
213
+ def self.ifconfig(iface=default_int)
214
+ ret = {}
215
+ iface = iface.to_s.scan(/[0-9A-Za-z]/).join # Sanitizing input, no spaces, semicolons, etc.
216
+ case RUBY_PLATFORM
217
+ when /linux/i
218
+ ifconfig_data = ifconfig_data_string(iface)
219
+ if ifconfig_data =~ /#{iface}/i
220
+ ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
221
+ else
222
+ raise ArgumentError, "Cannot ifconfig #{iface}"
223
+ end
224
+ real_iface = ifconfig_data.first
225
+ ret[:iface] = real_iface.split.first.downcase
226
+ if real_iface =~ /[\s]HWaddr[\s]+([0-9a-fA-F:]{17})/i
227
+ ret[:eth_saddr] = $1.downcase
228
+ ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
229
+ end
230
+ ifconfig_data.each do |s|
231
+ case s
232
+ when /inet addr:[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
233
+ ret[:ip_saddr] = $1
234
+ ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
235
+ ret[:ip4_obj] = IPAddr.new($1)
236
+ ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
237
+ when /inet6 addr:[\s]*([0-9a-fA-F:\x2f]+)/
238
+ ret[:ip6_saddr] = $1
239
+ ret[:ip6_obj] = IPAddr.new($1)
240
+ end
241
+ end # linux
242
+ when /darwin/i
243
+ ifconfig_data = ifconfig_data_string(iface)
244
+ if ifconfig_data =~ /#{iface}/i
245
+ ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
246
+ else
247
+ raise ArgumentError, "Cannot ifconfig #{iface}"
248
+ end
249
+ ret[:iface] = iface
250
+ ifconfig_data.each do |s|
251
+ case s
252
+ when /ether[\s]([0-9a-fA-F:]{17})/i
253
+ ret[:eth_saddr] = $1
254
+ ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
255
+ when /inet[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask[\s]+(0x[a-f0-9]+))?/i
256
+ imask = 0
257
+ if $3
258
+ imask = $3.to_i(16).to_s(2).count("1")
259
+ end
260
+
261
+ ret[:ip_saddr] = $1
262
+ ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
263
+ ret[:ip4_obj] = IPAddr.new($1)
264
+ ret[:ip4_obj] = ret[:ip4_obj].mask(imask) if imask
265
+ when /inet6[\s]*([0-9a-fA-F:\x2f]+)/
266
+ ret[:ip6_saddr] = $1
267
+ ret[:ip6_obj] = IPAddr.new($1)
268
+ end
269
+ end # darwin
270
+ when /freebsd/i
271
+ ifconfig_data = ifconfig_data_string(iface)
272
+ if ifconfig_data =~ /#{iface}/
273
+ ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
274
+ else
275
+ raise ArgumentError, "Cannot ifconfig #{iface}"
276
+ end
277
+ ret[:iface] = iface
278
+ ifconfig_data.each do |s|
279
+ case s
280
+ when /ether[\s]*([0-9a-fA-F:]{17})/
281
+ ret[:eth_saddr] = $1.downcase
282
+ ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
283
+ when /inet[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*netmask[\s]*(0x[0-9a-fA-F]{8}))?/
284
+ ret[:ip_saddr] = $1
285
+ ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
286
+ ret[:ip4_obj] = IPAddr.new($1)
287
+ ret[:ip4_obj] = ret[:ip4_obj].mask(($3.hex.to_s(2) =~ /0*$/)) if $3
288
+ when /inet6[\s]*([0-9a-fA-F:\x2f]+)/
289
+ ret[:ip6_saddr] = $1
290
+ ret[:ip6_obj] = IPAddr.new($1)
291
+ end
292
+ end # freebsd
293
+ end # RUBY_PLATFORM
294
+ ret
295
+ end
296
+
297
+ end
231
298
 
232
299
  end
233
300