bettercap 1.6.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -34
  3. data/bin/bettercap +1 -1
  4. data/lib/bettercap/context.rb +1 -1
  5. data/lib/bettercap/discovery/agents/mdns.rb +61 -0
  6. data/lib/bettercap/discovery/agents/upnp.rb +60 -0
  7. data/lib/bettercap/discovery/agents/wsd.rb +75 -0
  8. data/lib/bettercap/firewalls/linux.rb +0 -4
  9. data/lib/bettercap/logger.rb +63 -34
  10. data/lib/bettercap/network/network.rb +1 -1
  11. data/lib/bettercap/options/core_options.rb +1 -1
  12. data/lib/bettercap/proxy/http/modules/redirect.rb +1 -1
  13. data/lib/bettercap/proxy/http/proxy.rb +1 -9
  14. data/lib/bettercap/proxy/http/sslstrip/strip.rb +5 -5
  15. data/lib/bettercap/sniffer/parsers/asterisk.rb +37 -0
  16. data/lib/bettercap/sniffer/parsers/bfd.rb +159 -0
  17. data/lib/bettercap/sniffer/parsers/dhcp.rb +23 -23
  18. data/lib/bettercap/sniffer/parsers/dict.rb +13 -11
  19. data/lib/bettercap/sniffer/parsers/hsrp.rb +262 -0
  20. data/lib/bettercap/sniffer/parsers/https.rb +17 -19
  21. data/lib/bettercap/sniffer/parsers/mpd.rb +12 -10
  22. data/lib/bettercap/sniffer/parsers/nntp.rb +5 -1
  23. data/lib/bettercap/sniffer/parsers/post.rb +8 -9
  24. data/lib/bettercap/sniffer/parsers/radius.rb +410 -0
  25. data/lib/bettercap/sniffer/parsers/redis.rb +15 -13
  26. data/lib/bettercap/sniffer/parsers/rlogin.rb +20 -19
  27. data/lib/bettercap/sniffer/parsers/snmp.rb +16 -17
  28. data/lib/bettercap/sniffer/parsers/snpp.rb +13 -11
  29. data/lib/bettercap/sniffer/parsers/teamtalk.rb +41 -0
  30. data/lib/bettercap/sniffer/parsers/teamviewer.rb +8 -8
  31. data/lib/bettercap/sniffer/parsers/url.rb +6 -6
  32. data/lib/bettercap/sniffer/parsers/whatsapp.rb +6 -7
  33. data/lib/bettercap/sniffer/parsers/wol.rb +68 -0
  34. data/lib/bettercap/spoofers/arp.rb +3 -3
  35. data/lib/bettercap/spoofers/hsrp.rb +351 -0
  36. data/lib/bettercap/spoofers/mac.rb +126 -0
  37. data/lib/bettercap/version.rb +1 -1
  38. metadata +13 -2
@@ -19,27 +19,25 @@ class Https < Base
19
19
  @@lock = Mutex.new
20
20
 
21
21
  def on_packet( pkt )
22
- begin
23
- # poor man's TLS Client Hello with SNI extension parser :P
24
- if pkt.respond_to?(:tcp_dst) and \
25
- pkt.payload[0] == "\x16" and \
26
- pkt.payload[1] == "\x03" and \
27
- pkt.payload =~ /\x00\x00.{4}\x00.{2}([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6})\x00/
28
- hostname = $1
29
- if pkt.tcp_dst != 443
30
- hostname += ":#{pkt.tcp_dst}"
31
- end
32
-
33
- @@lock.synchronize {
34
- if @@prev.nil? or @@prev != hostname
35
- StreamLogger.log_raw( pkt, 'HTTPS', "https://#{hostname}/" )
36
- @@prev = hostname
37
- end
38
- }
22
+ # poor man's TLS Client Hello with SNI extension parser :P
23
+ if pkt.respond_to?(:tcp_dst) and \
24
+ pkt.payload[0] == "\x16" and \
25
+ pkt.payload[1] == "\x03" and \
26
+ pkt.payload =~ /\x00\x00.{4}\x00.{2}([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6})\x00/
27
+ hostname = $1
28
+ if pkt.tcp_dst != 443
29
+ hostname += ":#{pkt.tcp_dst}"
39
30
  end
