arachni 0.2.4 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|