dap 0.0.1 → 0.0.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.
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