dap 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4c2529f239c66a0e6c098ec4d920f32385c2051e
4
+ data.tar.gz: 9ab89eec2980e1ed96004cf2399706590d86f414
5
+ SHA512:
6
+ metadata.gz: 3e5587663578778dde872b500540665fbee3318e388a39e298cc5c01c31548a58d06464b6dea4f70ca4b74b58d1318f51e87c23f4ad20e680c9c30db36b2b67c
7
+ data.tar.gz: 46da39ede0b3f7966ec91cae4e50afb52e5de86a86431c725e9e3661ba20bf1b1d0576f24f2623412451e78987f441ad9e3cfc8953c2cfb6ff84bcc0fba36ec1
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ before_install:
6
+ - sudo apt-get update -qq
7
+ - sudo apt-get install -y libgeoip-dev libgeoip1
8
+ script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ gem 'geoip-c'
9
9
  gem 'recog'
10
10
 
11
11
  group :test do
12
- gem 'rspec', '~> 2.14.1'
13
- gem 'cucumber', '~> 1.3.8'
14
- gem 'aruba', '~> 0.5.3'
12
+ gem 'rspec', '~> 3.1.0'
13
+ gem 'cucumber', '~> 1.3.16'
14
+ gem 'aruba', '~> 0.6.1'
15
15
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- aruba (0.5.4)
4
+ aruba (0.6.1)
5
5
  childprocess (>= 0.3.6)
6
6
  cucumber (>= 1.1.1)
7
7
  rspec-expectations (>= 2.7.0)
@@ -9,7 +9,7 @@ GEM
9
9
  builder (3.2.2)
10
10
  childprocess (0.5.3)
11
11
  ffi (~> 1.0, >= 1.0.11)
12
- cucumber (1.3.15)
12
+ cucumber (1.3.16)
13
13
  builder (>= 2.1.2)
14
14
  diff-lcs (>= 1.1.3)
15
15
  gherkin (~> 2.12)
@@ -20,36 +20,40 @@ GEM
20
20
  geoip-c (0.9.1)
21
21
  gherkin (2.12.2)
22
22
  multi_json (~> 1.3)
23
- htmlentities (4.3.1)
23
+ htmlentities (4.3.2)
24
24
  mini_portile (0.6.0)
25
- multi_json (1.10.0)
25
+ multi_json (1.10.1)
26
26
  multi_test (0.1.1)
27
27
  net-dns (0.8.0)
28
- nokogiri (1.6.2.1)
28
+ nokogiri (1.6.3.1)
29
29
  mini_portile (= 0.6.0)
30
- oj (2.9.0)
31
- recog (0.01)
30
+ oj (2.10.2)
31
+ recog (0.02)
32
32
  nokogiri
33
- rspec (2.14.1)
34
- rspec-core (~> 2.14.0)
35
- rspec-expectations (~> 2.14.0)
36
- rspec-mocks (~> 2.14.0)
37
- rspec-core (2.14.8)
38
- rspec-expectations (2.14.5)
39
- diff-lcs (>= 1.1.3, < 2.0)
40
- rspec-mocks (2.14.6)
33
+ rspec (3.1.0)
34
+ rspec-core (~> 3.1.0)
35
+ rspec-expectations (~> 3.1.0)
36
+ rspec-mocks (~> 3.1.0)
37
+ rspec-core (3.1.1)
38
+ rspec-support (~> 3.1.0)
39
+ rspec-expectations (3.1.0)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.1.0)
42
+ rspec-mocks (3.1.0)
43
+ rspec-support (~> 3.1.0)
44
+ rspec-support (3.1.0)
41
45
 
42
46
  PLATFORMS
43
47
  ruby
44
48
 
45
49
  DEPENDENCIES
46
- aruba (~> 0.5.3)
50
+ aruba (~> 0.6.1)
47
51
  bit-struct
48
- cucumber (~> 1.3.8)
52
+ cucumber (~> 1.3.16)
49
53
  geoip-c
50
54
  htmlentities
51
55
  net-dns
52
56
  nokogiri
53
57
  oj
54
58
  recog
