dap 0.0.4 → 0.0.5
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 +4 -4
- data/Gemfile +1 -2
- data/Gemfile.lock +2 -3
- data/data/vulndb.rb +33 -0
- data/lib/dap/filter/http.rb +62 -34
- data/lib/dap/filter/vulnmatch.rb +140 -0
- data/lib/dap/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5979d47a46ba9175ad7041e9ae9e27281c0bbae
|
4
|
+
data.tar.gz: ce96da3ec587d1174890396b7e8cc0ec0d9e8789
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5ae71d2855a533c4d06060c416686e691fb5e93439a456b1b68c6c3b92443ba5790c3b5baf4e2dc2c04f51c69623dcfebc59a87bec13955fb319fc4bd5f736f
|
7
|
+
data.tar.gz: 39a3e52480b4970ca5f95df73090172243a857dc2902a251e913fbf3fc03250f6483baf52f5b8fb3e53701020adcc8531fbf73b0a8a214c5ad6abfbf72b4dad2
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -28,7 +28,7 @@ GEM
|
|
28
28
|
nokogiri (1.6.3.1)
|
29
29
|
mini_portile (= 0.6.0)
|
30
30
|
oj (2.10.2)
|
31
|
-
recog (
|
31
|
+
recog (2.0.2)
|
32
32
|
nokogiri
|
33
33
|
rspec (3.1.0)
|
34
34
|
rspec-core (~> 3.1.0)
|
@@ -53,7 +53,6 @@ DEPENDENCIES
|
|
53
53
|
geoip-c
|
54
54
|
htmlentities
|
55
55
|
net-dns
|
56
|
-
nokogiri
|
57
56
|
oj
|
58
|
-
recog (>=
|
57
|
+
recog (>= 2.0)
|
59
58
|
rspec (~> 3.1.0)
|
data/data/vulndb.rb
CHANGED
@@ -80,4 +80,37 @@ SEARCHES = {
|
|
80
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
81
|
}
|
82
82
|
}],
|
83
|
+
|
84
|
+
:http => [
|
85
|
+
#### ELASTICSEARCH RCE
|
86
|
+
{
|
87
|
+
# direct shellcommand elastic rce
|
88
|
+
:match => [
|
89
|
+
['http.path', '/_search'],
|
90
|
+
['http.body', 'script_fields'],
|
91
|
+
['http.body', 'java.lang.Runtime'],
|
92
|
+
['http.body', 'getRuntime()'],
|
93
|
+
],
|
94
|
+
:cve => ['VULN-ELASTICSEARCH-RCE', 'CVE-2014-3120']
|
95
|
+
},{
|
96
|
+
# this just adds another tag as it's most likely done with metasploit
|
97
|
+
:match => [
|
98
|
+
['http.path', '/_search'],
|
99
|
+
['http.body', 'script_fields'],
|
100
|
+
['http.body', 'metasploit.Payload'],
|
101
|
+
],
|
102
|
+
:cve => ['VULN-ELASTICSEARCH-RCE', 'METASPLOIT']
|
103
|
+
}] + [
|
104
|
+
#### PHP CGI
|
105
|
+
{
|
106
|
+
:match => [
|
107
|
+
['http.path', '/cgi-bin/php'],
|
108
|
+
],
|
109
|
+
:cve => ['VULN-PHPCGI']
|
110
|
+
},{
|
111
|
+
:match => [
|
112
|
+
['http.path', '/cgi-bin/authLogin.cgi'],
|
113
|
+
],
|
114
|
+
:cve => ['VULN-QNAP-SHELLSHOCK']
|
115
|
+
}],
|
83
116
|
}
|
data/lib/dap/filter/http.rb
CHANGED
@@ -2,11 +2,62 @@ module Dap
|
|
2
2
|
module Filter
|
3
3
|
|
4
4
|
require 'htmlentities'
|
5
|
-
require '
|
5
|
+
require 'shellwords'
|
6
6
|
require 'uri'
|
7
7
|
|
8
|
+
# Dirty element extractor, works around memory issues with Nokogiri
|
9
|
+
module HTMLGhetto
|
10
|
+
def extract_elements(data)
|
11
|
+
@coder ||= HTMLEntities.new
|
12
|
+
res = []
|
13
|
+
data.
|
14
|
+
to_s.
|
15
|
+
encode('UTF-8', invalid: :replace, undef: :replace, replace: '').
|
16
|
+
scan(/<([^>]+)>/m).each do |e|
|
17
|
+
|
18
|
+
e = e.first
|
19
|
+
|
20
|
+
# Skip closing tags
|
21
|
+
next if e[0,1] == "/"
|
22
|
+
|
23
|
+
# Get the name vs attributes
|
24
|
+
name, astr = e.split(/\s+/, 2).map{|x| x.to_s }
|
25
|
+
astr ||= ''
|
26
|
+
|
27
|
+
# Skip non-alpha elements
|
28
|
+
next unless name =~ /^[a-zA-Z]/
|
29
|
+
|
30
|
+
# Convert newlines to spaces & strip trailing />
|
31
|
+
astr = astr.gsub(/\n/, ' ').sub(/\/$/, '')
|
32
|
+
|
33
|
+
o = { name: name }
|
34
|
+
|
35
|
+
begin
|
36
|
+
Shellwords.shellwords(astr).each do |attr_str|
|
37
|
+
aname, avalue = attr_str.split('=', 2).map{|x| x.to_s.strip }
|
38
|
+
avalue = avalue.to_s.gsub(/^\"|"$/, '')
|
39
|
+
o[aname] = @coder.decode(avalue)
|
40
|
+
end
|
41
|
+
rescue ::Interrupt
|
42
|
+
raise $!
|
43
|
+
rescue ::Exception
|
44
|
+
# If shellwords couldn't parse it, split on space instead
|
45
|
+
astr.to_s.split(/\s+/).each do |attr_str|
|
46
|
+
aname, avalue = attr_str.split('=', 2).map{|x| x.to_s.strip }
|
47
|
+
avalue = avalue.to_s.gsub(/^\"|"$/, '')
|
48
|
+
o[aname] = @coder.decode(avalue)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
res << o
|
52
|
+
end
|
53
|
+
|
54
|
+
res
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
8
58
|
class FilterHTMLIframes
|
9
59
|
include Base
|
60
|
+
include HTMLGhetto
|
10
61
|
|
11
62
|
def process(doc)
|
12
63
|
out = []
|
@@ -20,25 +71,11 @@ class FilterHTMLIframes
|
|
20
71
|
end
|
21
72
|
|
22
73
|
def extract(data)
|
23
|
-
|
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|
|
74
|
+
extract_elements(data).select{|x| x[:name] == 'iframe'}.each do |e|
|
37
75
|
url = e['src']
|
38
|
-
next unless url
|
76
|
+
next unless (url && url.length > 0)
|
39
77
|
urls << url
|
40
78
|
end
|
41
|
-
|
42
79
|
urls
|
43
80
|
end
|
44
81
|
end
|
@@ -46,6 +83,7 @@ end
|
|
46
83
|
|
47
84
|
class FilterHTMLLinks
|
48
85
|
include Base
|
86
|
+
include HTMLGhetto
|
49
87
|
|
50
88
|
def process(doc)
|
51
89
|
out = []
|
@@ -61,20 +99,10 @@ class FilterHTMLLinks
|
|
61
99
|
def extract(data)
|
62
100
|
urls = []
|
63
101
|
|
64
|
-
data
|
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|
|
102
|
+
extract_elements(data).each do |e|
|
75
103
|
url = e['href'] || e['src']
|
76
|
-
next unless url
|
77
|
-
urls << { 'link' => url, 'element' => e
|
104
|
+
next unless (url && url.length > 0)
|
105
|
+
urls << { 'link' => url, 'element' => e[:name] }
|
78
106
|
end
|
79
107
|
|
80
108
|
urls
|
@@ -136,14 +164,14 @@ class FilterDecodeHTTPReply
|
|
136
164
|
when /^Date:\s*(.*)/i
|
137
165
|
d = DateTime.parse($1.strip) rescue nil
|
138
166
|
save["http_date"] = d.to_time.strftime("%Y%m%dT%H:%M:%S") if d
|
139
|
-
|
167
|
+
|
140
168
|
when /^Last-modified:\s*(.*)/i
|
141
169
|
d = DateTime.parse($1.strip) rescue nil
|
142
170
|
save["http_modified"] = d.to_time.strftime("%Y%m%dT%H:%M:%S") if d
|
143
171
|
|
144
172
|
when /^Location:\s*(.*)/i
|
145
|
-
save["http_location"] = $1.strip
|
146
|
-
|
173
|
+
save["http_location"] = $1.strip
|
174
|
+
|
147
175
|
when /^WWW-Authenticate:\s*(.*)/i
|
148
176
|
save["http_auth"] = $1.strip
|
149
177
|
|
@@ -159,7 +187,7 @@ class FilterDecodeHTTPReply
|
|
159
187
|
end
|
160
188
|
|
161
189
|
head, body = data.split(/\r?\n\r?\n/, 2)
|
162
|
-
|
190
|
+
|
163
191
|
# Some buggy systems exclude the header entirely
|
164
192
|
body ||= head
|
165
193
|
|
data/lib/dap/filter/vulnmatch.rb
CHANGED
@@ -64,5 +64,145 @@ class FilterVulnMatchMSSQL
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
+
class FilterVulnMatchHTTP
|
68
|
+
include Base
|
69
|
+
include BaseVulnMatch
|
70
|
+
|
71
|
+
def check_shellshock(doc)
|
72
|
+
if not doc["http.headers"]
|
73
|
+
return []
|
74
|
+
end
|
75
|
+
|
76
|
+
h = doc["http.headers"]
|
77
|
+
sspattern = /\(\)\s*{\s*:;\s*};/
|
78
|
+
|
79
|
+
if h["user-agent"] and h["user-agent"] =~ sspattern
|
80
|
+
return ['VULN-SHELLSHOCK', 'CVE-2014-6271']
|
81
|
+
end
|
82
|
+
|
83
|
+
if h["referrer"] and h["referrer"] =~ sspattern
|
84
|
+
return ['VULN-SHELLSHOCK', 'CVE-2014-6271']
|
85
|
+
end
|
86
|
+
|
87
|
+
return []
|
88
|
+
end
|
89
|
+
|
90
|
+
def check_elastic(doc)
|
91
|
+
if not doc['http.path']
|
92
|
+
return []
|
93
|
+
end
|
94
|
+
if not doc['http.path'] == '/_search'
|
95
|
+
return []
|
96
|
+
end
|
97
|
+
|
98
|
+
input = doc['http.url']
|
99
|
+
if doc['http.method'] == "POST"
|
100
|
+
input = doc['http.body']
|
101
|
+
end
|
102
|
+
|
103
|
+
if not input.match("script_fields")
|
104
|
+
return []
|
105
|
+
end
|
106
|
+
|
107
|
+
out = ['VULN-ELASTICSEARCH-RCE', 'CVE-2014-3120']
|
108
|
+
if input.match("Runtime") and input.match("getRuntime()")
|
109
|
+
out += ["EXEC-SHELLCMD"]
|
110
|
+
end
|
111
|
+
|
112
|
+
if input.match("FileOutputStream") and input.match("URLClassLoader")
|
113
|
+
out += ["EXEC-JAVA-CLASS"]
|
114
|
+
end
|
115
|
+
|
116
|
+
if input.match("getDeclaredConstructor")
|
117
|
+
out += ['CVE-2015-1427']
|
118
|
+
end
|
119
|
+
|
120
|
+
if input.match("metasploit.Payload")
|
121
|
+
out += ['METASPLOIT']
|
122
|
+
end
|
123
|
+
|
124
|
+
return out
|
125
|
+
end
|
126
|
+
|
127
|
+
def process(doc)
|
128
|
+
vulns = []
|
129
|
+
if doc['vulnerability']
|
130
|
+
vulns |= doc['vulnerability']
|
131
|
+
end
|
132
|
+
|
133
|
+
vulns |= check_elastic(doc)
|
134
|
+
vulns |= check_shellshock(doc)
|
135
|
+
|
136
|
+
# see vulndb.rb, allows for simple matches to be added quickly
|
137
|
+
SEARCHES[:http].each do | entry |
|
138
|
+
success = true
|
139
|
+
|
140
|
+
# all matches must go through
|
141
|
+
entry[:match].each do | k, v |
|
142
|
+
if not doc[k]
|
143
|
+
success = false
|
144
|
+
else
|
145
|
+
m = doc[k].match(v)
|
146
|
+
if not m
|
147
|
+
success = false
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
if not success
|
152
|
+
break
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
if success
|
157
|
+
vulns |= entry[:cve]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
if vulns != []
|
162
|
+
doc['vulnerability'] = vulns
|
163
|
+
end
|
164
|
+
|
165
|
+
[ doc ]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class FilterGenericSetMatch
|
170
|
+
include Base
|
171
|
+
attr_accessor :matchset
|
172
|
+
|
173
|
+
def initialize(args)
|
174
|
+
self.opts = {}
|
175
|
+
args.each do |arg|
|
176
|
+
k,v = arg.split("=", 2)
|
177
|
+
self.opts[k] = v
|
178
|
+
end
|
179
|
+
self.name = Dap::Factory.name_from_class(self.class)
|
180
|
+
|
181
|
+
fail "Expected key and set arguments to #{self.name} but got #{self.opts}" unless self.opts.has_key?("key") and self.opts.has_key?("set")
|
182
|
+
|
183
|
+
self.matchset = {}
|
184
|
+
File.readlines(self.opts["set"]).each do |line|
|
185
|
+
self.matchset[line.chomp] = nil
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def process(doc)
|
190
|
+
if doc.has_key?(self.opts["key"])
|
191
|
+
if doc[self.opts["key"]].kind_of?(Array)
|
192
|
+
doc[self.opts["key"]].each do |val|
|
193
|
+
if self.matchset.has_key?(val)
|
194
|
+
return [ doc ]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
else
|
198
|
+
if self.matchset.has_key?(doc[self.opts["key"]])
|
199
|
+
return [ doc ]
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
[ ]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
67
207
|
end
|
68
208
|
end
|
data/lib/dap/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rapid7 Research
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -241,7 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
241
241
|
version: '0'
|
242
242
|
requirements: []
|
243
243
|
rubyforge_project:
|
244
|
-
rubygems_version: 2.4.
|
244
|
+
rubygems_version: 2.4.8
|
245
245
|
signing_key:
|
246
246
|
specification_version: 4
|
247
247
|
summary: 'DAP: The Data Analysis Pipeline'
|