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,72 @@
1
+ require 'geoip'
2
+
3
+ module Dap
4
+ module Filter
5
+
6
+ module GeoIPLibrary
7
+ GEOIP_DIRS = [
8
+ File.expand_path( File.join( File.dirname(__FILE__), "..", "..", "..", "data")),
9
+ "/var/lib/geoip"
10
+ ]
11
+ GEOIP_CITY = %W{ geoip.dat geoip_city.dat GeoCity.dat IP_V4_CITY.dat GeoCityLite.dat }
12
+ GEOIP_ORGS = %W{ geoip_org.dat IP_V4_ORG.dat }
13
+
14
+ @@geo_city = nil
15
+ @@geo_orgs = nil
16
+
17
+ GEOIP_DIRS.each do |d|
18
+ GEOIP_CITY.each do |f|
19
+ path = File.join(d, f)
20
+ if ::File.exist?(path)
21
+ @@geo_city = GeoIP::City.new(path)
22
+ break
23
+ end
24
+ end
25
+ GEOIP_ORGS.each do |f|
26
+ path = File.join(d, f)
27
+ if ::File.exist?( path )
28
+ @@geo_orgs = GeoIP::Organization.new(path)
29
+ break
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+
36
+ #
37
+ # Add GeoIP tags using the MaxMind GeoIP::City
38
+ #
39
+ class FilterGeoIP
40
+ include BaseDecoder
41
+ include GeoIPLibrary
42
+ def decode(ip)
43
+ return unless @@geo_city
44
+ geo_hash = @@geo_city.look_up(ip)
45
+ return unless geo_hash
46
+ ret = {}
47
+ geo_hash.each_pair do |k,v|
48
+ next unless k
49
+ ret[k.to_s] = v.to_s
50
+ end
51
+
52
+ ret
53
+ end
54
+ end
55
+
56
+ #
57
+ # Add GeoIP tags using the MaxMind GeoIP::Organization database
58
+ #
59
+ class FilterGeoIPOrg
60
+ include BaseDecoder
61
+ include GeoIPLibrary
62
+ def decode(ip)
63
+ return unless @@geo_orgs
64
+ geo_hash = @@geo_orgs.look_up(ip)
65
+ return unless (geo_hash and geo_hash[:name])
66
+ { :org => geo_hash[:name] }
67
+ end
68
+ end
69
+
70
+
71
+ end
72
+ end
@@ -0,0 +1,173 @@
1
+ module Dap
2
+ module Filter
3
+
4
+ require 'htmlentities'
5
+ require 'nokogiri'
6
+ require 'uri'
7
+
8
+ class FilterHTMLIframes
9
+ include Base
10
+
11
+ def process(doc)
12
+ out = []
13
+ self.opts.each_pair do |k,v|
14
+ next unless doc.has_key?(k)
15
+ extract(doc[k]).each do |url|
16
+ out << doc.merge({ 'iframe' => url })
17
+ end
18
+ end
19
+ out
20
+ end
21
+
22
+ def extract(data)
23
+ @coder ||= HTMLEntities.new
24
+ urls = []
25
+
26
+ data = data.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
27
+ html = nil
28
+ begin
29
+ html = Nokogiri::HTML(data) do |conf|
30
+ conf.strict.noent
31
+ end
32
+ rescue ::Exception
33
+ return urls
34
+ end
35
+
36
+ html.xpath('//iframe').each do |e|
37
+ url = e['src']
38
+ next unless url
39
+ urls << url
40
+ end
41
+
42
+ urls
43
+ end
44
+ end
45
+
46
+
47
+ class FilterHTMLLinks
48
+ include Base
49
+
50
+ def process(doc)
51
+ out = []
52
+ self.opts.each_pair do |k,v|
53
+ next unless doc.has_key?(k)
54
+ extract(doc[k]).each do |link_info|
55
+ out << doc.merge(link_info)
56
+ end
57
+ end
58
+ out
59
+ end
60
+
61
+ def extract(data)
62
+ urls = []
63
+
64
+ data = data.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
65
+ html = nil
66
+ begin
67
+ html = Nokogiri::HTML(data) do |conf|
68
+ conf.strict.noent
69
+ end
70
+ rescue ::Exception
71
+ return urls
72
+ end
73
+
74
+ html.xpath('//*').each do |e|
75
+ url = e['href'] || e['src']
76
+ next unless url
77
+ urls << { 'link' => url, 'element' => e.name }
78
+ end
79
+
80
+ urls
81
+ end
82
+ end
83
+
84
+ class FilterDecodeURI
85
+ include BaseDecoder
86
+ def decode(data)
87
+ save = {}
88
+ uri = URI.parse(data) rescue nil
89
+ return unless uri
90
+
91
+ save["host"] = uri.host if uri.host
92
+ save["port"] = uri.port.to_s if uri.port
93
+ save["path"] = uri.path if uri.path
94
+ save["query"] = uri.query if uri.query
95
+ save["scheme"] = uri.scheme if uri.scheme
96
+ save["user"] = uri.user if uri.user
97
+ save["password"] = uri.password if uri.password
98
+
99
+ save
100
+ end
101
+ end
102
+
103
+
104
+ class FilterDecodeHTTPReply
105
+ include BaseDecoder
106
+
107
+ # TODO: Decode transfer-chunked responses
108
+ def decode(data)
109
+ lines = data.split(/\r?\n/)
110
+ resp = lines.shift
111
+ save = {}
112
+ return save if resp !~ /^HTTP\/\d+\.\d+\s+(\d+)\s+(.*)/
113
+
114
+ save["http_code"] = $1.to_i
115
+ save["http_message"] = $2.strip
116
+
117
+ clen = nil
118
+
119
+ while lines.length > 0
120
+ hline = lines.shift
121
+ case hline
122
+ when /^ETag:\s*(.*)/i
123
+ save["http_etag"] = $1
124
+
125
+ when /^Set-Cookie:\s*(.*)/i
126
+ bits = $1.gsub(/\;?\s*path=.*/i, '').gsub(/\;?\s*expires=.*/i, '').gsub(/\;\s*HttpOnly.*/, '')
127
+ save["http_cookie"] = bits.strip
128
+
129
+ when /^Server:\s*(.*)/i
130
+ save["http_server"] = $1.strip
131
+
132
+ when /^X-Powered-By:\s*(.*)/i
133
+ save["http_powered"] = $1.strip
134
+
135
+ when /^Date:\s*(.*)/i
136
+ d = DateTime.parse($1.strip) rescue nil
137
+ save["http_date"] = d.to_time.strftime("%Y%m%dT%H:%M:%S") if d
138
+
139
+ when /^Last-modified:\s*(.*)/i
140
+ d = DateTime.parse($1.strip) rescue nil
141
+ save["http_modified"] = d.to_time.strftime("%Y%m%dT%H:%M:%S") if d
142
+
143
+ when /^Location:\s*(.*)/i
144
+ save["http_location"] = $1.strip
145
+
146
+ when /^WWW-Authenticate:\s*(.*)/i
147
+ save["http_auth"] = $1.strip
148
+
149
+ when /^Content-Length:\s*(.*)/i
150
+ clen = $1.strip.to_i
151
+
152
+ when ""
153
+ break
154
+ end
155
+ end
156
+
157
+ head, body = data.split(/\r?\n\r?\n/, 2)
158
+
159
+ # Some buggy systems exclude the header entirely
160
+ body ||= head
161
+
162
+ save["http_body"] = body
163
+
164
+ if body =~ /<title>([^>]+)</min
165
+ save["http_title"] = $1.strip
166
+ end
167
+
168
+ save
169
+ end
170
+ end
171
+
172
+ end
173
+ end
@@ -0,0 +1,151 @@
1
+ module Dap
2
+ module Filter
3
+
4
+ MATCH_FQDN = /^([a-z0-9\_\-]+\.)+[a-z0-9\-]+\.?$/
5
+
6
+
7
+ class FilterExtractHostname
8
+ include BaseDecoder
9
+ def decode(data)
10
+ data = data.strip.gsub(/.*\@/, '').gsub(/^\*+/, '').gsub(/^\.+/, '').gsub(/\.+$/, '').downcase
11
+ return unless data =~ MATCH_FQDN
12
+
13
+ # https://data.iana.org/TLD/tlds-alpha-by-domain.txt
14
+ return unless %w{
15
+ AC AD AE AERO AF AG AI AL AM AN AO AQ AR ARPA AS ASIA AT AU AW AX AZ BA BB BD BE BF BG BH BI BIKE BIZ
16
+ BJ BM BN BO BR BS BT BV BW BY BZ CA CAMERA CAT CC CD CF CG CH CI CK CL CLOTHING CM CN CO COM CONSTRUCTION
17
+ CONTRACTORS COOP CR CU CV CW CX CY CZ DE DJ DK DM DO DZ EC EDU EE EG EQUIPMENT ER ES ESTATE ET EU FI FJ FK
18
+ FM FO FR GA GALLERY GB GD GE GF GG GH GI GL GM GN GOV GP GQ GR GRAPHICS GS GT GU GURU GW GY HK HM HN
19
+ HOLDINGS HR HT HU ID IE IL IM IN INFO INT IO IQ IR IS IT JE JM JO JOBS JP KE KG KH KI KM KN KP KR KW KY KZ
20
+ LA LAND LB LC LI LIGHTING LK LR LS LT LU LV LY MA MC MD ME MG MH MIL MK ML MM MN MO MOBI MP MQ MR MS MT MU
21
+ MUSEUM MV MW MX MY MZ NA NAME NC NE NET NF NG NI NL NO NP NR NU NZ OM ORG PA PE PF PG PH PK PL PLUMBING PM
22
+ PN POST PR PRO PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SEXY SG SH SI SINGLES SJ SK SL SM SN SO SR ST
23
+ SU SV SX SY SZ TATTOO TC TD TECHNOLOGY TEL TF TG TH TJ TK TL TM TN TO TP TR TRAVEL TT TV TW TZ UA UG UK US
24
+ UY UZ VA VC VE VENTURES VG VI VN VOYAGE VU WF WS XN--3E0B707E XN--45BRJ9C XN--80AO21A XN--80ASEHDB
25
+ XN--80ASWG XN--90A3AC XN--CLCHC0EA0B2G2A9GCD XN--FIQS8S XN--FIQZ9S XN--FPCRJ9C3D XN--FZC2C9E2C XN--GECRJ9C
26
+ XN--H2BRJ9C XN--J1AMH XN--J6W193G XN--KPRW13D XN--KPRY57D XN--L1ACC XN--LGBBAT1AD8J XN--MGB9AWBF
27
+ XN--MGBA3A4F16A XN--MGBAAM7A8H XN--MGBAYH7GPA XN--MGBBH1A71E XN--MGBC0A9AZCG XN--MGBERP4A5D4AR
28
+ XN--MGBX4CD0AB XN--NGBC5AZD XN--O3CW4H XN--OGBPF8FL XN--P1AI XN--PGBS0DH XN--S9BRJ9C XN--UNUP4Y
29
+ XN--WGBH1C XN--WGBL6A XN--XKC2AL3HYE2A XN--XKC2DL3A5EE0H XN--YFRO4I67O XN--YGBI2AMMX XXX YE YT ZA ZM ZW
30
+ }.include?(data.split('.').last.upcase)
31
+
32
+ { 'hostname' => data }
33
+ end
34
+ end
35
+
36
+ class FilterSplitDomains
37
+ include Base
38
+ def process(doc)
39
+ lines = [ ]
40
+ self.opts.each_pair do |k,v|
41
+ if doc.has_key?(k)
42
+ expand(doc[k]).each do |line|
43
+ lines << doc.merge({ "#{k}.domain" => line })
44
+ end
45
+ end
46
+ end
47
+ lines.length == 0 ? [ doc ] : [ lines ]
48
+ end
49
+
50
+ def expand(data)
51
+ names = []
52
+ bits = data.split('.')
53
+ while (bits.length > 1)
54
+ names << bits.join('.')
55
+ bits.shift
56
+ end
57
+ names
58
+ end
59
+ end
60
+
61
+
62
+ class FilterPrependSubdomains
63
+ include Base
64
+ def process(doc)
65
+ lines = [ ]
66
+ self.opts.each_pair do |k,v|
67
+ if doc.has_key?(k)
68
+ expand(doc[k], v).each do |line|
69
+ lines << doc.merge({ k => line })
70
+ end
71
+ end
72
+ end
73
+ lines.length == 0 ? [ ] : [ lines ]
74
+ end
75
+
76
+ def expand(data, names)
77
+ outp = [ data ]
78
+ bits = data.split(".")
79
+ subs = names.split(",")
80
+
81
+ # Avoid www.www.domain.tld and mail.www.domain.tld
82
+ return outp if subs.include?(bits.first)
83
+ subs.each do |sub|
84
+ outp << "#{sub}.#{data}"
85
+ end
86
+
87
+ outp
88
+ end
89
+
90
+ end
91
+
92
+ #
93
+ # Acts like SplitDomains but strips out common dynamic IP RDNS formats
94
+ #
95
+ # XXX - Lots of work left to do
96
+ #
97
+
98
+ class FilterSplitNonDynamicDomains
99
+ include Base
100
+ def process(doc)
101
+ lines = [ ]
102
+ self.opts.each_pair do |k,v|
103
+ if doc.has_key?(k)
104
+ expand(doc[k]).each do |line|
105
+ lines << doc.merge({ "#{k}.domain" => line })
106
+ end
107
+ end
108
+ end
109
+ lines.length == 0 ? [ doc ] : [ lines ]
110
+ end
111
+
112
+ def expand(data)
113
+ names = []
114
+ data = data.unpack("C*").pack("C*").
115
+ gsub(/.*ip\d+\.ip\d+\.ip\d+\.ip\d+\./, '').
116
+ gsub(/.*\d+[\_\-\.x]\d+[\_\-\.x]\d+[\_\-\.x]\d+[^\.]+/, '').
117
+ gsub(/.*node-[a-z0-9]+.*pool.*dynamic\./, '').
118
+ gsub(/.*[a-z][a-z]\d+\.[a-z]as[a-z0-9]+\./, '').
119
+ # cl223.001033200.technowave.ne.jp
120
+ gsub(/^cl\d+.[0-9]{6,14}\./, '').
121
+ # n157.s1117.m-zone.jp
122
+ gsub(/^n\d+.s\d+\.m-zone.jp/, 'm-zone.jp').
123
+ # u570054.xgsnu2.imtp.tachikawa.mopera.net
124
+ # s505207.xgsspn.imtp.tachikawa.spmode.ne.jp
125
+ gsub(/^[us]\d+.xgs[a-z0-9]+\.imtp/, 'imtp').
126
+ # tzbm6501209.tobizaru.jp
127
+ gsub(/^tzbm[0-9]{6,9}\./, '').
128
+ # ARennes-556-1-256-bdcst.w2-14.abo.wanadoo.fr
129
+ gsub(/.*\-\d+\-\d+\-\d+\-(net|bdcst)\./, '').
130
+ # bl19-128-119.dsl.telepac.pt
131
+ gsub(/.*\d+\-\d+\-\d+\.dsl/, 'dsl').
132
+ gsub(/.*pool\./, '').
133
+ gsub(/.*dynamic\./, '').
134
+ gsub(/.*static\./, '').
135
+ gsub(/.*dhcp[^\.]+\./, '').
136
+ gsub(/^\d{6,100}\./, '').
137
+ gsub(/^\.+/, '').
138
+ tr('^a-z0-9.-', '')
139
+
140
+ bits = data.split('.')
141
+ while (bits.length > 1)
142
+ names << bits.join('.')
143
+ bits.shift
144
+ end
145
+ names
146
+ end
147
+ end
148
+
149
+
150
+ end
151
+ end
@@ -0,0 +1,53 @@
1
+ module Dap
2
+ module Filter
3
+
4
+ require 'openssl'
5
+
6
+ class FilterDecodeX509
7
+ include BaseDecoder
8
+
9
+ def decode(data)
10
+ save = {}
11
+ cert = OpenSSL::X509::Certificate.new(data) rescue nil
12
+ return unless cert
13
+
14
+ dnames = []
15
+ cert.subject.to_s.split("/").each do |bit|
16
+ var,val = bit.split("=", 2)
17
+ next unless (var and val)
18
+ var = var.to_s.downcase.strip
19
+ save["s_#{var}"] = val
20
+ if var == "cn"
21
+ dnames << val
22
+ end
23
+ end
24
+
25
+ cert.issuer.to_s.split("/").each do |bit|
26
+ var,val = bit.split("=", 2)
27
+ next unless (var and val)
28
+ var = var.to_s.downcase.strip
29
+ save["i_#{var}"] = val
30
+ end
31
+
32
+ cert.extensions.each do |e|
33
+ next unless e.to_s =~ /^([^\s]+)\s*=\s*(.*)/
34
+ var,val = $1,$2
35
+ var = var.to_s.downcase.strip
36
+ save["e_#{var}"] = val.strip
37
+
38
+ if var == "subjectaltname"
39
+ val.split(",").map{|x| x.gsub("DNS:", "").gsub("IP:", "").gsub("email:", "").strip }.each do |name|
40
+ dnames << name
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ save["names"] = dnames
47
+ save
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end