dap 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +6 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +15 -0
  4. data/Gemfile.lock +55 -0
  5. data/LICENSE +20 -0
  6. data/README.md +15 -0
  7. data/bin/dap +137 -0
  8. data/dap.gemspec +42 -0
  9. data/data/.gitkeep +0 -0
  10. data/lib/dap.rb +101 -0
  11. data/lib/dap/filter.rb +8 -0
  12. data/lib/dap/filter/base.rb +37 -0
  13. data/lib/dap/filter/geoip.rb +72 -0
  14. data/lib/dap/filter/http.rb +173 -0
  15. data/lib/dap/filter/names.rb +151 -0
  16. data/lib/dap/filter/openssl.rb +53 -0
  17. data/lib/dap/filter/recog.rb +23 -0
  18. data/lib/dap/filter/simple.rb +340 -0
  19. data/lib/dap/filter/udp.rb +401 -0
  20. data/lib/dap/input.rb +74 -0
  21. data/lib/dap/input/csv.rb +60 -0
  22. data/lib/dap/input/warc.rb +81 -0
  23. data/lib/dap/output.rb +117 -0
  24. data/lib/dap/proto/addp.rb +0 -0
  25. data/lib/dap/proto/dtls.rb +21 -0
  26. data/lib/dap/proto/ipmi.rb +94 -0
  27. data/lib/dap/proto/natpmp.rb +19 -0
  28. data/lib/dap/proto/wdbrpc.rb +58 -0
  29. data/lib/dap/utils/oui.rb +16586 -0
  30. data/lib/dap/version.rb +3 -0
  31. data/samples/http_get_reply.ic12.bz2 +0 -0
  32. data/samples/http_get_reply.ic12.sh +1 -0
  33. data/samples/http_get_reply_iframes.json.bz2 +0 -0
  34. data/samples/http_get_reply_iframes.json.sh +1 -0
  35. data/samples/http_get_reply_links.json.sh +1 -0
  36. data/samples/iawide.warc.bz2 +0 -0
  37. data/samples/iawide_warc.sh +1 -0
  38. data/samples/ipmi_chan_auth_replies.crd.bz2 +0 -0
  39. data/samples/ipmi_chan_auth_replies.sh +1 -0
  40. data/samples/ssl_certs.bz2 +0 -0
  41. data/samples/ssl_certs_geo.sh +1 -0
  42. data/samples/ssl_certs_names.sh +1 -0
  43. data/samples/ssl_certs_names_expanded.sh +1 -0
  44. data/samples/ssl_certs_org.sh +1 -0
  45. data/samples/udp-netbios.csv.bz2 +0 -0
  46. data/samples/udp-netbios.sh +1 -0
  47. data/spec/dap/proto/ipmi_spec.rb +19 -0
  48. data/tools/geo-ip-summary.rb +149 -0
  49. data/tools/ipmi-vulns.rb +27 -0
  50. data/tools/json-summarize.rb +81 -0
  51. data/tools/netbios-counts.rb +271 -0
  52. data/tools/upnp-vulns.rb +35 -0
  53. data/tools/value-counts-to-md-table.rb +23 -0
  54. metadata +264 -0