40
- rescue; end
41
- end
42
31
 
32
+ @@lock.synchronize {
33
+ if @@prev.nil? or @@prev != hostname
34
+ StreamLogger.log_raw( pkt, 'HTTPS', "https://#{hostname}/" )
35
+ @@prev = hostname
36
+ end
37
+ }
38
+ end
39
+ rescue
40
+ end
43
41
  end
44
42
  end
45
43
  end
@@ -6,6 +6,10 @@ Author : Simone 'evilsocket' Margaritelli
6
6
  Email : evilsocket@gmail.com
7
7
  Blog : https://www.evilsocket.net/
8
8
 
9
+ Music Player Daemon (MPD) authentication parser:
10
+ Author : Brendan Coles
11
+ Email : bcoles[at]gmail.com
12
+
9
13
  This project is released under the GPL 3 license.
10
14
 
11
15
  =end
@@ -18,18 +22,16 @@ class Mpd < Base
18
22
  @name = 'MPD'
19
23
  end
20
24
  def on_packet( pkt )
21
- begin
22
- if pkt.tcp_dst == 6600
23
- lines = pkt.to_s.split(/\r?\n/)
24
- lines.each do |line|
25
- if line =~ /password\s+(.+)$/
26
- pass = $1
27
- StreamLogger.log_raw( pkt, @name, "password=#{pass}" )
28
- end
29
- end
25
+ return unless pkt.tcp_dst == 6600
26
+
27
+ lines = pkt.to_s.split(/\r?\n/)
28
+ lines.each do |line|
29
+ if line =~ /password\s+(.+)$/
30
+ pass = $1
31
+ StreamLogger.log_raw( pkt, @name, "password=#{pass}" )
30
32
  end
31
- rescue
32
33
  end
34
+ rescue
33
35
  end
34
36
  end
35
37
  end
@@ -5,7 +5,11 @@ BETTERCAP
5
5
 
6
6
  Author : Simone 'evilsocket' Margaritelli
7
7
  Email : evilsocket@gmail.com
8
- Blog : http://www.evilsocket.net/
8
+ Blog : https://www.evilsocket.net/
9
+
10
+ NNTP authentication parser:
11
+ Author : Brendan Coles
12
+ Email : bcoles[at]gmail.com
9
13
 
10
14
  This project is released under the GPL 3 license.
11
15
 
@@ -17,16 +17,15 @@ module Parsers
17
17
  class Post < Base
18
18
  def on_packet( pkt )
19
19
  s = pkt.to_s
20
- if s =~ /POST\s+[^\s]+\s+HTTP.+/
21
- begin
22
- req = BetterCap::Proxy::HTTP::Request.parse(pkt.payload)
23
- # the packet could be incomplete
24
- unless req.body.nil? or req.body.empty?
25
- StreamLogger.log_raw( pkt, "POST", req.to_url(1000) )
26
- StreamLogger.log_post( req )
27
- end
28
- rescue; end
20
+ return unless s =~ /POST\s+[^\s]+\s+HTTP.+/
21
+
22
+ req = BetterCap::Proxy::HTTP::Request.parse(pkt.payload)
23
+ # the packet could be incomplete
24
+ unless req.body.nil? or req.body.empty?
25
+ StreamLogger.log_raw( pkt, "POST", req.to_url(1000) )
26
+ StreamLogger.log_post( req )
29
27
  end
28
+ rescue
30
29
  end
31
30
  end
32
31
  end
