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
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative '../../../data/vulndb'
|
2
|
+
|
3
|
+
module Dap
|
4
|
+
module Filter
|
5
|
+
|
6
|
+
module BaseVulnMatch
|
7
|
+
def search(hash, service)
|
8
|
+
SEARCHES[service].each do | entry |
|
9
|
+
entry[:regex].each do | regex, value |
|
10
|
+
if regex =~ hash[entry[:hash_key]].force_encoding('BINARY')
|
11
|
+
# Handle cases that could be multiple hits, not for upnp but could be others.
|
12
|
+
hash[entry[:output_key]] = ( hash[entry[:output_key]] ? hash[entry[:output_key]] + value : value )
|
13
|
+
end
|
14
|
+
end if hash[entry[:hash_key]]
|
15
|
+
end
|
16
|
+
hash
|
17
|
+
end
|
18
|
+
|
19
|
+
def lookup(hash, service)
|
20
|
+
SEARCHES[service].each do | entry |
|
21
|
+
if hash[entry[:hash_key]]
|
22
|
+
res = entry[:cvemap][hash[entry[:hash_key]]]
|
23
|
+
if res
|
24
|
+
hash[entry[:output_key]] = res
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class FilterVulnMatchUPNP
|
33
|
+
include Base
|
34
|
+
include BaseVulnMatch
|
35
|
+
|
36
|
+
def process(doc)
|
37
|
+
doc = search(doc, :upnp)
|
38
|
+
[ doc ]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class FilterVulnMatchIPMI
|
43
|
+
include Base
|
44
|
+
include BaseVulnMatch
|
45
|
+
|
46
|
+
def process(doc)
|
47
|
+
doc = search(doc, :ipmi)
|
48
|
+
|
49
|
+
if (doc['data.ipmi_user_non_null'] == "0") && (doc['data.ipmi_user_null'] == "0")
|
50
|
+
doc["vulnerability"] = ( doc["vulnerability"] ? doc["vulnerability"] + ["IPMI-ANON"] : ["IPMI-ANON"] )
|
51
|
+
end
|
52
|
+
|
53
|
+
[ doc ]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class FilterVulnMatchMSSQL
|
58
|
+
include Base
|
59
|
+
include BaseVulnMatch
|
60
|
+
|
61
|
+
def process(doc)
|
62
|
+
doc = lookup(doc, :mssql)
|
63
|
+
[ doc ]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/dap/output.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
|
+
require 'oj'
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
|
1
5
|
module Dap
|
2
6
|
module Output
|
3
7
|
|
4
|
-
require 'oj'
|
5
8
|
|
6
9
|
module FileDestination
|
7
|
-
|
10
|
+
|
8
11
|
attr_accessor :fd
|
9
12
|
|
10
13
|
def open(file_name)
|
11
14
|
close
|
12
|
-
self.fd = ['-', 'stdout', nil].include?(file_name) ?
|
15
|
+
self.fd = ['-', 'stdout', nil].include?(file_name) ?
|
13
16
|
$stdout : ::File.open(file_name, "wb")
|
14
17
|
end
|
15
18
|
|
@@ -26,6 +29,33 @@ module Output
|
|
26
29
|
def stop
|
27
30
|
end
|
28
31
|
|
32
|
+
# String sanitizer for UTF-8
|
33
|
+
def sanitize(o)
|
34
|
+
|
35
|
+
# Handle strings
|
36
|
+
if o.kind_of? ::String
|
37
|
+
return o.to_s.encode(o.encoding, "UTF-8", :invalid => :replace, :undef => :replace, :replace => '')
|
38
|
+
end
|
39
|
+
|
40
|
+
# Handle hashes
|
41
|
+
if o.kind_of? ::Hash
|
42
|
+
r = {}
|
43
|
+
o.each_pair do |k,v|
|
44
|
+
k = sanitize(k)
|
45
|
+
v = sanitize(v)
|
46
|
+
r[k] = v
|
47
|
+
end
|
48
|
+
return r
|
49
|
+
end
|
50
|
+
|
51
|
+
# Handle arrays
|
52
|
+
if o.kind_of? ::Array
|
53
|
+
return o.map{|x| sanitize(x) }
|
54
|
+
end
|
55
|
+
|
56
|
+
# Leave as-is
|
57
|
+
o
|
58
|
+
end
|
29
59
|
end
|
30
60
|
|
31
61
|
|
@@ -34,7 +64,7 @@ module Output
|
|
34
64
|
# XXX: Quoted field handling is not supported, CSV should be a new output type
|
35
65
|
#
|
36
66
|
class OutputLines
|
37
|
-
|
67
|
+
|
38
68
|
attr_accessor :fields, :delimiter
|
39
69
|
FIELD_WILDCARD = '_'
|
40
70
|
|
@@ -44,7 +74,7 @@ module Output
|
|
44
74
|
file = nil
|
45
75
|
self.delimiter = ","
|
46
76
|
self.fields = FIELD_WILDCARD
|
47
|
-
|
77
|
+
|
48
78
|
header = false
|
49
79
|
|
50
80
|
args.each do |str|
|
@@ -57,7 +87,7 @@ module Output
|
|
57
87
|
when 'fields'
|
58
88
|
self.fields = v.split(',')
|
59
89
|
when 'delimiter'
|
60
|
-
self.delimiter =
|
90
|
+
self.delimiter =
|
61
91
|
case v.to_s
|
62
92
|
when 'tab'
|
63
93
|
"\t"
|
@@ -72,6 +102,7 @@ module Output
|
|
72
102
|
|
73
103
|
if header and not fields.include?(FIELD_WILDCARD)
|
74
104
|
self.fd.puts self.fields.join(self.delimiter)
|
105
|
+
self.fd.flush
|
75
106
|
end
|
76
107
|
|
77
108
|
end
|
@@ -81,17 +112,18 @@ module Output
|
|
81
112
|
|
82
113
|
if self.fields.include?(FIELD_WILDCARD)
|
83
114
|
doc.each_pair do |k,v|
|
84
|
-
out << v.to_s
|
115
|
+
out << sanitize(v.to_s)
|
85
116
|
end
|
86
117
|
else
|
87
118
|
self.fields.each do |k|
|
88
|
-
out << doc[k].to_s
|
119
|
+
out << sanitize(doc[k].to_s)
|
89
120
|
end
|
90
121
|
end
|
91
122
|
|
92
123
|
return unless out.length > 0
|
93
124
|
|
94
125
|
self.fd.puts out.join(self.delimiter)
|
126
|
+
self.fd.flush
|
95
127
|
end
|
96
128
|
|
97
129
|
end
|
@@ -100,15 +132,83 @@ module Output
|
|
100
132
|
# JSON Output (line-delimited records)
|
101
133
|
#
|
102
134
|
class OutputJSON
|
103
|
-
|
135
|
+
|
104
136
|
include FileDestination
|
105
137
|
|
106
138
|
def initialize(args)
|
107
139
|
self.open(args.first)
|
108
140
|
end
|
109
141
|
|
110
|
-
def write_record(doc)
|
111
|
-
self.fd.puts Oj.dump(doc)
|
142
|
+
def write_record(doc)
|
143
|
+
self.fd.puts Oj.dump(sanitize(doc))
|
144
|
+
self.fd.flush
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
#
|
151
|
+
# CSV Output
|
152
|
+
#
|
153
|
+
class OutputCSV
|
154
|
+
|
155
|
+
attr_accessor :fields, :delimiter
|
156
|
+
FIELD_WILDCARD = '_'
|
157
|
+
|
158
|
+
include FileDestination
|
159
|
+
|
160
|
+
def initialize(args)
|
161
|
+
file = nil
|
162
|
+
self.delimiter = ","
|
163
|
+
self.fields = FIELD_WILDCARD
|
164
|
+
|
165
|
+
header = false
|
166
|
+
|
167
|
+
args.each do |str|
|
168
|
+
k,v = str.split('=', 2)
|
169
|
+
case k
|
170
|
+
when 'file'
|
171
|
+
file = v
|
172
|
+
when 'header'
|
173
|
+
header = ( v =~ /^[ty1]/i ? true : false )
|
174
|
+
when 'fields'
|
175
|
+
self.fields = v.split(',')
|
176
|
+
when 'delimiter'
|
177
|
+
self.delimiter =
|
178
|
+
case v.to_s
|
179
|
+
when 'tab'
|
180
|
+
"\t"
|
181
|
+
when 'null'
|
182
|
+
"\x00"
|
183
|
+
else
|
184
|
+
v
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
self.open(file)
|
189
|
+
|
190
|
+
if header and not fields.include?(FIELD_WILDCARD)
|
191
|
+
self.fd.puts self.fields.to_csv
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
def write_record(doc)
|
197
|
+
out = []
|
198
|
+
|
199
|
+
if self.fields.include?(FIELD_WILDCARD)
|
200
|
+
doc.each_pair do |k,v|
|
201
|
+
out << sanitize(v.to_s)
|
202
|
+
end
|
203
|
+
else
|
204
|
+
self.fields.each do |k|
|
205
|
+
out << sanitize(doc[k].to_s)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
return unless out.length > 0
|
210
|
+
|
211
|
+
self.fd.puts out.to_csv
|
112
212
|
end
|
113
213
|
|
114
214
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
module Dap
|
3
|
+
module Proto
|
4
|
+
module MSSQL
|
5
|
+
|
6
|
+
#
|
7
|
+
# Data condensed from http://sqlserverbuilds.blogspot.com/
|
8
|
+
# Given a version like 8.00.2039, this data structure allows
|
9
|
+
# us to determine that the year version is 2000 sp4.
|
10
|
+
# The version_num_to_name method implements this conversion.
|
11
|
+
#
|
12
|
+
MSSQL_VERSIONS = {
|
13
|
+
'7.00'=> {
|
14
|
+
:year=>'7.0',
|
15
|
+
:service_packs=> {
|
16
|
+
623=>'-',
|
17
|
+
699=>'sp1',
|
18
|
+
842=>'sp2',
|
19
|
+
961=>'sp3',
|
20
|
+
1063=>'sp4'
|
21
|
+
}
|
22
|
+
},
|
23
|
+
'8.00'=> {
|
24
|
+
:year=>'2000',
|
25
|
+
:service_packs=> {
|
26
|
+
194=>'-',
|
27
|
+
384=>'sp1',
|
28
|
+
534=>'sp2',
|
29
|
+
760=>'sp3',
|
30
|
+
2039=>'sp4'
|
31
|
+
}
|
32
|
+
},
|
33
|
+
'9.00'=> {
|
34
|
+
:year=>'2005',
|
35
|
+
:service_packs=> {
|
36
|
+
1399=>'-',
|
37
|
+
2047=>'sp1',
|
38
|
+
3042=>'sp2',
|
39
|
+
4035=>'sp3',
|
40
|
+
5000=>'sp4'
|
41
|
+
}
|
42
|
+
},
|
43
|
+
'10.00'=> {
|
44
|
+
:year=>'2008',
|
45
|
+
:service_packs=> {
|
46
|
+
1600=>'-',
|
47
|
+
2531=>'sp1',
|
48
|
+
4000=>'sp2',
|
49
|
+
5500=>'sp3'
|
50
|
+
}
|
51
|
+
},
|
52
|
+
'10.50'=> {
|
53
|
+
:year=>'2008',
|
54
|
+
:service_packs=> {
|
55
|
+
1600=>'r2',
|
56
|
+
2500=>'r2 sp1',
|
57
|
+
4000=>'r2 sp2'
|
58
|
+
}
|
59
|
+
},
|
60
|
+
'11.00'=> {
|
61
|
+
:year=>'2012',
|
62
|
+
:service_packs=> {
|
63
|
+
2100=>'-',
|
64
|
+
3000=>'sp1'
|
65
|
+
}
|
66
|
+
},
|
67
|
+
'12.00'=> {
|
68
|
+
:year=>'2014',
|
69
|
+
:service_packs=> {
|
70
|
+
2000=>'-'
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
#
|
76
|
+
# Given a XX.YY.ZZ[.AA] version, will attempt to get the sql server
|
77
|
+
# year/service pack version for it.
|
78
|
+
def self.version_num_to_name(version)
|
79
|
+
rx = /(\d+)\.(\d+)\.(\d+).*/
|
80
|
+
if version =~ rx
|
81
|
+
v1 = $1.to_i
|
82
|
+
v2 = $2.to_i
|
83
|
+
v3 = $3.to_i
|
84
|
+
else
|
85
|
+
return [ nil, nil ]
|
86
|
+
end
|
87
|
+
#puts("v1=#{v1}, v2=#{v2}, v3=#{v3}")
|
88
|
+
key = sprintf("%d.%02d",v1,v2)
|
89
|
+
svc_pack = nil
|
90
|
+
year = nil
|
91
|
+
if MSSQL_VERSIONS[key]
|
92
|
+
year = MSSQL_VERSIONS[key][:year]
|
93
|
+
svc_packs = MSSQL_VERSIONS[key][:service_packs]
|
94
|
+
is_first=true
|
95
|
+
svc_packs.each do | k, v|
|
96
|
+
#puts( "k=#{k}, v=#{v}")
|
97
|
+
if v3 <= k and is_first
|
98
|
+
svc_pack = v
|
99
|
+
break
|
100
|
+
elsif v3 == k
|
101
|
+
svc_pack = v
|
102
|
+
break
|
103
|
+
else
|
104
|
+
svc_pack = v
|
105
|
+
end
|
106
|
+
is_first=false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
[ year, svc_pack]
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/dap/version.rb
CHANGED
data/spec/dap/proto/ipmi_spec.rb
CHANGED
@@ -7,10 +7,10 @@ module IPMI
|
|
7
7
|
|
8
8
|
describe Channel_Auth_Reply do
|
9
9
|
it "valid with the proper rmcp version and message length" do
|
10
|
-
expect(subject.valid?).to
|
11
|
-
expect(Channel_Auth_Reply.new(rmcp_version: 6).valid?).to
|
12
|
-
expect(Channel_Auth_Reply.new(message_length: 16).valid?).to
|
13
|
-
expect(Channel_Auth_Reply.new(rmcp_version: 6, message_length: 16).valid?).to
|
10
|
+
expect(subject.valid?).to be false
|
11
|
+
expect(Channel_Auth_Reply.new(rmcp_version: 6).valid?).to be false
|
12
|
+
expect(Channel_Auth_Reply.new(message_length: 16).valid?).to be false
|
13
|
+
expect(Channel_Auth_Reply.new(rmcp_version: 6, message_length: 16).valid?).to be true
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
data/tools/geo-ip-summary.rb
CHANGED
@@ -16,10 +16,18 @@ class GeoIPSummary
|
|
16
16
|
@tree['count'] = 0
|
17
17
|
end
|
18
18
|
|
19
|
+
def stringify(o)
|
20
|
+
if o.kind_of?( ::String )
|
21
|
+
o.to_s.encode(o.encoding, "UTF-8", :invalid => :replace, :undef => :replace, :replace => '')
|
22
|
+
else
|
23
|
+
o.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
19
27
|
def process_hash( json_hash )
|
20
|
-
country = json_hash[@country_name]
|
21
|
-
region = json_hash[@region_name] || 'Undefined Region'
|
22
|
-
city = json_hash[@city_name] || 'Undefined City'
|
28
|
+
country = stringify( json_hash[@country_name] )
|
29
|
+
region = stringify( json_hash[@region_name] || 'Undefined Region' )
|
30
|
+
city = stringify( json_hash[@city_name] || 'Undefined City' )
|
23
31
|
|
24
32
|
# Create subhashes and values as needed on down the tree
|
25
33
|
@tree[country] ||= {}
|
@@ -123,7 +131,7 @@ def parse_command_line(args)
|
|
123
131
|
opts.on('--var top-level-var', 'Sets the top level json name, for defining all of country/region/city') do | val |
|
124
132
|
options[:var] = val
|
125
133
|
options[:country] = "#{val}.country_name"
|
126
|
-
options[:region] = "#{val}.
|
134
|
+
options[:region] = "#{val}.region_name"
|
127
135
|
options[:city] = "#{val}.city"
|
128
136
|
end
|
129
137
|
|
@@ -136,14 +144,20 @@ def parse_command_line(args)
|
|
136
144
|
end
|
137
145
|
options
|
138
146
|
end
|
139
|
-
|
140
|
-
raise 'Need json key names for country,region and city.' if opts[:country].nil? || opts[:region].nil? || opts[:city].nil?
|
147
|
+
opts = parse_command_line(ARGV)
|
141
148
|
|
142
|
-
summarizer = GeoIPSummary.new(opts[:country], opts[:region], opts[:city])
|
143
|
-
while line=gets
|
144
|
-
summarizer.process_hash(Oj.load(line.strip))
|
145
|
-
end
|
146
149
|
|
147
|
-
|
150
|
+
raise 'Need json key names for country,region and city.' if opts[:country].nil? || opts[:region].nil? || opts[:city].nil?
|
151
|
+
|
152
|
+
summarizer = GeoIPSummary.new(opts[:country], opts[:region], opts[:city])
|
153
|
+
|
154
|
+
|
155
|
+
$stdin.each_line do |line|
|
156
|
+
json = Oj.load(line.unpack("C*").pack("C*").strip) rescue nil
|
157
|
+
next unless json
|
158
|
+
summarizer.process_hash(json)
|
159
|
+
end
|
160
|
+
|
161
|
+
Oj.default_options={:indent=>2}
|
148
162
|
|
149
|
-
|
163
|
+
puts Oj.dump(summarizer.order_tree)
|