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 +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
|