@@ -0,0 +1,410 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+
4
+ BETTERCAP
5
+
6
+ Author : Simone 'evilsocket' Margaritelli
7
+ Email : evilsocket@gmail.com
8
+ Blog : https://www.evilsocket.net/
9
+
10
+ RADIUS packet and authentication parser:
11
+ Author : Brendan Coles
12
+ Email : bcoles[at]gmail.com
13
+
14
+ This project is released under the GPL 3 license.
15
+
16
+ =end
17
+
18
+ module BetterCap
19
+ module Parsers
20
+ #
21
+ # RADIUS packet and authentication parser.
22
+ #
23
+ # Supports RADIUS authentication messages over UDP (ports 1812/udp and 1645/udp)
24
+ # Does not support RADIUS over TCP
25
+ # Does not support RADIUS accounting messages (ports 1813/udp and 1646/udp)
26
+ #
27
+ # References:
28
+ # - https://en.wikipedia.org/wiki/RADIUS
29
+ # - https://tools.ietf.org/html/rfc2865#section-3
30
+ # - https://tools.ietf.org/html/rfc2869
31
+ # - https://www.iana.org/assignments/radius-types/radius-types.xhtml
32
+ # - https://technet.microsoft.com/en-us/library/cc958030.aspx
33
+ # - http://www.untruth.org/~josh/security/radius/radius-auth.html
34
+ #
35
+ class Radius < Base
36
+ def initialize
37
+ @name = 'RADIUS'
38
+ end
39
+
40
+ def on_packet( pkt )
41
+ return unless is_radius? pkt
42
+
43
+ log = []
44
+ data = pkt.payload.to_s.unpack('H*').first
45
+
46
+ =begin
47
+
48
+ Packet format from RFC2865, section 3:
49
+
50
+ 0 1 2 3
51
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
52
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53
+ | Code | Identifier | Length |
54
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55
+ | |
56
+ | Authenticator |
57
+ | |
58
+ | |
59
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60
+ | Attributes ...
61
+ +-+-+-+-+-+-+-+-+-+-+-+-+-
62
+
63
+ =end
64
+
65
+ # data[0...2] # RADIUS Codes (decimal):
66
+ # 1 Access-Request
67
+ # 2 Access-Accept
68
+ # 3 Access-Reject
69
+ # 4 Accounting-Request
70
+ # 5 Accounting-Response
71
+ # 11 Access-Challenge
72
+ # 12 Status-Server (experimental)
73
+ # 13 Status-Client (experimental)
74
+ # 255 Reserved
75
+
76
+ code = data[0...2].to_i(16)
77
+
78
+ # We're only interested in Access requests and responses
79
+ case code
80
+ when 1
81
+ type = 'request'
82
+ when 2
83
+ type = 'accept'
84
+ when 3
85
+ type = 'reject'
86
+ when 11
87
+ type = 'challenge'
88
+ else
89
+ return
90
+ end
91
+
92
+ log << "access-#{type}".yellow
93
+ log << "#{'Code'.blue}=#{code}"
94
+
95
+ # data[2...4] # Identifier
96
+ id = data[2...4].to_i(16)
97
+ log << "#{'ID'.blue}=#{id}"
98
+
99
+ # data[4...8] # Length
100
+ length = data[4...8].to_i(16)
101
+
102
+ # data[4...36] # Request/Response Authenticator
103
+ authenticator = data[8...40]
104
+ log << "#{'Authenticator'.blue}=#{authenticator}"
105
+
106
+
107
+ =begin
108
+
109
+ RADIUS attribute format from RFC2865, section 5:
110
+
111
+ 0 1 2
112
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
113
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
114
+ | Type | Length | Value ...
115
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
116
+
117
+ =end
118
+
119
+ # Attribute type label lookup table
120
+ # https://www.iana.org/assignments/radius-types/radius-types.xhtml
121
+ attribute_types = [
122
+ [1, 'User-Name'],
123
+ [2, 'User-Password'],
124
+ [3, 'CHAP-Password'],
125
+ [4, 'NAS-IP-Address'],
126
+ [5, 'NAS-Port'],
127
+ [6, 'Service-Type'],
128
+ [7, 'Framed-Protocol'],
129
+ [8, 'Framed-IP-Address'],
130
+ [9, 'Framed-IP-Netmask'],
131
+ [10, 'Framed-Routing'],
132
+ [11, 'Filter-Id'],
133
+ [12, 'Framed-MTU'],
134
+ [13, 'Framed-Compression'],
135
+ [14, 'Login-IP-Host'],
136
+ [15, 'Login-Service'],
137
+ [16, 'Login-TCP-Port'],
138
+ [17, 'Unassigned'],
139
+ [18, 'Reply-Message'],
140
+ [19, 'Callback-Number'],
141
+ [20, 'Callback-Id'],
142
+ #[21, 'Unassigned'],
143
+ [22, 'Framed-Route'],
144
+ [23, 'Framed-IPX-Network'],
145
+ [24, 'State'],
146
+ [25, 'Class'],
147
+ [26, 'Vendor-Specific'],
148
+ [27, 'Session-Timeout'],
149
+ [28, 'Idle-Timeout'],
150
+ [29, 'Termination-Action'],
151
+ [30, 'Called-Station-Id'],
152
+ [31, 'Calling-Station-Id'],
153
+ [32, 'NAS-Identifier'],
154
+ [33, 'Proxy-State'],
155
+ [34, 'Login-LAT-Service'],
156
+ [35, 'Login-LAT-Node'],
157
+ [36, 'Login-LAT-Group'],
158
+ [37, 'Framed-AppleTalk-Link'],
159
+ [38, 'Framed-AppleTalk-Network'],
160
+ [39, 'Framed-AppleTalk-Zone'],
161
+ [40, 'Acct-Status-Type'],
162
+ [41, 'Acct-Delay-Time'],
163
+ [42, 'Acct-Input-Octets'],
164
+ [43, 'Acct-Output-Octets'],
165
+ [44, 'Acct-Session-Id'],
166
+ [45, 'Acct-Authentic'],
167
+ [46, 'Acct-Session-Time'],
168
+ [47, 'Acct-Input-Packets'],
169
+ [48, 'Acct-Output-Packets'],
170
+ [49, 'Acct-Terminate-Cause'],
171
+ [50, 'Acct-Multi-Session-Id'],
172
+ [51, 'Acct-Link-Count'],
173
+ [52, 'Acct-Input-Gigawords'],
174
+ [53, 'Acct-Output-Gigawords'],
175
+ #[54, 'Unassigned'],
176
+ [55, 'Event-Timestamp'],
177
+ [56, 'Egress-VLANID'],
178
+ [57, 'Ingress-Filters'],
179
+ [58, 'Egress-VLAN-Name'],
180
+ [59, 'User-Priority-Table'],
181
+ [60, 'CHAP-Challenge'],
182
+ [61, 'NAS-Port-Type'],
183
+ [62, 'Port-Limit'],
184
+ [63, 'Login-LAT-Port'],
185
+ [64, 'Tunnel-Type'],
186
+ [65, 'Tunnel-Medium-Type'],
187
+ [66, 'Tunnel-Client-Endpoint'],
188
+ [67, 'Tunnel-Server-Endpoint'],
189
+ [68, 'Acct-Tunnel-Connection'],
190
+ [69, 'Tunnel-Password'],
191
+ [70, 'ARAP-Password'],
192
+ [71, 'ARAP-Features'],
193
+ [72, 'ARAP-Zone-Access'],
194
+ [73, 'ARAP-Security'],
195
+ [74, 'ARAP-Security-Data'],
196
+ [75, 'Password-Retry'],
197
+ [76, 'Prompt'],
198
+ [77, 'Connect-Info'],
199
+ [78, 'Configuration-Token'],
200
+ [79, 'EAP-Message'],
201
+ [80, 'Message-Authenticator'],
202
+ [81, 'Tunnel-Private-Group-ID'],
203
+ [82, 'Tunnel-Assignment-ID'],
204
+ [83, 'Tunnel-Preference'],
205
+ [84, 'ARAP-Challenge-Response'],
206
+ [85, 'Acct-Interim-Interval'],
207
+ [86, 'Acct-Tunnel-Packets-Lost'],
208
+ [87, 'NAS-Port-Id'],
209
+ [88, 'Framed-Pool'],
210
+ [89, 'CUI'],
211
+ [90, 'Tunnel-Client-Auth-ID'],
212
+ [91, 'Tunnel-Server-Auth-ID'],
213
+ [92, 'NAS-Filter-Rule'],
214
+ #[93, 'Unassigned'],
215
+ [94, 'Originating-Line-Info'],
216
+ [95, 'NAS-IPv6-Address'],
217
+ [96, 'Framed-Interface-Id'],
218
+ [97, 'Framed-IPv6-Prefix'],
219
+ [98, 'Login-IPv6-Host'],
220
+ [99, 'Framed-IPv6-Route'],
221
+ [100, 'Framed-IPv6-Pool'],
222
+ [101, 'Error-Cause Attribute'],
223
+ [102, 'EAP-Key-Name'],
224
+ [103, 'Digest-Response'],
225
+ [104, 'Digest-Realm'],
226
+ [105, 'Digest-Nonce'],
227
+ [106, 'Digest-Response-Auth'],
228
+ [107, 'Digest-Nextnonce'],
229
+ [108, 'Digest-Method'],
230
+ [109, 'Digest-URI'],
231
+ [110, 'Digest-Qop'],
232
+ [111, 'Digest-Algorithm'],
233
+ [112, 'Digest-Entity-Body-Hash'],
234
+ [113, 'Digest-CNonce'],
235
+ [114, 'Digest-Nonce-Count'],
236
+ [115, 'Digest-Username'],
237
+ [116, 'Digest-Opaque'],
238
+ [117, 'Digest-Auth-Param'],
239
+ [118, 'Digest-AKA-Auts'],
240
+ [119, 'Digest-Domain'],
241
+ [120, 'Digest-Stale'],
242
+ [121, 'Digest-HA1'],
243
+ [122, 'SIP-AOR'],
244
+ [123, 'Delegated-IPv6-Prefix'],
245
+ [124, 'MIP6-Feature-Vector'],
246
+ [125, 'MIP6-Home-Link-Prefix'],
247
+ [126, 'Operator-Name'],
248
+ [127, 'Location-Information'],
249
+ [128, 'Location-Data'],
250
+ [129, 'Basic-Location-Policy-Rules'],
251
+ [130, 'Extended-Location-Policy-Rules'],
252
+ [131, 'Location-Capable'],
253
+ [132, 'Requested-Location-Info'],
254
+ [133, 'Framed-Management-Protocol'],
255
+ [134, 'Management-Transport-Protection'],
256
+ [135, 'Management-Policy-Id'],
257
+ [136, 'Management-Privilege-Level'],
258
+ [137, 'PKM-SS-Cert'],
259
+ [138, 'PKM-CA-Cert'],
260
+ [139, 'PKM-Config-Settings'],
261
+ [140, 'PKM-Cryptosuite-List'],
262
+ [141, 'PKM-SAID'],
263
+ [142, 'PKM-SA-Descriptor'],
264
+ [143, 'PKM-Auth-Key'],
265
+ [144, 'DS-Lite-Tunnel-Name'],
266
+ [145, 'Mobile-Node-Identifier'],
267
+ [146, 'Service-Selection'],
268
+ [147, 'PMIP6-Home-LMA-IPv6-Address'],
269
+ [148, 'PMIP6-Visited-LMA-IPv6-Address'],
270
+ [149, 'PMIP6-Home-LMA-IPv4-Address'],
271
+ [150, 'PMIP6-Visited-LMA-IPv4-Address'],
272
+ [151, 'PMIP6-Home-HN-Prefix'],
273
+ [152, 'PMIP6-Visited-HN-Prefix'],
274
+ [153, 'PMIP6-Home-Interface-ID'],
275
+ [154, 'PMIP6-Visited-Interface-ID'],
276
+ [155, 'PMIP6-Home-IPv4-HoA'],
277
+ [156, 'PMIP6-Visited-IPv4-HoA'],
278
+ [157, 'PMIP6-Home-DHCP4-Server-Address'],
279
+ [158, 'PMIP6-Visited-DHCP4-Server-Address'],
280
+ [159, 'PMIP6-Home-DHCP6-Server-Address'],
281
+ [160, 'PMIP6-Visited-DHCP6-Server-Address'],
282
+ [161, 'PMIP6-Home-IPv4-Gateway'],
283
+ [162, 'PMIP6-Visited-IPv4-Gateway'],
284
+ [163, 'EAP-Lower-Layer'],
285
+ [164, 'GSS-Acceptor-Service-Name'],
286
+ [165, 'GSS-Acceptor-Host-Name'],
287
+ [166, 'GSS-Acceptor-Service-Specifics'],
288
+ [167, 'GSS-Acceptor-Realm-Name'],
289
+ [168, 'Framed-IPv6-Address'],
290
+ [169, 'DNS-Server-IPv6-Address'],
291
+ [170, 'Route-IPv6-Information'],
292
+ [171, 'Delegated-IPv6-Prefix-Pool'],
293
+ [172, 'Stateful-IPv6-Address-Pool'],
294
+ [173, 'IPv6-6rd-Configuration'],
295
+ [174, 'Allowed-Called-Station-Id'],
296
+ [175, 'EAP-Peer-Id'],
297
+ [176, 'EAP-Server-Id'],
298
+ [177, 'Mobility-Domain-Id'],
299
+ [178, 'Preauth-Timeout'],
300
+ [179, 'Network-Id-Name'],
301
+ [180, 'EAPoL-Announcement'],
302
+ [181, 'WLAN-HESSID'],
303
+ [182, 'WLAN-Venue-Info'],
304
+ [183, 'WLAN-Venue-Language'],
305
+ [184, 'WLAN-Venue-Name'],
306
+ [185, 'WLAN-Reason-Code'],
307
+ [186, 'WLAN-Pairwise-Cipher'],
308
+ [187, 'WLAN-Group-Cipher'],
309
+ [188, 'WLAN-AKM-Suite'],
310
+ [189, 'WLAN-Group-Mgmt-Cipher'],
311
+ [190, 'WLAN-RF-Band'],
312
+ #[191, 'Unassigned'],
313
+ #[192-223, 'Experimental Use'],
314
+ #[224-240, 'Implementation Specific'],
315
+ #[241, 'Extended-Attribute-1'],
316
+ #[241.1, 'Frag-Status'],
317
+ #[241.2, 'Proxy-State-Length'],
318
+ #[241.3, 'Response-Length'],
319
+ #[241.4, 'Original-Packet-Code'],
320
+ #[241.5, 'IP-Port-Limit-Info'],
321
+ #[241.6, 'IP-Port-Range'],
322
+ #[241.7, 'IP-Port-Forwarding-Map'],
323
+ #[241.{8-25}, 'Unassigned'],
324
+ #[241.26, 'Extended-Vendor-Specific-1'],
325
+ #[241.{27-240}, 'Unassigned'],
326
+ #[241.{241-255}, 'Reserved'],
327
+ #[242, 'Extended-Attribute-2'],
328
+ #[242.{1-25}, 'Unassigned'],
329
+ #[242.26, 'Extended-Vendor-Specific-2'],
330
+ #[242.{27-240}, 'Unassigned'],
331
+ #[242.{241-255}, 'Reserved'],
332
+ #[243, 'Extended-Attribute-3'],
333
+ #[243.{1-25}, 'Unassigned'],
334
+ #[243.26, 'Extended-Vendor-Specific-3'],
335
+ #[243.{27-240}, 'Unassigned'],
336
+ #[243.{241-255}, 'Reserved'],
337
+ #[244, 'Extended-Attribute-4'],
338
+ #[244.{1-25}, 'Unassigned'],
339
+ #[244.26, 'Extended-Vendor-Specific-4'],
340
+ #[244.{27-240}, 'Unassigned'],
341
+ #[244.{241-255}, 'Reserved'],
342
+ #[245, 'Extended-Attribute-5'],
343
+ #[245.1, 'SAML-Assertion'],
344
+ #[245.2, 'SAML-Protocol'],
345
+ #[245.{3-25}, 'Unassigned'],
346
+ #[245.26, 'Extended-Vendor-Specific-5'],
347
+ #[245.{27-240}, 'Unassigned'],
348
+ #[245.{241-255}, 'Reserved'],
349
+ #[246, 'Extended-Attribute-6'],
350
+ #[246.{1-25}, 'Unassigned'],
351
+ #[246.26, 'Extended-Vendor-Specific-6'],
352
+ #[246.{27-240}, 'Unassigned'],
353
+ #[246.{241-255}, 'Reserved'],
354
+ #[247-255, 'Reserved']
355
+ ]
356
+
357
+ # data[40..-1] # RADIUS Attributes
358
+ attributes = data[40..(length * 2)]
359
+
360
+ attributes_hash = []
361
+ start = 0
362
+ while start < attributes.length
363
+ # attributes[0...2] # Attribute type
364
+ att_type = attributes[start...(start + 2)].to_i(16)
365
+
366
+ # attributes[2...4] # Attribute length (including type, length and value fields)
367
+ att_length = attributes[(start + 2)...(start + 4)].to_i(16)
368
+
369
+ # Break if attribute is malformed
370
+ break if att_length == 0
371
+
372
+ # attributes[4...??] # Attribute value
373
+ att_value = attributes[(start + 4)...(start + (att_length * 2))]
374
+
375
+ attributes_hash << [att_type, att_value]
376
+ start = start + (att_length * 2)
377
+ end
378
+
379
+ attributes_hash.each do |type,value|
380
+ # Lookup attribute label
381
+ att_label = attribute_types.select {|id,name| id == type}.flatten[1].to_s
382
+ att_label = "(#{type})" if att_label == ''
383
+
384
+ # Display printable ASCII, where possible
385
+ if [value].pack('H*').to_s =~ /\A[a-zA-Z0-9_\-+\.,"' ]*\z/
386
+ log << "#{att_label.blue}='#{[value].pack('H*').yellow}'"
387
+ # Convert values containing IPv4 addresses to dotted decimal
388
+ elsif value.length == 8 && att_label =~ /IP/
389
+ ip = value.scan(/\w{2}/).map{ |c| c.to_i(16).to_s(10) }.join('.')
390
+ log << "#{att_label.blue}='#{ip.yellow}'"
391
+ else
392
+ log << "#{att_label.blue}=#{value}"
393
+ end
394
+ end
395
+
396
+ StreamLogger.log_raw pkt, @name, log.join(' ')
397
+ rescue
398
+ end
399
+
400
+ private
401
+
402
+ def is_radius?(pkt)
403
+ return ( pkt.respond_to?('udp_src') && pkt.respond_to?('udp_dst') && \
404
+ ( pkt.udp_src == 1812 || pkt.udp_dst == 1812 || \
405
+ pkt.udp_src == 1645 || pkt.udp_dst == 1645 ) && \
406
+ pkt.payload.length >= 20 )
407
+ end
408
+ end
409
+ end
410
+ end