arachni 0.2.4 → 0.3
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/CHANGELOG.md +33 -0
- data/README.md +2 -4
- data/Rakefile +15 -4
- data/bin/arachni +0 -0
- data/bin/arachni_web +0 -0
- data/bin/arachni_web_autostart +0 -0
- data/bin/arachni_xmlrpc +0 -0
- data/bin/arachni_xmlrpcd +0 -0
- data/bin/arachni_xmlrpcd_monitor +0 -0
- data/lib/arachni.rb +1 -1
- data/lib/framework.rb +36 -6
- data/lib/http.rb +12 -5
- data/lib/module/auditor.rb +482 -59
- data/lib/module/base.rb +17 -0
- data/lib/module/manager.rb +26 -2
- data/lib/module/trainer.rb +1 -12
- data/lib/module/utilities.rb +12 -0
- data/lib/parser/auditable.rb +8 -3
- data/lib/parser/elements.rb +11 -0
- data/lib/parser/page.rb +3 -1
- data/lib/parser/parser.rb +130 -18
- data/lib/rpc/xml/server/dispatcher.rb +21 -0
- data/lib/spider.rb +141 -82
- data/lib/ui/cli/cli.rb +2 -3
- data/lib/ui/web/addon_manager.rb +273 -0
- data/lib/ui/web/addons/autodeploy.rb +172 -0
- data/lib/ui/web/addons/autodeploy/lib/manager.rb +291 -0
- data/lib/ui/web/addons/autodeploy/views/index.erb +124 -0
- data/lib/ui/web/addons/sample.rb +78 -0
- data/lib/ui/web/addons/sample/views/index.erb +4 -0
- data/lib/ui/web/addons/scheduler.rb +139 -0
- data/lib/ui/web/addons/scheduler/views/index.erb +131 -0
- data/lib/ui/web/addons/scheduler/views/options.erb +93 -0
- data/lib/ui/web/dispatcher_manager.rb +80 -13
- data/lib/ui/web/instance_manager.rb +87 -0
- data/lib/ui/web/scheduler.rb +166 -0
- data/lib/ui/web/server.rb +142 -202
- data/lib/ui/web/server/public/js/jquery-ui-timepicker.js +985 -0
- data/lib/ui/web/server/public/plugins/sample/style.css +0 -0
- data/lib/ui/web/server/public/style.css +42 -0
- data/lib/ui/web/server/views/addon.erb +15 -0
- data/lib/ui/web/server/views/addons.erb +46 -0
- data/lib/ui/web/server/views/dispatchers.erb +1 -1
- data/lib/ui/web/server/views/instance.erb +9 -11
- data/lib/ui/web/server/views/layout.erb +14 -1
- data/lib/ui/web/server/views/welcome.erb +7 -6
- data/lib/ui/web/utilities.rb +134 -0
- data/modules/audit/code_injection_timing.rb +6 -2
- data/modules/audit/code_injection_timing/payloads.txt +2 -2
- data/modules/audit/os_cmd_injection_timing.rb +7 -3
- data/modules/audit/os_cmd_injection_timing/payloads.txt +1 -1
- data/modules/audit/sqli_blind_rdiff.rb +18 -233
- data/modules/audit/sqli_blind_rdiff/payloads.txt +5 -0
- data/modules/audit/sqli_blind_timing.rb +9 -2
- data/path_extractors/anchors.rb +1 -1
- data/path_extractors/forms.rb +1 -1
- data/path_extractors/frames.rb +1 -1
- data/path_extractors/generic.rb +1 -1
- data/path_extractors/links.rb +1 -1
- data/path_extractors/meta_refresh.rb +1 -1
- data/path_extractors/scripts.rb +1 -1
- data/path_extractors/sitemap.rb +1 -1
- data/plugins/proxy/server.rb +3 -2
- data/plugins/waf_detector.rb +0 -3
- metadata +37 -34
- data/lib/anemone/cookie_store.rb +0 -35
- data/lib/anemone/core.rb +0 -371
- data/lib/anemone/exceptions.rb +0 -5
- data/lib/anemone/http.rb +0 -144
- data/lib/anemone/page.rb +0 -338
- data/lib/anemone/page_store.rb +0 -160
- data/lib/anemone/storage.rb +0 -34
- data/lib/anemone/storage/base.rb +0 -75
- data/lib/anemone/storage/exceptions.rb +0 -15
- data/lib/anemone/storage/mongodb.rb +0 -89
- data/lib/anemone/storage/pstore.rb +0 -50
- data/lib/anemone/storage/redis.rb +0 -90
- data/lib/anemone/storage/tokyo_cabinet.rb +0 -57
- data/lib/anemone/tentacle.rb +0 -40
@@ -35,209 +35,32 @@ class BlindrDiffSQLInjection < Arachni::Module::Base
|
|
35
35
|
super( page )
|
36
36
|
end
|
37
37
|
|
38
|
-
def prepare
|
38
|
+
def prepare
|
39
|
+
@@__bools ||= []
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
'"',
|
44
|
-
''
|
45
|
-
]
|
41
|
+
if @@__bools.empty?
|
42
|
+
read_file( 'payloads.txt' ) {
|
43
|
+
|str|
|
46
44
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
# we need 2 requests thus we change the second one a little bit to
|
51
|
-
# fool the Auditor's redundancy filter
|
52
|
-
'\'"``'
|
53
|
-
]
|
54
|
-
|
55
|
-
# %q% will be replaced by a character in @__quotes
|
56
|
-
@__injection = '%q% and %q%1'
|
57
|
-
|
58
|
-
@__opts = {
|
59
|
-
:format => [ Format::APPEND ],
|
60
|
-
# we need to do our own redundancy checks
|
61
|
-
:redundant => true
|
62
|
-
}
|
63
|
-
|
64
|
-
# used for redundancy checks
|
65
|
-
@@__audited ||= Set.new
|
66
|
-
|
67
|
-
# this is the structure of the responses
|
68
|
-
@responses = {
|
69
|
-
:orig => '',
|
70
|
-
|
71
|
-
:good => {
|
72
|
-
|
73
|
-
},
|
74
|
-
:bad => {
|
75
|
-
}
|
76
|
-
}
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
def run( )
|
81
|
-
|
82
|
-
return if( __audited? )
|
83
|
-
|
84
|
-
if( !@page.query_vars || @page.query_vars.empty? )
|
85
|
-
print_status( 'Nothing to audit on current page, skipping...' )
|
86
|
-
return
|
87
|
-
end
|
88
|
-
|
89
|
-
# get the link object that fits the URL of the page
|
90
|
-
# this will be the one to audit
|
91
|
-
@page.links.each {
|
92
|
-
|link|
|
93
|
-
@__candidate = link if link.action == @page.url
|
94
|
-
}
|
95
|
-
|
96
|
-
return if !@__candidate
|
97
|
-
|
98
|
-
# let's get a fresh rendering of the page to assist us with
|
99
|
-
# irrelevant dynamic content elimination (banners, ads, etc...)
|
100
|
-
@http.get( @page.url, :params => @page.query_vars ).on_complete {
|
101
|
-
|res|
|
102
|
-
|
103
|
-
# eliminate dynamic content that's context-irrelevant
|
104
|
-
# ie. changing with every refresh
|
105
|
-
@responses[:orig] = @page.html.rdiff( res.body )
|
106
|
-
}
|
107
|
-
|
108
|
-
# force the webapp to return an error page
|
109
|
-
prep_bad_responses( )
|
110
|
-
|
111
|
-
# start injecting 'good' SQL queries
|
112
|
-
prep_good_responses( )
|
113
|
-
|
114
|
-
@http.after_run {
|
115
|
-
# analyze the HTML code of the responses in order to determine
|
116
|
-
# which injections were succesfull
|
117
|
-
analyze( )
|
118
|
-
}
|
119
|
-
end
|
120
|
-
|
121
|
-
# Audits page with 'bad' SQL characters and gathers error pages
|
122
|
-
def prep_bad_responses( )
|
123
|
-
|
124
|
-
@__bad_chars.each {
|
125
|
-
|str|
|
126
|
-
|
127
|
-
# get injection variations that will hopefully cause an internal/silent
|
128
|
-
# SQL error
|
129
|
-
variations = @__candidate.injection_sets( str, @__opts )
|
130
|
-
|
131
|
-
@responses[:bad_total] = variations.size
|
132
|
-
|
133
|
-
variations.each {
|
134
|
-
|link|
|
135
|
-
|
136
|
-
# the altered link variable
|
137
|
-
altered = link.altered
|
138
|
-
|
139
|
-
print_status( @__candidate.get_status_str( altered ) )
|
140
|
-
|
141
|
-
# register us as the auditor
|
142
|
-
link.auditor( self )
|
143
|
-
# submit the link and get the response
|
144
|
-
link.submit( @__opts ).on_complete {
|
145
|
-
|res|
|
146
|
-
|
147
|
-
@responses[:bad][altered] ||= res.body.clone
|
148
|
-
|
149
|
-
# remove context-irrelevant dynamic content like banners and such
|
150
|
-
# from the error page
|
151
|
-
@responses[:bad][altered] = @responses[:bad][altered].rdiff( res.body.clone )
|
45
|
+
[ '\'', '"', '' ].each {
|
46
|
+
|quote|
|
47
|
+
@@__bools << str.gsub( '%q%', quote )
|
152
48
|
}
|
153
49
|
}
|
154
|
-
}
|
155
|
-
end
|
156
|
-
|
157
|
-
# Injects SQL code that doesn't affect the flow of execution nor presentation
|
158
|
-
def prep_good_responses( )
|
159
|
-
|
160
|
-
@__quotes.each {
|
161
|
-
|quote|
|
162
|
-
|
163
|
-
# prepare the statement with combinations of quote characters
|
164
|
-
str = @__injection.gsub( '%q%', quote )
|
165
|
-
|
166
|
-
variations = @__candidate.injection_sets( str, @__opts )
|
167
|
-
|
168
|
-
@responses[:good_total] = variations.size
|
169
|
-
|
170
|
-
variations.each {
|
171
|
-
|link|
|
172
|
-
|
173
|
-
# the altered link variable
|
174
|
-
altered = link.altered
|
175
|
-
|
176
|
-
# register us as the auditor
|
177
|
-
link.auditor( self )
|
178
|
-
|
179
|
-
# submit the link and get the response
|
180
|
-
link.submit( @__opts ).on_complete {
|
181
|
-
|res|
|
182
|
-
|
183
|
-
@responses[:good][altered] ||= []
|
184
|
-
|
185
|
-
# save the response for later analysis
|
186
|
-
@responses[:good][altered] << {
|
187
|
-
'str' => str,
|
188
|
-
'res' => res
|
189
|
-
}
|
190
|
-
}
|
191
|
-
|
192
|
-
}
|
193
|
-
}
|
194
|
-
|
195
|
-
end
|
196
|
-
|
197
|
-
# Goes through the responses induced by {#prep_good_responses} and {#__check} their code
|
198
|
-
def analyze( )
|
199
|
-
@responses[:good].keys.each {
|
200
|
-
|key|
|
201
|
-
@responses[:good][key].each {
|
202
|
-
|res|
|
203
|
-
__check( res['str'], res['res'], key )
|
204
|
-
}
|
205
|
-
}
|
206
|
-
end
|
207
|
-
|
208
|
-
#
|
209
|
-
# Compares HTML responses in order to identify successful blind sql injections
|
210
|
-
#
|
211
|
-
# @param [String] str the string that unveiled the vulnerability
|
212
|
-
# @param [Typhoeus::Response]
|
213
|
-
# @param [String] var the vulnerable variable
|
214
|
-
#
|
215
|
-
def __check( str, res, var )
|
216
|
-
|
217
|
-
# if one of the injections gives the same results as the
|
218
|
-
# original page then a blind SQL injection exists
|
219
|
-
check = res.body.rdiff( @page.html )
|
220
|
-
|
221
|
-
if( check == @responses[:orig] && @responses[:bad][var] != check &&
|
222
|
-
!@http.custom_404?( res ) && res.code == 200 )
|
223
|
-
__log_results( var, res, str )
|
224
50
|
end
|
225
|
-
|
226
51
|
end
|
227
52
|
|
228
|
-
def
|
229
|
-
@@__audited << __audit_id( )
|
230
|
-
end
|
53
|
+
def run( )
|
231
54
|
|
232
|
-
|
233
|
-
|
234
|
-
|
55
|
+
opts = {}
|
56
|
+
# fault injection seeds
|
57
|
+
opts[:faults] = [ '\'"`' ]
|
58
|
+
# boolean injection seeds
|
59
|
+
opts[:bools] = @@__bools
|
235
60
|
|
236
|
-
|
237
|
-
@@__audited.include?( __audit_id( ) )
|
61
|
+
audit_rdiff( opts )
|
238
62
|
end
|
239
63
|
|
240
|
-
|
241
64
|
def self.info
|
242
65
|
{
|
243
66
|
:name => 'Blind (rDiff) SQL Injection',
|
@@ -247,7 +70,9 @@ class BlindrDiffSQLInjection < Arachni::Module::Base
|
|
247
70
|
(Note: This module may get confused by certain types of XSS vulnerabilities.
|
248
71
|
If this module returns a positive result you should investigate nonetheless.)},
|
249
72
|
:elements => [
|
250
|
-
Issue::Element::LINK
|
73
|
+
Issue::Element::LINK,
|
74
|
+
Issue::Element::FORM,
|
75
|
+
Issue::Element::COOKIE
|
251
76
|
],
|
252
77
|
:author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com> ',
|
253
78
|
:version => '0.3',
|
@@ -276,46 +101,6 @@ class BlindrDiffSQLInjection < Arachni::Module::Base
|
|
276
101
|
}
|
277
102
|
end
|
278
103
|
|
279
|
-
private
|
280
|
-
|
281
|
-
def __log_results( var, res, str )
|
282
|
-
|
283
|
-
url = res.effective_url
|
284
|
-
|
285
|
-
# since we bypassed the auditor completely we need to create
|
286
|
-
# our own opts hash and pass it to the Vulnerability class.
|
287
|
-
#
|
288
|
-
# this is only required for Metasploitable vulnerabilities
|
289
|
-
opts = {
|
290
|
-
:injected_orig => URI( @page.url ).query,
|
291
|
-
:combo => @__candidate.auditable
|
292
|
-
}
|
293
|
-
|
294
|
-
issue = Issue.new( {
|
295
|
-
:var => var,
|
296
|
-
:url => url,
|
297
|
-
:method => res.request.method.to_s,
|
298
|
-
:opts => opts,
|
299
|
-
:injected => str,
|
300
|
-
:id => str,
|
301
|
-
:regexp => 'n/a',
|
302
|
-
:regexp_match => 'n/a',
|
303
|
-
:elem => Issue::Element::LINK,
|
304
|
-
:response => res.body,
|
305
|
-
:verification => true,
|
306
|
-
:headers => {
|
307
|
-
:request => res.request.headers,
|
308
|
-
:response => res.headers,
|
309
|
-
}
|
310
|
-
}.merge( self.class.info )
|
311
|
-
)
|
312
|
-
|
313
|
-
print_ok( "In #{Issue::Element::LINK} var '#{var}' ( #{url} )" )
|
314
|
-
|
315
|
-
# register our results with the system
|
316
|
-
register_results( [ issue ] )
|
317
|
-
end
|
318
|
-
|
319
104
|
end
|
320
105
|
end
|
321
106
|
end
|
@@ -18,7 +18,7 @@ module Modules
|
|
18
18
|
# @author: Tasos "Zapotek" Laskos
|
19
19
|
# <tasos.laskos@gmail.com>
|
20
20
|
# <zapotek@segfault.gr>
|
21
|
-
# @version: 0.2
|
21
|
+
# @version: 0.2.1
|
22
22
|
#
|
23
23
|
# @see http://cwe.mitre.org/data/definitions/89.html
|
24
24
|
# @see http://capec.mitre.org/data/definitions/7.html
|
@@ -55,6 +55,13 @@ class BlindTimingSQLInjection < Arachni::Module::Base
|
|
55
55
|
audit_timeout( @@__injection_str, @__opts )
|
56
56
|
end
|
57
57
|
|
58
|
+
def redundant
|
59
|
+
# We add ourselves to the list too.
|
60
|
+
# We don't want more than one timing-attack variation per issue,
|
61
|
+
# it's too expensive.
|
62
|
+
[ 'sqli', 'sqli_blind_rdiff', 'sqli_blind_timing' ]
|
63
|
+
end
|
64
|
+
|
58
65
|
def self.info
|
59
66
|
{
|
60
67
|
:name => 'Blind (timing) SQL injection',
|
@@ -68,7 +75,7 @@ class BlindTimingSQLInjection < Arachni::Module::Base
|
|
68
75
|
Issue::Element::HEADER
|
69
76
|
],
|
70
77
|
:author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com> ',
|
71
|
-
:version => '0.
|
78
|
+
:version => '0.2.1',
|
72
79
|
:references => {
|
73
80
|
'OWASP' => 'http://www.owasp.org/index.php/Blind_SQL_Injection',
|
74
81
|
'MITRE - CAPEC' => 'http://capec.mitre.org/data/definitions/7.html'
|
data/path_extractors/anchors.rb
CHANGED
data/path_extractors/forms.rb
CHANGED
data/path_extractors/frames.rb
CHANGED
data/path_extractors/generic.rb
CHANGED
data/path_extractors/links.rb
CHANGED
data/path_extractors/scripts.rb
CHANGED
data/path_extractors/sitemap.rb
CHANGED
data/plugins/proxy/server.rb
CHANGED
@@ -25,7 +25,7 @@ class Proxy
|
|
25
25
|
# @author: Tasos "Zapotek" Laskos
|
26
26
|
# <tasos.laskos@gmail.com>
|
27
27
|
# <zapotek@segfault.gr>
|
28
|
-
# @version: 0.1
|
28
|
+
# @version: 0.1.1
|
29
29
|
#
|
30
30
|
class Server < WEBrick::HTTPProxyServer
|
31
31
|
|
@@ -57,7 +57,8 @@ class Proxy
|
|
57
57
|
res.header['content-type'] = 'text/plain'
|
58
58
|
res.header.delete( 'content-encoding' )
|
59
59
|
|
60
|
-
res.body << reasons.
|
60
|
+
res.body << reasons.pop + "\n"
|
61
|
+
res.body << reasons.map{ |msg| " * #{msg}" }.join( "\n" )
|
61
62
|
end
|
62
63
|
end
|
63
64
|
end
|
data/plugins/waf_detector.rb
CHANGED
@@ -126,7 +126,6 @@ class WAFDetector < Arachni::Plugin::Base
|
|
126
126
|
|
127
127
|
def queue_original
|
128
128
|
@precision.times {
|
129
|
-
# grab the page containing the login form
|
130
129
|
@framework.http.get( @url.to_s ).on_complete {
|
131
130
|
|res|
|
132
131
|
@responses[:original] ||= res.body
|
@@ -137,7 +136,6 @@ class WAFDetector < Arachni::Plugin::Base
|
|
137
136
|
|
138
137
|
def queue_vanilla( )
|
139
138
|
@precision.times {
|
140
|
-
# grab the page containing the login form
|
141
139
|
@framework.http.get( @url.to_s, :params => @safe ).on_complete {
|
142
140
|
|res|
|
143
141
|
@responses[:vanilla] ||= res.body
|
@@ -148,7 +146,6 @@ class WAFDetector < Arachni::Plugin::Base
|
|
148
146
|
|
149
147
|
def queue_spicy( )
|
150
148
|
@precision.times {
|
151
|
-
# grab the page containing the login form
|
152
149
|
@framework.http.get( @url.to_s, :params => @unsafe ).on_complete {
|
153
150
|
|res|
|
154
151
|
@responses[:spicy] ||= res.body
|