55
- rspec (~> 2.14.1)
59
+ rspec (~> 3.1.0)
data/README.md CHANGED
@@ -12,4 +12,27 @@ DAP depends on GeoIP (http://dev.maxmind.com/geoip/legacy/downloadable/) to be a
12
12
 
13
13
  ## Usage
14
14
 
15
- See [tree/master/samples](/tree/master/samples)
15
+ See [Samples](https://github.com/rapid7/dap/tree/master/samples)
16
+
17
+ ### Quick Setup for GeoIP Lookups
18
+
19
+ ```
20
+ $ git clone https://github.com/rapid7/dap.git
21
+ $ cd dap
22
+ $ gem install bundler
23
+ $ bundle install
24
+ $ sudo bash
25
+ # mkdir -p /var/lib/geoip && cd /var/lib/geoip && wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz && gunzip GeoLiteCity.dat.gz && mv GeoLiteCity.dat geoip.dat
26
+ ```
27
+
28
+ ```
29
+ $ echo 8.8.8.8 | bin/dap + lines + geo_ip line + json
30
+ {"line":"8.8.8.8","line.country_code":"US","line.country_code3":"USA","line.country_name":"United States","line.latitude":"38.0","line.longitude":"-97.0"}
31
+ ```
32
+
33
+ Where dap gets fun is doing transforms, like just grabbing the country code:
34
+ ```
35
+ $ echo 8.8.8.8 | bin/dap + lines + geo_ip line + select line.country_code3 + lines
36
+ USA
37
+ ```
38
+
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new do |t|
5
+ t.pattern = "spec/**/*_spec.rb"
6
+ end
7
+
8
+ require 'yard'
9
+ require 'yard/rake/yardoc_task'
10
+ YARD::Rake::YardocTask.new do |t|
11
+ t.files = ['lib/**/*.rb', '-', 'README.md']
12
+ end
13
+
14
+ require 'cucumber'
15
+ require 'cucumber/rake/task'
16
+
17
+ Cucumber::Rake::Task.new(:features) do |t|
18
+ t.cucumber_opts = "features --format pretty"
19
+ end
20
+
21
+ task :default => [ :spec, :features, :yard ]
22
+
data/data/vulndb.rb ADDED
@@ -0,0 +1,83 @@
1
+
2
+ # Searches contains each of the services, within each service it contains
3
+ # a hash key that will be compared against each of the items in the
4
+ # regex hash, and if a hit is returned the value from the regex is inserted
5
+ # into the hash with the output_key as the key.
6
+ #
7
+ SEARCHES = {
8
+ :upnp => [{
9
+ :hash_key => 'data.upnp_server',
10
+ :output_key => 'vulnerability',
11
+ :regex => {
12
+ /MiniUPnPd\/1\.0([\.\,\-\~\s]|$)/mi => ['CVE-2013-0229'],
13
+ /MiniUPnPd\/1\.[0-3]([\.\,\-\~\s]|$)/mi => ['CVE-2013-0230'],
14
+ /Intel SDK for UPnP devices.*|Portable SDK for UPnP devices(\/?\s*$|\/1\.([0-5]\..*|8\.0.*|(6\.[0-9]|6\.1[0-7])([\.\,\-\~\s]|$)))/mi => ['CVE-2012-5958', 'CVE-2012-5959']
15
+ }
16
+ }],
17
+
18
+ :ipmi => [{
19
+ :hash_key => 'data.ipmi_compat_password',
20
+ :output_key => 'vulnerability',
21
+ :regex => {
22
+ /1/ => ['IPMI-STRAIGHT-PASS'],
23
+ }
24
+ },{
25
+ :hash_key => 'data.ipmi_compat_md2',
26
+ :output_key => 'vulnerability',
27
+ :regex => {
28
+ /1/ => ['IPMI-MD2'],
29
+ }
30
+ },{
31
+ :hash_key => 'data.ipmi_compat_none',
32
+ :output_key => 'vulnerability',
33
+ :regex => {
34
+ /1/ => ['IPMI-NOAUTH'],
35
+ }
36
+ },{
37
+ :hash_key => 'data.ipmi_user_disable_message_auth',
38
+ :output_key => 'vulnerability',
39
+ :regex => {
40
+ /1/ => ['IPMI-PERMSG'],
41
+ }
42
+ },{
43
+ :hash_key => 'data.ipmi_user_disable_user_auth',
44
+ :output_key => 'vulnerability',
45
+ :regex => {
46
+ /1/ => ['IPMI-USRLVL'],
47
+ }
48
+ }],
49
+
50
+ :mssql => [{
51
+ :hash_key => 'data.version.name',
52
+ :output_key => 'vulnerability',
53
+ :cvemap => {
54
+ #['', ''] => ['CVE-2007-5090'],
55
+ ['2000', '-'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232', 'CVE-2008-4110', 'CVE-2008-5416'],
56
+ #['2000', '""'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232'],
57
+ ['2000', 'sp1'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232'],
58
+ ['2000', 'sp2'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232'],
59
+ ['2000', 'sp3'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232'],
60
+ ['2000', 'sp3a'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232'],
61
+ ['2000', 'sp4'] => ['CVE-2008-0085', 'CVE-2008-0086', 'CVE-2008-0106', 'CVE-2008-0107', 'CVE-2012-0158', 'CVE-2012-1856'],
62
+ ['2005', '-'] => ['CVE-2008-5416'],
63
+ ['2005', 'sp1'] => ['CVE-2008-0085', 'CVE-2008-0107'],
64
+ ['2005', 'sp2'] => ['CVE-2007-4814', 'CVE-2007-5348', 'CVE-2008-0085', 'CVE-2008-0086', 'CVE-2008-0106', 'CVE-2008-0107', 'CVE-2008-3012', 'CVE-2008-3013', 'CVE-2008-3014', 'CVE-2008-3015', 'CVE-2009-2500', 'CVE-2009-2501', 'CVE-2009-2502', 'CVE-2009-2503', 'CVE-2009-2504', 'CVE-2009-2518', 'CVE-2009-2528', 'CVE-2009-3126'],
65
+ ['2005', 'sp3'] => ['CVE-2009-2500', 'CVE-2009-2501', 'CVE-2009-2502', 'CVE-2009-2503', 'CVE-2009-2504', 'CVE-2009-2518', 'CVE-2009-2528', 'CVE-2009-3126', 'CVE-2011-1280'],
66
+ ['2005', 'sp4'] => ['CVE-2011-1280', 'CVE-2012-0158', 'CVE-2012-1856', 'CVE-2012-2552'],
67
+ ['2008', 'r2'] => ['CVE-2011-1280', 'CVE-2012-0158', 'CVE-2012-1856'],
68
+ ['2008', 'r2 sp1'] => ['CVE-2012-1856', 'CVE-2012-2552'],
69
+ ['2008', 'r2 sp2'] => ['CVE-2012-1856', 'CVE-2014-4061'],
70
+ ['2008', 'sp1'] => ['CVE-2011-1280'],
71
+ ['2008', 'sp2'] => ['CVE-2011-1280', 'CVE-2012-0158', 'CVE-2012-1856', 'CVE-2012-2552'],
72
+ ['2008', 'sp3'] => ['CVE-2012-0158', 'CVE-2012-1856', 'CVE-2012-2552', 'CVE-2014-4061'],
73
+ ['2012', '-'] => ['CVE-2012-2552'],
74
+ ['2012', 'sp1'] => ['CVE-2014-1820', 'CVE-2014-4061'],
75
+ ['2014', '-'] => ['CVE-2014-1820'],
76
+ ['7.0', '-'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232', 'CVE-2004-1560'],
77
+ ['7.0', 'sp1'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232', 'CVE-2004-1560'],
78
+ ['7.0', 'sp2'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232', 'CVE-2004-1560'],
79
+ ['7.0', 'sp3'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232', 'CVE-2004-1560'],
80
+ ['7.0', 'sp4'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232', 'CVE-2004-1560', 'CVE-2008-0085', 'CVE-2008-0086', 'CVE-2008-0106', 'CVE-2008-0107'],
81
+ }
82
+ }],
83
+ }
data/lib/dap/filter.rb CHANGED
@@ -5,4 +5,5 @@ require 'dap/filter/udp'
5
5
  require 'dap/filter/openssl'
6
6
  require 'dap/filter/names'
7
7
  require 'dap/filter/geoip'
8
- require 'dap/filter/recog'
8
+ require 'dap/filter/recog'
9
+ require 'dap/filter/vulnmatch'
@@ -113,6 +113,7 @@ class FilterDecodeHTTPReply
113
113
 
114
114
  save["http_code"] = $1.to_i
115
115
  save["http_message"] = $2.strip
116
+ save["http_raw_headers"] = {}
116
117
 
117
118
  clen = nil
118
119
 
@@ -149,6 +150,9 @@ class FilterDecodeHTTPReply
149
150
  when /^Content-Length:\s*(.*)/i
150
151
  clen = $1.strip.to_i
151
152
 
153
+ when /^([A-Za-z0-9\-]+):\s*(.*)/i
154
+ save["http_raw_headers"][$1.downcase.strip] = $2.strip
155
+
152
156
  when ""
153
157
  break
154
158
  end
@@ -232,7 +232,7 @@ class FilterSplitComma
232
232
  lines = [ ]
233
233
  self.opts.each_pair do |k,v|
234
234
  if doc.has_key?(k)
235
- doc[k].to_s.split(/m/).each do |line|
235
+ doc[k].to_s.split(/,/).each do |line|
236
236
  lines << doc.merge({ "#{k}.word" => line })
237
237
  end
238
238
  end
@@ -336,5 +336,29 @@ class FilterFieldSplitArray
336
336
  end
337
337
  end
338
338
 
339
+ class FilterFieldArrayJoinComma
340
+ include Base
341
+ def process(doc)
342
+ self.opts.each_pair do |k,v|
343
+ if doc.has_key?(v) and doc[v].respond_to?(:each)
344
+ doc[k] = doc[v].join(",")
345
+ end
346
+ end
347
+ [ doc ]
348
+ end
349
+ end
350
+
351
+ class FilterFieldArrayJoinWhitespace
352
+ include Base
353
+ def process(doc)
354
+ self.opts.each_pair do |k,v|
355
+ if doc.has_key?(v) and doc[v].respond_to?(:each)
356
+ doc[k] = doc[v].join(" ")
357
+ end
358
+ end
359
+ [ doc ]
360
+ end
361
+ end
362
+
339
363
  end
340
364
  end
@@ -10,6 +10,7 @@ require 'dap/proto/dtls'
10
10
  require 'dap/proto/natpmp'
11
11
  require 'dap/proto/wdbrpc'
12
12
  require 'dap/proto/ipmi'
13
+ require 'dap/proto/mssql'
13
14
  require 'dap/utils/oui'
14
15
 
15
16
  #
@@ -26,8 +27,9 @@ class FilterDecodeMDNSSrvReply
26
27
  svcs = r.answer.map {|x| (x.value.to_s) }
27
28
  svcs.delete('')
28
29
  return if not (svcs and svcs.length > 0)
29
- return { "mdns_services" => svc.join(" ") }
30
+ return { "mdns_services" => svcs }
30
31
  rescue ::Exception
32
+ { }
31
33
  end
32
34
  nil
33
35
  end
@@ -75,26 +77,42 @@ end
75
77
  class FilterDecodeWDBRPC_Reply
76
78
  include BaseDecoder
77
79
  def decode(data)
80
+ buff = data.dup
81
+
78
82
  info = {}
79
83
  head = buff.slice!(0,36)
80
- info['agent_ver'] = wdbrpc_decode_str(buff)
81
- info['agent_mtu'] = wdbrpc_decode_int(buff)
82
- info['agent_mod'] = wdbrpc_decode_int(buff)
83
- info['rt_type'] = wdbrpc_decode_int(buff)
84
- info['rt_vers'] = wdbrpc_decode_str(buff)
85
- info['rt_cpu_type'] = wdbrpc_decode_int(buff)
86
- info['rt_has_fpp'] = wdbrpc_decode_bool(buff)
87
- info['rt_has_wp'] = wdbrpc_decode_bool(buff)
88
- info['rt_page_size'] = wdbrpc_decode_int(buff)
89
- info['rt_endian'] = wdbrpc_decode_int(buff)
90
- info['rt_bsp_name'] = wdbrpc_decode_str(buff)
91
- info['rt_bootline'] = wdbrpc_decode_str(buff)
92
- info['rt_membase'] = wdbrpc_decode_int(buff)
93
- info['rt_memsize'] = wdbrpc_decode_int(buff)
94
- info['rt_region_count'] = wdbrpc_decode_int(buff)
95
- info['rt_regions'] = wdbrpc_decode_arr(buff, :int)
96
- info['rt_hostpool_base'] = wdbrpc_decode_int(buff)
97
- info['rt_hostpool_size'] = wdbrpc_decode_int(buff)
84
+ return info unless head.to_s.length == 36
85
+ return unless buff.length > 0
86
+ info['agent_ver'] = Dap::Proto::WDBRPC.wdbrpc_decode_str(buff)
87
+ info['agent_mtu'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
88
+ info['agent_mod'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
89
+ info['rt_type'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
90
+ info['rt_vers'] = Dap::Proto::WDBRPC.wdbrpc_decode_str(buff)
91
+ info['rt_cpu_type'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
92
+ info['rt_has_fpp'] = Dap::Proto::WDBRPC.wdbrpc_decode_bool(buff)
93
+ info['rt_has_wp'] = Dap::Proto::WDBRPC.wdbrpc_decode_bool(buff)
94
+ info['rt_page_size'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
95
+ info['rt_endian'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
96
+ info['rt_bsp_name'] = Dap::Proto::WDBRPC.wdbrpc_decode_str(buff)
97
+ info['rt_bootline'] = Dap::Proto::WDBRPC.wdbrpc_decode_str(buff)
98
+ info['rt_membase'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
99
+ info['rt_memsize'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
100
+ info['rt_region_count'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
101
+ info['rt_regions'] = Dap::Proto::WDBRPC.wdbrpc_decode_arr(buff, :int)
102
+ info['rt_hostpool_base'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
103
+ info['rt_hostpool_size'] = Dap::Proto::WDBRPC.wdbrpc_decode_int(buff)
104
+
105
+ if info['rt_regions']
106
+ info['rt_regions'] = info['rt_regions'].map{|x| x.to_s}.join(" ")
107
+ end
108
+
109
+ nulls = []
110
+ info.each_pair do |k,v|
111
+ nulls << k if v.nil?
112
+ end
113
+
114
+ nulls.each {|k| info.delete(k) }
115
+
98
116
  info
99
117
  end
100
118
  end
@@ -247,6 +265,16 @@ class FilterDecodeMSSQLReply
247
265
  data.scan(/([A-Za-z0-9 \.\-_]+?);(.+?);/).each do | var, val|
248
266
  info["mssql.#{var.encode!( 'UTF-8', invalid: :replace, undef: :replace, replace: '' )}"] = val.encode!( 'UTF-8', invalid: :replace, undef: :replace, replace: '' )
249
267
  end
268
+
269
+ info
270
+ end
271
+ end
272
+
273
+ class FilterDecodeMSSQLVersion
274
+ include BaseDecoder
275
+ def decode(data)
276
+ info = {}
277
+ info["name"] = Dap::Proto::MSSQL::version_num_to_name(data)
250
278
  info
251
279
  end
252
280
  end
@@ -258,9 +286,9 @@ class FilterDecodeSIPOptionsReply
258
286
  include BaseDecoder
259
287
  def decode(data)
260
288
  info = {}
261
-
289
+
262
290
  return info unless (data and data.length > 0)
263
-
291
+
264
292
  head,body = data.to_s.split(/\r?\n\r?\n/, 2)
265
293
 
266
294
  head.split(/\r?\n/).each do |line|
@@ -305,6 +333,105 @@ class FilterDecodeDTLS
305
333
  end
306
334
  end
307
335
 
336
+ #
337
+ # Decode a BACnet Read Property Multiple reply
338
+ #
339
+ class FilterDecodeBacnetRPMReply
340
+ include BaseDecoder
341
+ TAG_TYPE_LENGTHS = {
342
+ 2 => 2,
343
+ 10 => 4,
344
+ 11 => 4,
345
+ }
346
+ def decode(sdata)
347
+ info = {}
348
+ return if sdata.length < 9
349
+
350
+ data = sdata.dup
351
+
352
+ bacnet_vlc_type, bacnet_vlc_function, bacnet_vlc_length = data.slice!(0,4).unpack("CCn")
353
+ # if this isn't a BACnet/IP (0x81) original unicast NPDU (0x0a), abort
354
+ if bacnet_vlc_type != 0x81 || bacnet_vlc_function != 0x0a
355
+ return info
356
+ else
357
+ info['bacnet_vlc_type'] = bacnet_vlc_type
358
+ info['bacnet_vlc_function'] = bacnet_vlc_function
359
+ info['bacnet_vlc_length'] = bacnet_vlc_length
360
+ end
361
+
362
+ # we only know how to decode version 1, so abort if it is anything else
363
+ # but store the version in the event that we want to parse these later
364
+ bacnet_npdu_version, bacnet_npdu_control = data.slice!(0,2).unpack("CC")
365
+ info['bacnet_npdu_version'] = bacnet_npdu_version
366
+ info['bacnet_npdu_control'] = bacnet_npdu_control
367
+ return info if bacnet_npdu_version != 1
368
+
369
+ bacnet_apdu_type_flags, bacnet_apdu_invoke_id, bacnet_apdu_service_choice = data.slice!(0,3).unpack("CCC")
370
+ bacnet_apdu_type = bacnet_apdu_type_flags >> 4
371
+ bacnet_apdu_flags = bacnet_apdu_type_flags & 0b00001111
372
+ info['bacnet_apdu_type'] = bacnet_apdu_type
373
+ info['bacnet_apdu_flags'] = bacnet_apdu_flags
374
+ info['bacnet_apdu_invoke_id'] = bacnet_apdu_invoke_id
375
+ info['bacnet_apdu_service_choice'] = bacnet_apdu_service_choice
376
+ return info unless (bacnet_apdu_type == 3 && bacnet_apdu_service_choice == 14)
377
+
378
+ return info unless data.size > 5
379
+ # XXX: don't know what to do with this right now
380
+ bacnet_object_id = data.slice!(0,5)
381
+ return info unless data.slice!(0,1).unpack('C').first == 0x1e
382
+ props = {}
383
+ # XXX: I think this is ASN.1, but still need to confirm
384
+ while (true) do
385
+ #puts "size is #{data.size}, data is #{data.each_byte.map { |b| b.to_s(16) }.join(' ')}"
386
+ break if data.size < 4
387
+ property_tag, property_id = data.slice!(0,2).unpack('CC')
388
+ props[property_id] = true
389
+ # slice off the opening tag
390
+ otag = data.slice!(0,1).unpack('C').first
391
+ if otag == 0x5e
392
+ data.slice!(0,5)
393
+ #puts "Property #{property_id} unknown"
394
+ props[property_id] = nil
395
+ else
396
+ # it isn't clear if the length is one byte wide followed by one byte of
397
+ # 0x00 for spacing or if it is two bytes little endian. Looks like the later.
398
+ # XXX?
399
+ tag_flags = data.slice!(0,1).unpack('C').first
400
+ tag_type = tag_flags >> 4
401
+ if TAG_TYPE_LENGTHS.key?(tag_type)
402
+ #puts "Know how to handle property #{property_id}'s tag type #{tag_type}"
403
+ props[property_id] = data.slice!(0, TAG_TYPE_LENGTHS[tag_type])
404
+ else
405
+ if tag_type == 7
406
+ property_length = data.slice!(0,2).unpack('v').first
407
+ #puts "Handled property #{property_id}'s #{property_length}-byte tag type #{tag_type}"
408
+ property_length -= 1
409
+ # handle String
410
+ props[property_id] = data.slice!(0, property_length)
411
+ else
412
+ #puts "Don't know how to handle property #{property_id}'s tag type #{tag_type}"
413
+ end
414
+ end
415
+
416
+ ctag = data.slice!(0,1).unpack('C')
417
+ end
418
+ if data.size == 0
419
+ #puts "done"
420
+ break
421
+ else
422
+ #puts "going"
423
+ end
424
+ end
425
+
426
+ props.each do |k,v|
427
+ info[k] = v
428
+ end
429
+
430
+
431
+ info
432
+ end
433
+ end
434
+
308
435
  #
309
436
  # Decode a NTP reply
310
437
  #
@@ -324,31 +451,28 @@ class FilterDecodeNTPReply
324
451
  ntp_flags = data.slice!(0,1).unpack('C').first
325
452
  ntp_version = (ntp_flags & 0b00111000) >> 3
326
453
  info['ntp.version'] = ntp_version
454
+ info['ntp.mode'] = (ntp_flags & 0b00000111)
327
455
 
328
456
  # NTP 2 & 3 share a common header, so parse those together
329
457
  if ntp_version == 2 || ntp_version == 3
330
- # 0 1 2 3
331
- # 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
332
- # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
333
- # |R|M| VN | Mode|A| Sequence | Implementation| Req Code |
334
- # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
335
-
336
- info['ntp.response'] = ntp_flags >> 7
337
- info['ntp.more'] = (ntp_flags & 0b01000000) >> 6
338
- info['ntp.mode'] = (ntp_flags & 0b00000111)
339
- ntp_auth_seq, ntp_impl, ntp_rcode = data.slice!(0,3).unpack('C*')
340
- info['ntp.implementation'] = ntp_impl
341
- info['ntp.request_code'] = ntp_rcode
342
-
343
- # if it is mode 7, parse that:
344
- # 0 1 2 3
345
- # 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
346
- # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
347
- # | Err | Number of data items | MBZ | Size of data item |
348
- # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
349
- # ... data ...
458
+
350
459
  if info['ntp.mode'] == 7
351
- return info if data.size < 4
460
+ # if it is mode 7, parse that:
461
+ # 0 1 2 3
462
+ # 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
463
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
464
+ # |R|M| VN | Mode|A| Sequence | Implementation| Req Code |
465
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
466
+ # | Err | Number of data items | MBZ | Size of data item |
467
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
468
+ # ... data ...
469
+
470
+ return info if data.size < 8
471
+ info['ntp.response'] = ntp_flags >> 7
472
+ info['ntp.more'] = (ntp_flags & 0b01000000) >> 6
473
+ ntp_auth_seq, ntp_impl, ntp_rcode = data.slice!(0,3).unpack('C*')
474
+ info['ntp.implementation'] = ntp_impl
475
+ info['ntp.request_code'] = ntp_rcode
352
476
  mode7_data = data.slice!(0,4).unpack('n*')
353
477
  info['ntp.mode7.err'] = mode7_data.first >> 11
354
478
  info['ntp.mode7.data_items_count'] = mode7_data.first & 0b0000111111111111
@@ -371,11 +495,18 @@ class FilterDecodeNTPReply
371
495
  #u_int32 daddr; /* destination host address */
372
496
  #u_int32 flags; /* flags about destination */
373
497
  #u_short port; /* port number of last reception */
374
-
375
- firsttime,lasttime,restr,count,raddr,laddr,flags,dport = data[idx, 30].unpack("NNNNNNNn")
376
- remote_addresses << [raddr].pack("N").unpack("C*").map{|x| x.to_s }.join(".")
377
- local_addresses << [laddr].pack("N").unpack("C*").map{|x| x.to_s }.join(".")
378
- idx += info['ntp.mode7.data_item_size']
498
+ data_block = data[idx, 30]
499
+ # Occasionally not all of data captured, need to defensively handle this case.
500
+ if data_block
501
+ firsttime,lasttime,restr,count,raddr,laddr,flags,dport = data_block.unpack("NNNNNNNn")
502
+ # even if data_block is not nil, might not have all of the 30 bytes of data, so make sure
503
+ # that remote and local address are non-nil.
504
+ remote_addresses << [raddr].pack("N").unpack("C*").map{|x| x.to_s }.join(".") if raddr
505
+ local_addresses << [laddr].pack("N").unpack("C*").map{|x| x.to_s }.join(".") if laddr
506
+ idx += info['ntp.mode7.data_item_size']
507
+ else
508
+ break
509
+ end
379
510
  end
380
511
 
381
512
  info['ntp.monlist.remote_addresses'] = remote_addresses.join(' ')
@@ -384,10 +515,38 @@ class FilterDecodeNTPReply
384
515
  info['ntp.monlist.local_addresses.count'] = local_addresses.size
385
516
  end
386
517
  end
518
+ elsif info['ntp.mode'] == 6
519
+ # control responses, supposedly. abort if there isn't enough to have an empty control response
520
+ return info if data.size < 12
521
+ ntp_control_flags = data.slice!(0,1).unpack('C').first
522
+ info["ntp.control.response"] = ntp_control_flags >> 7
523
+ info["ntp.control.error"] = (ntp_control_flags & 0b01000000) >> 6
524
+ info["ntp.control.more"] = (ntp_control_flags & 0b00100000) >> 5
525
+ info["ntp.control.opcode"] = (ntp_control_flags & 0b00011111)
526
+ %w(seq status association_id offset count).each do |field|
527
+ info["ntp.control.#{field}"] = data.slice!(0,2).unpack('n').first
528
+ end
529
+ data = data.slice(0,info["ntp.control.count"])
530
+ # decode readvar responses
531
+ if info["ntp.control.opcode"] == 2
532
+ info["ntp.control.readvar"] = data
533
+ data.strip!
534
+ data.gsub!(/\r\n/, ' ')
535
+ data.split(/, /).each do |pair|
536
+ next unless pair =~ /=/
537
+ key, value = pair.split(/=/)
538
+ if value
539
+ value.gsub!(/^['"]/, '')
540
+ value.gsub!(/['"]$/, '')
541
+ end
542
+ info["ntp.control.readvar.#{key}"] = value
543
+ end
544
+ else
545
+ info["ntp.control.data"] = data
546
+ end
387
547
  end
388
548
  elsif ntp_version == 4
389
549
  info['ntp.leap_indicator'] = ntp_flags >> 6
390
- info['ntp.mode'] = ntp_flags & 0b00000111
391
550
  info['ntp.peer.stratum'], info['ntp.peer.interval'], info['ntp.peer.precision'] = data.slice!(0,3).unpack('C*')
392
551
  info['ntp.root.delay'], info['ntp.root.dispersion'], info['ntp.ref_id'] = data.slice!(0,12).unpack('N*')
393
552
  info['ntp.timestamp.reference'], info['ntp.timestamp.origin'], info['ntp.timestamp.receive'], info['ntp.timestamp.transmit'] = data.slice!(0,32).unpack('Q*')
@@ -396,6 +555,54 @@ class FilterDecodeNTPReply
396
555
  info
397
556
  end
398
557
  end
558
+ #
559
+ class FilterDecodePortmapperReply
560
+ include BaseDecoder
561
+ ID_TO_PROTOCOL = {
562
+ 0=>"ip", 1=>"icmp", 2=>"igmp", 3=>"ggp",
563
+ 4=>"ipencap", 5=>"st", 6=>"tcp", 8=>"egp",
564
+ 9=>"igp", 12=>"pup", 17=>"udp", 20=>"hmp",
565
+ 22=>"xns-idp", 27=>"rdp", 29=>"iso-tp4", 33=>"dccp",
566
+ 36=>"xtp", 37=>"ddp", 38=>"idpr-cmtp", 41=>"ipv6",
567
+ 43=>"ipv6-route", 44=>"ipv6-frag", 45=>"idrp", 46=>"rsvp",
568
+ 47=>"gre", 50=>"esp", 51=>"ah", 57=>"skip",
569
+ 58=>"ipv6-icmp", 59=>"ipv6-nonxt", 60=>"ipv6-opts", 73=>"rspf",
570
+ 81=>"vmtp", 88=>"eigrp", 89=>"ospf", 93=>"ax.25",
571
+ 94=>"ipip", 97=>"etherip", 98=>"encap", 103=>"pim",
572
+ 108=>"ipcomp", 112=>"vrrp", 115=>"l2tp", 124=>"isis",
573
+ 132=>"sctp", 133=>"fc", 135=>"mobility-header", 136=>"udplite",
574
+ 137=>"mpls-in-ip", 138=>"manet", 139=>"hip", 140=>"shim6",
575
+ 141=>"wesp", 142=>"rohc"
576
+ }
577
+ # returns array of program-version-protocol-port strings for each rpc service
578
+ def parse_data(data)
579
+ ret = []
580
+ # Skip past header that contains no rpc services
581
+ stripped = data[8..-1]
582
+ curr_pos = 0
583
+ has_next = ( !stripped.nil? && stripped.length >= 8 ? stripped[curr_pos,8].to_i(16) : 0 )
584
+ curr_pos +=8
585
+ while has_next > 0
586
+ # See if enough data present for next set of reads.
587
+ if data.length > curr_pos+40
588
+ prog_id = stripped[curr_pos,8].to_i(16); curr_pos+=8
589
+ version = stripped[curr_pos,8].to_i(16); curr_pos += 8
590
+ proto_id = stripped[curr_pos,8].to_i(16); curr_pos+=8
591
+ protocol = ID_TO_PROTOCOL[ proto_id ] || "proto-#{proto_id}"
592
+ port = stripped[curr_pos,8].to_i(16); curr_pos += 8
593
+ ret << "#{prog_id}-v#{version}-#{protocol}-#{port}" if prog_id > 0
594
+ has_next = stripped[curr_pos,8].to_i(16); curr_pos += 8
595
+ else
596
+ break
597
+ end
598
+ end
599
+ ret
600
+ end
601
+
602
+ def decode(data)
603
+ { 'rpc_services'=>parse_data(data) }
604
+ end
605
+ end
399
606
 
400
607
  end
401
608
  end