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 +7 -0
- data/.travis.yml +8 -0
- data/Gemfile +3 -3
- data/Gemfile.lock +22 -18
- data/README.md +24 -1
- data/Rakefile +22 -0
- data/data/vulndb.rb +83 -0
- data/lib/dap/filter.rb +2 -1
- data/lib/dap/filter/http.rb +4 -0
- data/lib/dap/filter/simple.rb +25 -1
- data/lib/dap/filter/udp.rb +255 -48
- data/lib/dap/filter/vulnmatch.rb +68 -0
- data/lib/dap/output.rb +111 -11
- data/lib/dap/proto/mssql.rb +114 -0
- data/lib/dap/version.rb +1 -1
- data/spec/dap/proto/ipmi_spec.rb +4 -4
- data/tools/geo-ip-summary.rb +26 -12
- data/tools/ipmi-vulns.rb +4 -2
- data/tools/json-summarize.rb +78 -33
- data/tools/netbios-counts.rb +13 -6
- data/tools/upnp-vulns.rb +9 -9
- metadata +36 -53
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
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
-
aruba (0.
|
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.
|
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.
|
23
|
+
htmlentities (4.3.2)
|
24
24
|
mini_portile (0.6.0)
|
25
|
-
multi_json (1.10.
|
25
|
+
multi_json (1.10.1)
|
26
26
|
multi_test (0.1.1)
|
27
27
|
net-dns (0.8.0)
|
28
|
-
nokogiri (1.6.
|
28
|
+
nokogiri (1.6.3.1)
|
29
29
|
mini_portile (= 0.6.0)
|
30
|
-
oj (2.
|
31
|
-
recog (0.
|
30
|
+
oj (2.10.2)
|
31
|
+
recog (0.02)
|
32
32
|
nokogiri
|
33
|
-
rspec (
|
34
|
-
rspec-core (~>
|
35
|
-
rspec-expectations (~>
|
36
|
-
rspec-mocks (~>
|
37
|
-
rspec-core (
|
38
|
-
|
39
|
-
|
40
|
-
|
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.
|
50
|
+
aruba (~> 0.6.1)
|
47
51
|
bit-struct
|
48
|
-
cucumber (~> 1.3.
|
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 (~>
|
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 [
|
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
data/lib/dap/filter/http.rb
CHANGED
@@ -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
|
data/lib/dap/filter/simple.rb
CHANGED
@@ -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(
|
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
|
data/lib/dap/filter/udp.rb
CHANGED
@@ -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" =>
|
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
|
81
|
-
|
82
|
-
info['
|
83
|
-
info['
|
84
|
-
info['
|
85
|
-
info['
|
86
|
-
info['
|
87
|
-
info['
|
88
|
-
info['
|
89
|
-
info['
|
90
|
-
info['
|
91
|
-
info['
|
92
|
-
info['
|
93
|
-
info['
|
94
|
-
info['
|
95
|
-
info['
|
96
|
-
info['
|
97
|
-
info['
|
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
|
-
|
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
|
-
|
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
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
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
|