dap 0.0.1
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.
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +55 -0
- data/LICENSE +20 -0
- data/README.md +15 -0
- data/bin/dap +137 -0
- data/dap.gemspec +42 -0
- data/data/.gitkeep +0 -0
- data/lib/dap.rb +101 -0
- data/lib/dap/filter.rb +8 -0
- data/lib/dap/filter/base.rb +37 -0
- data/lib/dap/filter/geoip.rb +72 -0
- data/lib/dap/filter/http.rb +173 -0
- data/lib/dap/filter/names.rb +151 -0
- data/lib/dap/filter/openssl.rb +53 -0
- data/lib/dap/filter/recog.rb +23 -0
- data/lib/dap/filter/simple.rb +340 -0
- data/lib/dap/filter/udp.rb +401 -0
- data/lib/dap/input.rb +74 -0
- data/lib/dap/input/csv.rb +60 -0
- data/lib/dap/input/warc.rb +81 -0
- data/lib/dap/output.rb +117 -0
- data/lib/dap/proto/addp.rb +0 -0
- data/lib/dap/proto/dtls.rb +21 -0
- data/lib/dap/proto/ipmi.rb +94 -0
- data/lib/dap/proto/natpmp.rb +19 -0
- data/lib/dap/proto/wdbrpc.rb +58 -0
- data/lib/dap/utils/oui.rb +16586 -0
- data/lib/dap/version.rb +3 -0
- data/samples/http_get_reply.ic12.bz2 +0 -0
- data/samples/http_get_reply.ic12.sh +1 -0
- data/samples/http_get_reply_iframes.json.bz2 +0 -0
- data/samples/http_get_reply_iframes.json.sh +1 -0
- data/samples/http_get_reply_links.json.sh +1 -0
- data/samples/iawide.warc.bz2 +0 -0
- data/samples/iawide_warc.sh +1 -0
- data/samples/ipmi_chan_auth_replies.crd.bz2 +0 -0
- data/samples/ipmi_chan_auth_replies.sh +1 -0
- data/samples/ssl_certs.bz2 +0 -0
- data/samples/ssl_certs_geo.sh +1 -0
- data/samples/ssl_certs_names.sh +1 -0
- data/samples/ssl_certs_names_expanded.sh +1 -0
- data/samples/ssl_certs_org.sh +1 -0
- data/samples/udp-netbios.csv.bz2 +0 -0
- data/samples/udp-netbios.sh +1 -0
- data/spec/dap/proto/ipmi_spec.rb +19 -0
- data/tools/geo-ip-summary.rb +149 -0
- data/tools/ipmi-vulns.rb +27 -0
- data/tools/json-summarize.rb +81 -0
- data/tools/netbios-counts.rb +271 -0
- data/tools/upnp-vulns.rb +35 -0
- data/tools/value-counts-to-md-table.rb +23 -0
- metadata +264 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'recog'
|
2
|
+
|
3
|
+
module Dap
|
4
|
+
module Filter
|
5
|
+
|
6
|
+
class FilterRecog
|
7
|
+
include Base
|
8
|
+
|
9
|
+
def process(doc)
|
10
|
+
self.opts.each_pair do |k,v|
|
11
|
+
next unless doc.has_key?(k)
|
12
|
+
match = Recog::Nizer.match(v, doc[k])
|
13
|
+
next unless match
|
14
|
+
match.each_pair do |ok, ov|
|
15
|
+
doc["#{k}.recog.#{ok}"] = ov.to_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
[ doc ]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,340 @@
|
|
1
|
+
module Dap
|
2
|
+
module Filter
|
3
|
+
|
4
|
+
class FilterRename
|
5
|
+
include Base
|
6
|
+
def process(doc)
|
7
|
+
self.opts.each_pair do |k,v|
|
8
|
+
if doc.has_key?(k)
|
9
|
+
doc[v] = doc[k]
|
10
|
+
doc.delete(k)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
[ doc ]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class FilterRemove
|
18
|
+
include Base
|
19
|
+
def process(doc)
|
20
|
+
self.opts.each_pair do |k,v|
|
21
|
+
if doc.has_key?(k)
|
22
|
+
doc.delete(k)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
[ doc ]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class FilterSelect
|
30
|
+
include Base
|
31
|
+
def process(doc)
|
32
|
+
ndoc = {}
|
33
|
+
self.opts.each_pair do |k,v|
|
34
|
+
if doc.has_key?(k)
|
35
|
+
ndoc[k] = doc[k]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
(ndoc.keys.length == 0) ? [] : [ ndoc ]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class FilterInsert
|
43
|
+
include Base
|
44
|
+
def process(doc)
|
45
|
+
self.opts.each_pair do |k,v|
|
46
|
+
doc[k] = v
|
47
|
+
end
|
48
|
+
[ doc ]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class FilterInclude
|
53
|
+
include Base
|
54
|
+
def process(doc)
|
55
|
+
self.opts.each_pair do |k,v|
|
56
|
+
if doc.has_key?(k) and doc[k].to_s.index(v)
|
57
|
+
return [ doc ]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
[ ]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# where 'some.field == some_value'
|
65
|
+
# where 'some.field != some_value'
|
66
|
+
# TODO: do something other than basic string comparison. Would be nice to have where 'some.field > 2', etc
|
67
|
+
class FilterWhere
|
68
|
+
attr_accessor :query
|
69
|
+
|
70
|
+
def initialize(args)
|
71
|
+
fail "Expected 3 arguments to 'where' but got #{args.size}" unless args.size == 3
|
72
|
+
self.query = args
|
73
|
+
end
|
74
|
+
|
75
|
+
def process(doc)
|
76
|
+
field, operator, expected = self.query
|
77
|
+
return [ doc ] if doc.has_key?(field) and doc[field].send(operator, expected)
|
78
|
+
[ ]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class FilterExclude
|
83
|
+
include Base
|
84
|
+
def process(doc)
|
85
|
+
self.opts.each_pair do |k,v|
|
86
|
+
if doc.has_key?(k) and doc[k].to_s.index(v)
|
87
|
+
return [ ]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
[ doc ]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class FilterExists
|
95
|
+
include Base
|
96
|
+
def process(doc)
|
97
|
+
self.opts.each_pair do |k,v|
|
98
|
+
if doc.has_key?(k) and doc[k].to_s.length > 0
|
99
|
+
return [ doc ]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
[ ]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class FilterNotExists < FilterExists
|
107
|
+
include Base
|
108
|
+
def process(doc)
|
109
|
+
exists_doc = super(doc)
|
110
|
+
exists_doc.empty? ? [ doc ] : [ ]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Applies some simple annotation to the given fields, adding another
|
115
|
+
# field name with the appended annotation type, i.e.:
|
116
|
+
#
|
117
|
+
# $ echo '{"foo":"blah"}' | dap json stdin + annotate foo=length + json
|
118
|
+
# {"foo":"bar","foo.length":4}
|
119
|
+
class FilterAnnotate
|
120
|
+
include Base
|
121
|
+
def process(doc)
|
122
|
+
self.opts.each_pair do |k,v|
|
123
|
+
if doc.has_key?(k)
|
124
|
+
case v
|
125
|
+
when 'length'
|
126
|
+
doc["#{k}.length"] = doc[k].length
|
127
|
+
when 'size'
|
128
|
+
doc["#{k}.size"] = doc[k].size
|
129
|
+
else
|
130
|
+
fail "Unsupported annotation '#{v}'"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
[ doc ]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class FilterTransform
|
139
|
+
include Base
|
140
|
+
def process(doc)
|
141
|
+
self.opts.each_pair do |k,v|
|
142
|
+
if doc.has_key?(k)
|
143
|
+
case v
|
144
|
+
when 'downcase'
|
145
|
+
doc[k] = doc[k].to_s.downcase
|
146
|
+
when 'upcase'
|
147
|
+
doc[k] = doc[k].to_s.upcase
|
148
|
+
when 'ascii'
|
149
|
+
doc[k] = doc[k].to_s.gsub(/[\x00-\x1f\x7f-\xff]/n, '')
|
150
|
+
when 'utf8encode'
|
151
|
+
doc[k] = doc[k].to_s.encode!('UTF-8', invalid: :replace, undef: :replace, replace: '')
|
152
|
+
when 'base64decode'
|
153
|
+
doc[k] = doc[k].to_s.unpack('m*').first
|
154
|
+
when 'base64encode'
|
155
|
+
doc[k] = [doc[k].to_s].pack('m*').gsub(/\s+/n, '')
|
156
|
+
when 'qprintdecode'
|
157
|
+
doc[k] = doc[k].to_s.gsub(/=([0-9A-Fa-f]{2})/n){ |x| [x[1,2]].pack("H*") }
|
158
|
+
when 'qprintencode'
|
159
|
+
doc[k] = doc[k].to_s.gsub(/[\x00-\x20\x3d\x7f-\xff]/n){|x| ( "=%.2x" % x.unpack("C").first ).upcase }
|
160
|
+
when 'hexdecode'
|
161
|
+
doc[k] = [ doc[k].to_s ].pack("H*")
|
162
|
+
when 'hexencode'
|
163
|
+
doc[k] = doc[k].to_s.unpack("H*").first
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
[ doc ]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class FilterTruncate
|
172
|
+
include Base
|
173
|
+
def process(doc)
|
174
|
+
self.opts.each_pair do |k,v|
|
175
|
+
if doc.has_key?(k)
|
176
|
+
doc[k] = doc[k].to_s[0, v.to_i]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
[ doc ]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
class FilterSplitLine
|
184
|
+
include Base
|
185
|
+
def process(doc)
|
186
|
+
lines = [ ]
|
187
|
+
self.opts.each_pair do |k,v|
|
188
|
+
if doc.has_key?(k)
|
189
|
+
doc[k].to_s.split(/\n/).each do |line|
|
190
|
+
lines << doc.merge({ "#{k}.line" => line })
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
lines.length == 0 ? [ doc ] : [ lines ]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
class FilterSplitWord
|
199
|
+
include Base
|
200
|
+
def process(doc)
|
201
|
+
lines = [ ]
|
202
|
+
self.opts.each_pair do |k,v|
|
203
|
+
if doc.has_key?(k)
|
204
|
+
doc[k].to_s.split(/\W/).each do |line|
|
205
|
+
lines << doc.merge({ "#{k}.word" => line })
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
lines.length == 0 ? [ doc ] : [ lines ]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
class FilterSplitTab
|
214
|
+
include Base
|
215
|
+
def process(doc)
|
216
|
+
lines = [ ]
|
217
|
+
self.opts.each_pair do |k,v|
|
218
|
+
if doc.has_key?(k)
|
219
|
+
doc[k].to_s.split(/\t/).each do |line|
|
220
|
+
lines << doc.merge({ "#{k}.tab" => line })
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
lines.length == 0 ? [ doc ] : [ lines ]
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
class FilterSplitComma
|
230
|
+
include Base
|
231
|
+
def process(doc)
|
232
|
+
lines = [ ]
|
233
|
+
self.opts.each_pair do |k,v|
|
234
|
+
if doc.has_key?(k)
|
235
|
+
doc[k].to_s.split(/m/).each do |line|
|
236
|
+
lines << doc.merge({ "#{k}.word" => line })
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
lines.length == 0 ? [ doc ] : [ lines ]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
class FilterSplitArray
|
245
|
+
include Base
|
246
|
+
def process(doc)
|
247
|
+
lines = [ ]
|
248
|
+
self.opts.each_pair do |k,v|
|
249
|
+
if doc.has_key?(k) and doc[k].respond_to?(:each)
|
250
|
+
doc[k].each do |line|
|
251
|
+
lines << doc.merge({ "#{k}.item" => line })
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
lines.length == 0 ? [ doc ] : [ lines ]
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
class FilterFieldSplitLine
|
260
|
+
include Base
|
261
|
+
def process(doc)
|
262
|
+
self.opts.each_pair do |k,v|
|
263
|
+
if doc.has_key?(k)
|
264
|
+
lcount = 1
|
265
|
+
doc[k].to_s.split(/\n/).each do |line|
|
266
|
+
doc.merge!({ "#{k}.f#{lcount}" => line })
|
267
|
+
lcount += 1
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
[ doc ]
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
class FilterFieldSplitWord
|
276
|
+
include Base
|
277
|
+
def process(doc)
|
278
|
+
self.opts.each_pair do |k,v|
|
279
|
+
if doc.has_key?(k)
|
280
|
+
wcount = 1
|
281
|
+
doc[k].to_s.split(/\W/).each do |word|
|
282
|
+
doc.merge!({ "#{k}.f#{wcount}" => word })
|
283
|
+
wcount += 1
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
[ doc ]
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
class FilterFieldSplitTab
|
292
|
+
include Base
|
293
|
+
def process(doc)
|
294
|
+
self.opts.each_pair do |k,v|
|
295
|
+
if doc.has_key?(k)
|
296
|
+
wcount = 1
|
297
|
+
doc[k].to_s.split(/\t/).each do |word|
|
298
|
+
doc.merge!({ "#{k}.f#{wcount}" => word })
|
299
|
+
wcount += 1
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
[ doc ]
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
class FilterFieldSplitComma
|
308
|
+
include Base
|
309
|
+
def process(doc)
|
310
|
+
self.opts.each_pair do |k,v|
|
311
|
+
if doc.has_key?(k)
|
312
|
+
wcount = 1
|
313
|
+
doc[k].to_s.split(/,/).each do |word|
|
314
|
+
doc.merge!({ "#{k}.f#{wcount}" => word })
|
315
|
+
wcount += 1
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
[ doc ]
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
class FilterFieldSplitArray
|
324
|
+
include Base
|
325
|
+
def process(doc)
|
326
|
+
self.opts.each_pair do |k,v|
|
327
|
+
if doc.has_key?(k) and doc[k].respond_to?(:each)
|
328
|
+
wcount = 1
|
329
|
+
doc[k].each do |word|
|
330
|
+
doc.merge!({ "#{k}.f#{wcount}" => word })
|
331
|
+
wcount += 1
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
[ doc ]
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
end
|
340
|
+
end
|
@@ -0,0 +1,401 @@
|
|
1
|
+
module Dap
|
2
|
+
module Filter
|
3
|
+
|
4
|
+
require 'openssl'
|
5
|
+
require 'net/dns'
|
6
|
+
require 'bit-struct'
|
7
|
+
|
8
|
+
require 'dap/proto/addp'
|
9
|
+
require 'dap/proto/dtls'
|
10
|
+
require 'dap/proto/natpmp'
|
11
|
+
require 'dap/proto/wdbrpc'
|
12
|
+
require 'dap/proto/ipmi'
|
13
|
+
require 'dap/utils/oui'
|
14
|
+
|
15
|
+
#
|
16
|
+
# Decode a MDNS Services probe response ( zmap: mdns_5353.pkt )
|
17
|
+
#
|
18
|
+
class FilterDecodeMDNSSrvReply
|
19
|
+
include BaseDecoder
|
20
|
+
def decode(data)
|
21
|
+
begin
|
22
|
+
r = Net::DNS::Packet.parse(data)
|
23
|
+
return if not r
|
24
|
+
|
25
|
+
# XXX: This can throw an exception on bad data
|
26
|
+
svcs = r.answer.map {|x| (x.value.to_s) }
|
27
|
+
svcs.delete('')
|
28
|
+
return if not (svcs and svcs.length > 0)
|
29
|
+
return { "mdns_services" => svc.join(" ") }
|
30
|
+
rescue ::Exception
|
31
|
+
end
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Decode a DNS bind.version probe response ( zmap: dns_53.pkt )
|
38
|
+
#
|
39
|
+
class FilterDecodeDNSVersionReply
|
40
|
+
include BaseDecoder
|
41
|
+
def decode(data)
|
42
|
+
begin
|
43
|
+
r = Net::DNS::Packet.parse(data)
|
44
|
+
return if not r
|
45
|
+
|
46
|
+
# XXX: This can throw an exception on bad data
|
47
|
+
vers = r.answer.map{|x| x.txt.strip rescue nil }.reject{|x| x.nil? }.first
|
48
|
+
return if not vers
|
49
|
+
return { "dns_version" => vers }
|
50
|
+
rescue ::Exception
|
51
|
+
{ }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Decode a SSDP probe response ( zmap: upnp_1900.pkt )
|
58
|
+
#
|
59
|
+
class FilterDecodeUPNP_SSDP_Reply
|
60
|
+
include BaseDecoder
|
61
|
+
def decode(data)
|
62
|
+
head = { }
|
63
|
+
data.split(/\n/).each do |line|
|
64
|
+
k,v = line.strip.split(':', 2)
|
65
|
+
next if not k
|
66
|
+
head["upnp_#{k.downcase}"] = (v.to_s.strip)
|
67
|
+
end
|
68
|
+
head
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Decode a VxWorks WDBRPC probe response ( zmap: wdbrpc_17185.pkt )
|
74
|
+
#
|
75
|
+
class FilterDecodeWDBRPC_Reply
|
76
|
+
include BaseDecoder
|
77
|
+
def decode(data)
|
78
|
+
info = {}
|
79
|
+
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)
|
98
|
+
info
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Decode a SNMP GET probe response ( zmap: snmp1_161.pkt )
|
104
|
+
#
|
105
|
+
class FilterDecodeSNMPGetReply
|
106
|
+
include BaseDecoder
|
107
|
+
def decode(data)
|
108
|
+
asn = OpenSSL::ASN1.decode(data) rescue nil
|
109
|
+
return if not asn
|
110
|
+
|
111
|
+
snmp_error = asn.value[0].value rescue nil
|
112
|
+
snmp_comm = asn.value[1].value rescue nil
|
113
|
+
snmp_data = asn.value[2].value[3].value[0] rescue nil
|
114
|
+
snmp_oid = snmp_data.value[0].value rescue nil
|
115
|
+
snmp_info = snmp_data.value[1].value rescue nil
|
116
|
+
|
117
|
+
return if not (snmp_error and snmp_comm and snmp_data and snmp_oid and snmp_info)
|
118
|
+
snmp_info = snmp_info.to_s.gsub(/\s+/, ' ').gsub(/[\x00-\x1f]/, ' ')
|
119
|
+
|
120
|
+
return if not snmp_info
|
121
|
+
{ 'snmp_value' => snmp_info }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Decode a IPMI GetChannelAuth probe response ( zmap: ipmi_623.pkt )
|
127
|
+
#
|
128
|
+
class FilterDecodeIPMIChanAuthReply
|
129
|
+
include BaseDecoder
|
130
|
+
def decode(data)
|
131
|
+
info = Dap::Proto::IPMI::Channel_Auth_Reply.new(data)
|
132
|
+
return unless info.valid?
|
133
|
+
{}.tap do |h|
|
134
|
+
info.fields.each do |f|
|
135
|
+
name = f.name
|
136
|
+
h[name] = info.send(name).to_s
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Decode a NAT-PMP External Address response
|
144
|
+
#
|
145
|
+
class FilterDecodeNATPMPExternalAddressResponse
|
146
|
+
include BaseDecoder
|
147
|
+
def decode(data)
|
148
|
+
return unless (data && data.size == Dap::Proto::NATPMP::REQUIRED_SIZE)
|
149
|
+
info = Dap::Proto::NATPMP::ExternalAddressResponse.new(data)
|
150
|
+
{}.tap do |h|
|
151
|
+
info.fields.each do |f|
|
152
|
+
name = f.name
|
153
|
+
h[name] = info.send(name).to_s
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Decode a NetBIOS status probe response ( zmap: netbios_137.pkt )
|
161
|
+
#
|
162
|
+
class FilterDecodeNetbiosStatusReply
|
163
|
+
include BaseDecoder
|
164
|
+
def decode(data)
|
165
|
+
ret = {}
|
166
|
+
head = data.slice!(0,12)
|
167
|
+
|
168
|
+
xid, flags, quests, answers, auths, adds = head.unpack('n6')
|
169
|
+
return if quests != 0
|
170
|
+
return if answers == 0
|
171
|
+
|
172
|
+
qname = data.slice!(0,34)
|
173
|
+
rtype,rclass,rttl,rlen = data.slice!(0,10).unpack('nnNn')
|
174
|
+
return if not rlen
|
175
|
+
|
176
|
+
buff = data.slice!(0,rlen)
|
177
|
+
|
178
|
+
names = []
|
179
|
+
|
180
|
+
case rtype
|
181
|
+
when 0x21
|
182
|
+
hname = nil
|
183
|
+
inf = ''
|
184
|
+
rcnt = buff.slice!(0,1).unpack("C")[0]
|
185
|
+
return unless rcnt
|
186
|
+
1.upto(rcnt) do
|
187
|
+
tname = buff.slice!(0,15).gsub(/\x00.*/, '').strip
|
188
|
+
ttype = buff.slice!(0,1).unpack("C")[0]
|
189
|
+
tflag = buff.slice!(0,2).unpack('n')[0]
|
190
|
+
names << [ tname, ttype, tflag ]
|
191
|
+
end
|
192
|
+
|
193
|
+
maddr = buff.slice!(0,6).unpack("C*").map{|c| "%.2x" % c }.join(":")
|
194
|
+
names.each do |name|
|
195
|
+
inf << name[0]
|
196
|
+
|
197
|
+
next unless name[1]
|
198
|
+
inf << ":%.2x" % name[1]
|
199
|
+
|
200
|
+
next unless name[2]
|
201
|
+
if (name[2] & 0x8000 == 0)
|
202
|
+
inf << ":U "
|
203
|
+
else
|
204
|
+
inf << ":G "
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
return unless names.length > 0
|
210
|
+
|
211
|
+
{}.tap do |hash|
|
212
|
+
hash['netbios_names'] = (inf)
|
213
|
+
hash['netbios_mac'] = maddr
|
214
|
+
hash['netbios_hname'] = names[0][0]
|
215
|
+
unless maddr == '00:00:00:00:00:00'
|
216
|
+
hash['netbios_mac_company'] = mac_company(maddr)
|
217
|
+
hash['netbios_mac_company_name'] = mac_company_name(maddr)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def mac_company(address)
|
223
|
+
begin
|
224
|
+
name = Dap::Utils::Oui.lookup_oui_fullname(address)
|
225
|
+
name.split("/").first.strip
|
226
|
+
rescue => error
|
227
|
+
''
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def mac_company_name(address)
|
232
|
+
begin
|
233
|
+
Dap::Utils::Oui.lookup_oui_company_name(address)
|
234
|
+
rescue => error
|
235
|
+
''
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
#
|
240
|
+
# Decode a MSSQL reply
|
241
|
+
#
|
242
|
+
class FilterDecodeMSSQLReply
|
243
|
+
include BaseDecoder
|
244
|
+
def decode(data)
|
245
|
+
info = {}
|
246
|
+
# Some binary characters often proceed key, restrict to alphanumeric and a few other common chars
|
247
|
+
data.scan(/([A-Za-z0-9 \.\-_]+?);(.+?);/).each do | var, val|
|
248
|
+
info["mssql.#{var.encode!( 'UTF-8', invalid: :replace, undef: :replace, replace: '' )}"] = val.encode!( 'UTF-8', invalid: :replace, undef: :replace, replace: '' )
|
249
|
+
end
|
250
|
+
info
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
#
|
255
|
+
# Decode a SIP OPTIONS Reply
|
256
|
+
#
|
257
|
+
class FilterDecodeSIPOptionsReply
|
258
|
+
include BaseDecoder
|
259
|
+
def decode(data)
|
260
|
+
info = {}
|
261
|
+
|
262
|
+
return info unless (data and data.length > 0)
|
263
|
+
|
264
|
+
head,body = data.to_s.split(/\r?\n\r?\n/, 2)
|
265
|
+
|
266
|
+
head.split(/\r?\n/).each do |line|
|
267
|
+
case line
|
268
|
+
when /^SIP\/(\d+\.\d+) (\d+)(.*)/
|
269
|
+
info['sip_version'] = $1
|
270
|
+
info['sip_code'] = $2
|
271
|
+
if $3.length > 0
|
272
|
+
info['sip_message'] = $3.strip
|
273
|
+
end
|
274
|
+
when /^([a-zA-z0-9][^:]+):(.*)/
|
275
|
+
var = $1.strip
|
276
|
+
val = $2.strip
|
277
|
+
var = var.downcase.gsub(/[^a-zA-Z0-9_]/, '_').gsub(/_+/, '_')
|
278
|
+
info["sip_#{var}"] = val
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
if body and body.length > 0
|
283
|
+
info['sip_data'] = body
|
284
|
+
end
|
285
|
+
|
286
|
+
info
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
#
|
291
|
+
# Quickly decode a DTLS message
|
292
|
+
#
|
293
|
+
class FilterDecodeDTLS
|
294
|
+
include BaseDecoder
|
295
|
+
def decode(data)
|
296
|
+
return unless data.length >= 13
|
297
|
+
info = Dap::Proto::DTLS::RecordLayer.new(data)
|
298
|
+
return unless (info && info.valid?)
|
299
|
+
{}.tap do |h|
|
300
|
+
info.fields.each do |f|
|
301
|
+
name = f.name
|
302
|
+
h[name] = info.send(name).to_s
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
#
|
309
|
+
# Decode a NTP reply
|
310
|
+
#
|
311
|
+
class FilterDecodeNTPReply
|
312
|
+
include BaseDecoder
|
313
|
+
def decode(sdata)
|
314
|
+
info = {}
|
315
|
+
return if sdata.length < 4
|
316
|
+
|
317
|
+
# Make a copy since our parser is destructive
|
318
|
+
data = sdata.dup
|
319
|
+
|
320
|
+
# TODO: all of this with bitstruct?
|
321
|
+
# The format of the packet depends largely on the version, so extract just the version.
|
322
|
+
# Fortunately the version is in the same place regardless of NTP protocol version --
|
323
|
+
# The 3rd-5th bits of the first byte of the response
|
324
|
+
ntp_flags = data.slice!(0,1).unpack('C').first
|
325
|
+
ntp_version = (ntp_flags & 0b00111000) >> 3
|
326
|
+
info['ntp.version'] = ntp_version
|
327
|
+
|
328
|
+
# NTP 2 & 3 share a common header, so parse those together
|
329
|
+
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 ...
|
350
|
+
if info['ntp.mode'] == 7
|
351
|
+
return info if data.size < 4
|
352
|
+
mode7_data = data.slice!(0,4).unpack('n*')
|
353
|
+
info['ntp.mode7.err'] = mode7_data.first >> 11
|
354
|
+
info['ntp.mode7.data_items_count'] = mode7_data.first & 0b0000111111111111
|
355
|
+
info['ntp.mode7.mbz'] = mode7_data.last >> 11
|
356
|
+
info['ntp.mode7.data_item_size'] = mode7_data.last & 0b0000111111111111
|
357
|
+
|
358
|
+
# extra monlist response data
|
359
|
+
if ntp_rcode == 42
|
360
|
+
if info['ntp.mode7.data_item_size'] == 72
|
361
|
+
remote_addresses = []
|
362
|
+
local_addresses = []
|
363
|
+
idx = 0
|
364
|
+
1.upto(info['ntp.mode7.data_items_count']) do
|
365
|
+
|
366
|
+
#u_int32 firsttime; /* first time we received a packet */
|
367
|
+
#u_int32 lasttime; /* last packet from this host */
|
368
|
+
#u_int32 restr; /* restrict bits (was named lastdrop) */
|
369
|
+
#u_int32 count; /* count of packets received */
|
370
|
+
#u_int32 addr; /* host address V4 style */
|
371
|
+
#u_int32 daddr; /* destination host address */
|
372
|
+
#u_int32 flags; /* flags about destination */
|
373
|
+
#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']
|
379
|
+
end
|
380
|
+
|
381
|
+
info['ntp.monlist.remote_addresses'] = remote_addresses.join(' ')
|
382
|
+
info['ntp.monlist.remote_addresses.count'] = remote_addresses.size
|
383
|
+
info['ntp.monlist.local_addresses'] = local_addresses.join(' ')
|
384
|
+
info['ntp.monlist.local_addresses.count'] = local_addresses.size
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
elsif ntp_version == 4
|
389
|
+
info['ntp.leap_indicator'] = ntp_flags >> 6
|
390
|
+
info['ntp.mode'] = ntp_flags & 0b00000111
|
391
|
+
info['ntp.peer.stratum'], info['ntp.peer.interval'], info['ntp.peer.precision'] = data.slice!(0,3).unpack('C*')
|
392
|
+
info['ntp.root.delay'], info['ntp.root.dispersion'], info['ntp.ref_id'] = data.slice!(0,12).unpack('N*')
|
393
|
+
info['ntp.timestamp.reference'], info['ntp.timestamp.origin'], info['ntp.timestamp.receive'], info['ntp.timestamp.transmit'] = data.slice!(0,32).unpack('Q*')
|
394
|
+
end
|
395
|
+
|
396
|
+
info
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
end
|
401
|
+
end
|