@@ -0,0 +1,3 @@
1
+ module Dap
2
+ VERSION = "0.0.1"
3
+ end
Binary file
@@ -0,0 +1 @@
1
+ bzcat http_get_reply.ic12.bz2 | ../bin/dap lines + field_split_tab line + rename line.f1=ip line.f4=data + select ip data + transform data=qprintdecode + decode_http_reply data + select ip data.http_code data.http_server + json
@@ -0,0 +1 @@
1
+ bzcat http_get_reply_iframes.json.bz2 | ../bin/dap json + transform data=base64decode + include data='<iframe' + html_iframes data + select ip iframe + json
@@ -0,0 +1 @@
1
+ bzcat http_get_reply_iframes.json.bz2 | ../bin/dap json + transform data=base64decode + html_links data + select ip link element + decode_uri link + json
Binary file
@@ -0,0 +1 @@
1
+ bzcat iawide.warc.bz2 | ../bin/dap warc + html_links content + select ip link element + decode_uri link + json
@@ -0,0 +1 @@
1
+ bzcat ipmi_chan_auth_replies.crd.bz2 | ../bin/dap lines + field_split_tab line + rename line.f2=ip line.f6=data + select ip data + transform data=hexdecode + decode_ipmi_chan_auth_reply data + remove data + json
Binary file
@@ -0,0 +1 @@
1
+ bzcat ssl_certs.bz2 | ../bin/dap json + select host_ip ssl_version port cipher + geo_ip host_ip + json
@@ -0,0 +1 @@
1
+ bzcat ssl_certs.bz2 | ../bin/dap json + field_split_array certs + transform certs.f1=base64decode + remove certs + decode_x509 certs.f1 + select certs.f1.names + exists certs.f1.names + split_array certs.f1.names + select certs.f1.names.item + exists certs.f1.names.item + lines
@@ -0,0 +1 @@
1
+ bzcat ssl_certs.bz2 | ../bin/dap json + field_split_array certs + transform certs.f1=base64decode + remove certs + decode_x509 certs.f1 + select certs.f1.names + exists certs.f1.names + split_array certs.f1.names + select certs.f1.names.item + exists certs.f1.names.item + rename certs.f1.names.item=hostname + extract_hostname hostname + select hostname.hostname + split_domains hostname.hostname + select hostname.hostname.domain + rename hostname.hostname.domain=name + prepend_subdomains name=www,dns,mail,vpn,secure,ssl + json
@@ -0,0 +1 @@
1
+ bzcat ssl_certs.bz2 | ../bin/dap json + select host_ip ssl_version port cipher + geo_ip_org host_ip + json
Binary file
@@ -0,0 +1 @@
1
+ bzcat udp-netbios.csv.bz2 | ../bin/dap csv - header=y + select saddr data + rename saddr=ip + transform data=hexdecode + decode_netbios_status_reply data + remove data + geo_ip ip + json
@@ -0,0 +1,19 @@
1
+ require 'bit-struct'
2
+ require_relative '../../../lib/dap/proto/ipmi'
3
+
4
+ module Dap
5
+ module Proto
6
+ module IPMI
7
+
8
+ describe Channel_Auth_Reply do
9
+ it "valid with the proper rmcp version and message length" do
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
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env ruby
2
+ require 'oj'
3
+ require 'optparse'
4
+
5
+ class GeoIPSummary
6
+ attr_accessor :country_name, :region_name, :city_name, :tree
7
+ #
8
+ # Pass the hash keys for the country name, region name and
9
+ # city name that we'll encounter during the process_hash function.
10
+ #
11
+ def initialize(country_name, region_name, city_name)
12
+ @country_name = country_name
13
+ @region_name = region_name
14
+ @city_name = city_name
15
+ @tree = {}
16
+ @tree['count'] = 0
17
+ end
18
+
19
+ 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'
23
+
24
+ # Create subhashes and values as needed on down the tree
25
+ @tree[country] ||= {}
26
+ @tree[country]['count'] ||=0
27
+ @tree[country][region] ||= {}
28
+ @tree[country][region]['count'] ||= 0
29
+ @tree[country][region][city] ||= 0
30
+
31
+ # Now increment counters
32
+ @tree['count'] += 1
33
+ @tree[country]['count'] += 1
34
+ @tree[country][region]['count'] +=1
35
+ @tree[country][region][city] += 1
36
+ end
37
+
38
+ # Performs the final sorting of the hash, with descending order of counts
39
+ #
40
+ def order_tree
41
+ @tree.each do | country, country_hash|
42
+ if country != 'count'
43
+ country_hash.each do | region, region_hash |
44
+ @tree[country][region] = order_hash(@tree[country][region]) if region != 'count'
45
+ end
46
+ @tree[country] = order_hash(@tree[country])
47
+ end
48
+ end
49
+ @tree = order_hash(@tree)
50
+ end
51
+
52
+ private
53
+
54
+ # Sorts the hash, and returns a copy of the hash in sorted order by their counts, or if
55
+ # counts are equal then by their names.
56
+ def order_hash(h)
57
+ keys = h.keys.sort { | k1,k2 |
58
+ if k1 == 'count'
59
+ ret = -1
60
+ elsif k2 == 'count'
61
+ ret = 1
62
+ else
63
+ # Cities level is slightly different form, if hash at this level then compare
64
+ # count value within hash, otherwise just compare values. mult by -1 to reverse
65
+ # ordering
66
+ if h[k1].class == Hash
67
+ ret = ( h[k1]['count'] <=> h[k2]['count'] ) * -1
68
+ ret = k1 <=> k2 if ret == 0 && k1!=nil && k2!=nil
69
+ else
70
+ ret = ( h[k1] <=> h[k2] ) * -1
71
+ ret = k1 <=> k2 if ret == 0 && k1!=nil && k2!=nil
72
+ end
73
+ end
74
+ ret
75
+ }
76
+
77
+ # build up return hash
78
+ ret_hash = {}
79
+ keys.each do | key |
80
+ ret_hash[key] = h[key]
81
+ end
82
+
83
+ ret_hash
84
+ end
85
+ end
86
+ HELP=<<EOF
87
+ This script is used to summarize geoip data from data in a json file. The name of the json element for
88
+ the country, region, and city must be provided. The output is a hash with the country/region/city data and
89
+ the count of occurrences from the input file; this output hash is sorted in count descending order so that
90
+ the most common country, region within a country, and city within a region is returned first.
91
+
92
+ Example with dap:
93
+ bzcat ../samples/ssl_certs.bz2 | ../bin/dap json + select host_ip + geo_ip host_ip + json | ./geo-ip-summary.rb --var host_ip > /tmp/ssl_geo.json
94
+ EOF
95
+
96
+ def parse_command_line(args)
97
+
98
+ options={
99
+ :country => nil,
100
+ :region => nil,
101
+ :city => nil,
102
+ :var => nil
103
+ }
104
+
105
+ OptionParser.new do | opts |
106
+ opts.banner = HELP
107
+ opts.separator ''
108
+
109
+ opts.separator 'GeoIP name options:'
110
+
111
+ opts.on( '--country country_key', 'The name of json key for the country.') do | val |
112
+ options[:country] = val
113
+ end
114
+
115
+ opts.on( '--region region_key', 'The name of the json key for the region.') do | val |
116
+ options[:region] = val
117
+ end
118
+
119
+ opts.on( '--city city_key', 'The name of the json key for the city.' ) do | val |
120
+ options[:city] = val
121
+ end
122
+
123
+ opts.on('--var top-level-var', 'Sets the top level json name, for defining all of country/region/city') do | val |
124
+ options[:var] = val
125
+ options[:country] = "#{val}.country_name"
126
+ options[:region] = "#{val}.region"
127
+ options[:city] = "#{val}.city"
128
+ end
129
+
130
+ opts.on_tail('-h', '--help', 'Show this message') do
131
+ puts opts
132
+ exit(0)
133
+ end
134
+ opts.parse!(args)
135
+ options
136
+ end
137
+ options
138
+ end
139
+ opts = parse_command_line(ARGV)
140
+ raise 'Need json key names for country,region and city.' if opts[:country].nil? || opts[:region].nil? || opts[:city].nil?
141
+
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
+
147
+ Oj.default_options={:indent=>2}
148
+
149
+ puts Oj.dump(summarizer.order_tree)
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'oj'
4
+
5
+ SEARCHES = {
6
+ "data.ipmi_compat_password" => { value: "1", name: "straight-pass" },
7
+ "data.ipmi_compat_md2" => { value: "1", name: "md2" },
8
+ "data.ipmi_compat_none" => { value: "1", name: "noauth" },
9
+ "data.ipmi_user_disable_message_auth" => { value: "1", name: "permsg" },
10
+ "data.ipmi_user_disable_user_auth" => { value: "1", name: "usrlvl" }
11
+ }
12
+
13
+ def search(hash)
14
+ SEARCHES.each do | key, vuln |
15
+ if hash[key] == vuln[:value]
16
+ hash["VULN-IPMI-#{vuln[:name].upcase}"] = "true"
17
+ end
18
+ end
19
+ if (hash['data.ipmi_user_non_null'] == "0") && (hash['data.ipmi_user_null'] == "0")
20
+ hash["VULN-IPMI-ANON"] = "true"
21
+ end
22
+ hash
23
+ end
24
+
25
+ while line=gets
26
+ puts Oj.dump(search(Oj.load(line.strip)))
27
+ end
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ require 'oj'
3
+ require 'optparse'
4
+
5
+ HELP=<<EOF
6
+ This script is used to locate the frequency of a given key in a json document. It will
7
+ inspect and increment the frequency count for each instance of the key found in the json
8
+ document, then order them in descending order and output a json document with the top n
9
+ occurrences of the key value.
10
+
11
+ Note that if passed a key that has unique values, this script can consume a lot of memory.
12
+
13
+ Sample:
14
+ unpigz -c /tmp/2014-05-05-mssql-udp-decoded.json.gz | ruby ~/src/dap/tools/json-summarize.rb --top 20 --key data.mssql.Version
15
+ EOF
16
+
17
+ def parse_command_line(args)
18
+
19
+ options={
20
+ :key => nil,
21
+ :number => nil
22
+ }
23
+
24
+ OptionParser.new do | opts |
25
+ opts.banner = HELP
26
+ opts.separator ''
27
+
28
+ opts.separator 'GeoIP name options:'
29
+
30
+ opts.on( '--key keyname', 'The name of json key to be summarized.') do | val |
31
+ options[:key] = val
32
+ end
33
+
34
+ opts.on( '--top num_items', 'Return top n occurrences.') do | val |
35
+ options[:number] = val.to_i
36
+ end
37
+
38
+ opts.on_tail('-h', '--help', 'Show this message') do
39
+ puts opts
40
+ exit(0)
41
+ end
42
+ opts.parse!(args)
43
+ options
44
+ end
45
+ options
46
+ end
47
+
48
+ # Sorts the hash in descending numerical value for the values
49
+ # part of the hash, returning the sorted hash.
50
+ #
51
+ def order_hash(h)
52
+ keys = h.keys.sort { | k1,k2 |
53
+ ret = ( h[k1] <=> h[k2] ) * -1
54
+ ret = k1 <=> k2 if ret == 0 && k1!=nil && k2!=nil
55
+ ret
56
+ }
57
+ # build up return hash
58
+ ret_hash = {}
59
+ keys.each do | key |
60
+ ret_hash[key] = h[key]
61
+ end
62
+
63
+ ret_hash
64
+ end
65
+
66
+
67
+
68
+
69
+ summary={}
70
+ opts = parse_command_line(ARGV)
71
+ key = opts[:key]
72
+
73
+ while line = gets
74
+ val = Oj.load(line.chomp.strip)[key]
75
+ summary[val] ||= 0
76
+ summary[val] += 1
77
+ end
78
+
79
+ summary = Hash[ *order_hash(summary).flatten.slice(0,2*opts[:number]) ]
80
+ puts Oj.dump(summary)
81
+
@@ -0,0 +1,271 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'ostruct'
5
+ require 'oj'
6
+ require 'json'
7
+
8
+ options = OpenStruct.new
9
+ options.top_count = 5
10
+ options.exclude_default_counts = false
11
+
12
+ OptionParser.new do |opts|
13
+ opts.banner = "Usage: netbios-counts.rb [options]"
14
+
15
+ opts.on("-c", "--count [NUM]", OptionParser::DecimalInteger,
16
+ "Specify the number of top count results") do |count|
17
+ options.top_count = count if count > 1
18
+ end
19
+
20
+ opts.on("--count-hostnames-containing [TEXT]", "Count hostnames that include the speified text") do |text|
21
+ options.hostname_containing = text
22
+ end
23
+
24
+ opts.on("--exclude-default-counts", "Exclude the provided top counts") do
25
+ options.exclude_default_counts = true
26
+ end
27
+ end.parse!
28
+
29
+ NUM_TOP_RECORDS = options.top_count
30
+
31
+ module Counter
32
+ def count(hash)
33
+ value = countable_value(hash)
34
+ @counts[value] += 1 unless (value.empty? || value == 'UNKNOWN')
35
+ end
36
+
37
+ def top_counts
38
+ [].tap do |counts|
39
+ ordered_by_count.to_a.take(NUM_TOP_RECORDS).each do |values|
40
+ counts << count_hash(values)
41
+ end
42
+ end
43
+ end
44
+
45
+ def ordered_by_count
46
+ Hash[@counts.sort_by{|k, v| v}.reverse]
47
+ end
48
+ end
49
+
50
+ class CompanyNameCounter
51
+ include Counter
52
+
53
+ def initialize
54
+ @counts = Hash.new(0)
55
+ end
56
+
57
+ def countable_value(hash)
58
+ hash['data.netbios_mac_company'].to_s
59
+ end
60
+
61
+ def count_hash(values)
62
+ { 'name' => values[0], 'count' => values[1] }
63
+ end
64
+
65
+ def apply_to(hash)
66
+ hash['top_companies'] = top_counts
67
+ end
68
+ end
69
+
70
+ class NetbiosNameCounter
71
+ include Counter
72
+
73
+ def initialize
74
+ @counts = Hash.new(0)
75
+ end
76
+
77
+ def countable_value(hash)
78
+ hash['data.netbios_hname'].to_s
79
+ end
80
+
81
+ def count_hash(values)
82
+ { 'hostname' => values[0], 'count' => values[1] }
83
+ end
84
+
85
+ def apply_to(hash)
86
+ hash['top_netbios_hostnames'] = top_counts
87
+ end
88
+ end
89
+
90
+ class MacAddressCounter
91
+ include Counter
92
+
93
+ def initialize
94
+ @counts = Hash.new(0)
95
+ end
96
+
97
+ def countable_value(hash)
98
+ address = hash['data.netbios_mac'].to_s
99
+ [].tap do |data|
100
+ unless (address.empty? || address == '00:00:00:00:00:00')
101
+ data << address
102
+ data << hash['data.netbios_hname']
103
+ data << hash['data.netbios_mac_company']
104
+ end
105
+ end
106
+ end
107
+
108
+ def count_hash(values)
109
+ {
110
+ 'mac_address' => values[0][0],
111
+ 'hostname' => values[0][1],
112
+ 'company' => values[0][2],
113
+ 'count' => values[1]
114
+ }
115
+ end
116
+
117
+ def apply_to(hash)
118
+ hash['top_mac_addresses'] = top_counts
119
+ end
120
+ end
121
+
122
+ class GeoCounter
123
+ def initialize
124
+ @cities = Hash.new(0)
125
+ @countries = Hash.new(0)
126
+ @regions = Hash.new(0)
127
+ end
128
+
129
+ def count(hash)
130
+ city = hash['ip.city'].to_s
131
+ country_code = hash['ip.country_code'].to_s
132
+ region = hash['ip.region'].to_s
133
+ region_name = hash['ip.region_name'].to_s
134
+
135
+ @cities[[city, country_code]] += 1 unless city.empty?
136
+ @countries[country_code] += 1 unless country_code.empty?
137
+ @regions[[region, region_name]] += 1 unless region.empty?
138
+ end
139
+
140
+ def top_cities
141
+ [].tap do |counts|
142
+ ordered_cities.to_a.take(NUM_TOP_RECORDS).each do |values|
143
+ counts << {
144
+ 'city' => values[0][0],
145
+ 'country_code' => values[0][1],
146
+ 'count' => values[1]
147
+ }
148
+ end
149
+ end
150
+ end
151
+
152
+ def top_countries
153
+ [].tap do |counts|
154
+ ordered_countries.to_a.take(NUM_TOP_RECORDS).each do |values|
155
+ counts << { 'country_code' => values[0], 'count' => values[1] }
156
+ end
157
+ end
158
+ end
159
+
160
+ def top_regions
161
+ [].tap do |counts|
162
+ ordered_regions.to_a.take(NUM_TOP_RECORDS).each do |values|
163
+ counts << {
164
+ 'region' => values[0][0],
165
+ 'region_name' => values[0][1],
166
+ 'count' => values[1]
167
+ }
168
+ end
169
+ end
170
+ end
171
+
172
+ def ordered_cities
173
+ Hash[@cities.sort_by{|k, v| v}.reverse]
174
+ end
175
+
176
+ def ordered_countries
177
+ Hash[@countries.sort_by{|k, v| v}.reverse]
178
+ end
179
+
180
+ def ordered_regions
181
+ Hash[@regions.sort_by{|k, v| v}.reverse]
182
+ end
183
+
184
+ def apply_to(hash)
185
+ hash['top_cities'] = top_cities unless top_cities.empty?
186
+ hash['top_countries'] = top_countries unless top_countries.empty?
187
+ hash['top_regions'] = top_regions unless top_regions.empty?
188
+ end
189
+ end
190
+
191
+ class SambaCounter
192
+ include Counter
193
+
194
+ def initialize
195
+ @counts = Hash.new(0)
196
+ end
197
+
198
+ def countable_value(hash)
199
+ address = hash['data.netbios_mac'].to_s
200
+ if (address == '00:00:00:00:00:00')
201
+ hash['data.netbios_hname']
202
+ else
203
+ ''
204
+ end
205
+ end
206
+
207
+ def count_hash(values)
208
+ { 'name' => values[0], 'count' => values[1] }
209
+ end
210
+
211
+ def apply_to(hash)
212
+ hash['top_samba_names'] = top_counts
213
+ end
214
+ end
215
+
216
+ class HostnameContainingCounter
217
+ include Counter
218
+
219
+ def initialize(text)
220
+ @text = text
221
+ @counts = Hash.new(0)
222
+ end
223
+
224
+ def countable_value(hash)
225
+ hostname = hash['data.netbios_hname'].to_s
226
+ [].tap do |data|
227
+ if hostname.include?(@text)
228
+ data << hostname
229
+ data << hash['data.netbios_mac_company']
230
+ data << hash['ip.city'].to_s
231
+ data << hash['ip.country_code'].to_s
232
+ data << hash['ip.country_name'].to_s
233
+ end
234
+ end
235
+ end
236
+
237
+ def count_hash(values)
238
+ {
239
+ 'hostname' => values[0][0],
240
+ 'company' => values[0][1],
241
+ 'city' => values[0][2],
242
+ 'country_code' => values[0][3],
243
+ 'country_name' => values[0][4],
244
+ 'count' => values[1]
245
+ }
246
+ end
247
+
248
+ def apply_to(hash)
249
+ hash["hostnames with '#{@text}'"] = top_counts
250
+ end
251
+ end
252
+
253
+ counters = []
254
+ unless options.exclude_default_counts
255
+ counters << CompanyNameCounter.new
256
+ counters << NetbiosNameCounter.new
257
+ counters << MacAddressCounter.new
258
+ counters << GeoCounter.new
259
+ counters << SambaCounter.new
260
+ end
261
+ counters << HostnameContainingCounter.new(options.hostname_containing) unless options.hostname_containing.nil?
262
+
263
+ while line=gets
264
+ hash = Oj.load(line.strip)
265
+ counters.each { |counter| counter.count(hash) }
266
+ end
267
+
268
+ summary = {}
269
+ counters.each { |counter| counter.apply_to(summary) }
270
+
271
+ puts JSON.pretty_generate(summary)