arachni 1.0.5 → 1.0.6
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/CHANGELOG.md +50 -0
- data/README.md +9 -2
- data/components/checks/active/code_injection.rb +5 -5
- data/components/checks/active/code_injection_timing.rb +3 -3
- data/components/checks/active/no_sql_injection_differential.rb +3 -2
- data/components/checks/active/os_cmd_injection.rb +11 -5
- data/components/checks/active/os_cmd_injection_timing.rb +11 -4
- data/components/checks/active/path_traversal.rb +2 -2
- data/components/checks/active/sql_injection.rb +1 -1
- data/components/checks/active/sql_injection/patterns/mssql +1 -0
- data/components/checks/active/sql_injection_differential.rb +3 -2
- data/components/checks/active/unvalidated_redirect.rb +3 -3
- data/components/checks/passive/common_directories/directories.txt +2 -0
- data/components/checks/passive/common_files/filenames.txt +1 -0
- data/lib/arachni/browser.rb +17 -1
- data/lib/arachni/check/auditor.rb +5 -2
- data/lib/arachni/check/base.rb +30 -5
- data/lib/arachni/element/capabilities/analyzable/differential.rb +2 -5
- data/lib/arachni/element/capabilities/auditable.rb +3 -1
- data/lib/arachni/element/capabilities/with_dom.rb +1 -0
- data/lib/arachni/element/capabilities/with_node.rb +1 -1
- data/lib/arachni/element/cookie.rb +2 -2
- data/lib/arachni/element/form.rb +1 -1
- data/lib/arachni/element/header.rb +2 -2
- data/lib/arachni/element/link_template.rb +1 -1
- data/lib/arachni/framework.rb +21 -1144
- data/lib/arachni/framework/parts/audit.rb +282 -0
- data/lib/arachni/framework/parts/browser.rb +132 -0
- data/lib/arachni/framework/parts/check.rb +86 -0
- data/lib/arachni/framework/parts/data.rb +158 -0
- data/lib/arachni/framework/parts/platform.rb +34 -0
- data/lib/arachni/framework/parts/plugin.rb +61 -0
- data/lib/arachni/framework/parts/report.rb +128 -0
- data/lib/arachni/framework/parts/scope.rb +40 -0
- data/lib/arachni/framework/parts/state.rb +457 -0
- data/lib/arachni/http/client.rb +33 -30
- data/lib/arachni/http/request.rb +6 -2
- data/lib/arachni/issue.rb +55 -1
- data/lib/arachni/platform/manager.rb +25 -21
- data/lib/arachni/state/framework.rb +7 -1
- data/lib/arachni/utilities.rb +10 -0
- data/lib/version +1 -1
- data/spec/arachni/browser_spec.rb +13 -0
- data/spec/arachni/check/auditor_spec.rb +1 -0
- data/spec/arachni/check/base_spec.rb +80 -0
- data/spec/arachni/element/cookie_spec.rb +2 -2
- data/spec/arachni/framework/parts/audit_spec.rb +391 -0
- data/spec/arachni/framework/parts/browser_spec.rb +26 -0
- data/spec/arachni/framework/parts/check_spec.rb +24 -0
- data/spec/arachni/framework/parts/data_spec.rb +187 -0
- data/spec/arachni/framework/parts/platform_spec.rb +62 -0
- data/spec/arachni/framework/parts/plugin_spec.rb +41 -0
- data/spec/arachni/framework/parts/report_spec.rb +66 -0
- data/spec/arachni/framework/parts/scope_spec.rb +86 -0
- data/spec/arachni/framework/parts/state_spec.rb +528 -0
- data/spec/arachni/framework_spec.rb +17 -1344
- data/spec/arachni/http/client_spec.rb +12 -7
- data/spec/arachni/issue_spec.rb +35 -0
- data/spec/arachni/platform/manager_spec.rb +2 -3
- data/spec/arachni/state/framework_spec.rb +15 -0
- data/spec/components/checks/active/code_injection_timing_spec.rb +5 -5
- data/spec/components/checks/active/no_sql_injection_differential_spec.rb +4 -0
- data/spec/components/checks/active/os_cmd_injection_spec.rb +20 -7
- data/spec/components/checks/active/os_cmd_injection_timing_spec.rb +5 -5
- data/spec/components/checks/active/sql_injection_differential_spec.rb +4 -0
- data/spec/components/checks/active/sql_injection_spec.rb +2 -3
- data/spec/support/servers/arachni/browser.rb +31 -0
- data/spec/support/servers/checks/active/code_injection.rb +1 -1
- data/spec/support/servers/checks/active/no_sql_injection_differential.rb +36 -34
- data/spec/support/servers/checks/active/os_cmd_injection.rb +6 -12
- data/spec/support/servers/checks/active/os_cmd_injection_timing.rb +9 -4
- data/spec/support/servers/checks/active/sql_injection.rb +1 -1
- data/spec/support/servers/checks/active/sql_injection_differential.rb +37 -34
- data/spec/support/shared/element/capabilities/with_node.rb +25 -0
- data/spec/support/shared/framework.rb +26 -0
- data/ui/cli/output.rb +2 -0
- data/ui/cli/rpc/server/dispatcher/option_parser.rb +1 -1
- metadata +32 -4
- data/components/checks/active/sql_injection/patterns/coldfusion +0 -1
data/lib/arachni/http/client.rb
CHANGED
@@ -206,7 +206,7 @@ class Client
|
|
206
206
|
duped_after_run = observers_for( :after_run ).dup
|
207
207
|
observers_for( :after_run ).clear
|
208
208
|
duped_after_run.each { |block| block.call }
|
209
|
-
end while @queue_size > 0
|
209
|
+
end while @queue_size > 0 || observers_for( :after_run ).any?
|
210
210
|
|
211
211
|
notify_after_each_run
|
212
212
|
|
@@ -711,7 +711,8 @@ class Client
|
|
711
711
|
#
|
712
712
|
# @param [Request] request
|
713
713
|
def forward_request( request )
|
714
|
-
request.id
|
714
|
+
add_callbacks = !request.id
|
715
|
+
request.id = @request_count
|
715
716
|
|
716
717
|
if debug_level_3?
|
717
718
|
print_debug_level_3 '------------'
|
@@ -728,40 +729,42 @@ class Client
|
|
728
729
|
print_debug_level_3 '------------'
|
729
730
|
end
|
730
731
|
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
732
|
+
if add_callbacks
|
733
|
+
request.on_complete do |response|
|
734
|
+
synchronize do
|
735
|
+
@response_count += 1
|
736
|
+
@burst_response_count += 1
|
737
|
+
@burst_response_time_sum += response.time
|
738
|
+
@total_response_time_sum += response.time
|
739
|
+
|
740
|
+
if Platform::Manager.fingerprint?( response )
|
741
|
+
# Force a fingerprint by converting the Response to a Page object.
|
742
|
+
response.to_page
|
743
|
+
end
|
737
744
|
|
738
|
-
|
739
|
-
# Force a fingerprint by converting the Response to a Page object.
|
740
|
-
response.to_page
|
741
|
-
end
|
745
|
+
notify_on_complete( response )
|
742
746
|
|
743
|
-
|
747
|
+
parse_and_set_cookies( response ) if request.update_cookies?
|
744
748
|
|
745
|
-
|
749
|
+
if debug_level_3?
|
750
|
+
print_debug_level_3 '------------'
|
751
|
+
print_debug_level_3 "Got response for request ID#: #{response.request.id}"
|
752
|
+
print_debug_level_3 "Performer: #{response.request.performer}"
|
753
|
+
print_debug_level_3 "Status: #{response.code}"
|
754
|
+
print_debug_level_3 "Code: #{response.return_code}"
|
755
|
+
print_debug_level_3 "Message: #{response.return_message}"
|
756
|
+
print_debug_level_3 "URL: #{response.url}"
|
757
|
+
print_debug_level_3 "Headers:\n#{response.headers_string}"
|
758
|
+
print_debug_level_3 "Parsed headers: #{response.headers}"
|
759
|
+
end
|
746
760
|
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
print_debug_level_3 "Status: #{response.code}"
|
752
|
-
print_debug_level_3 "Code: #{response.return_code}"
|
753
|
-
print_debug_level_3 "Message: #{response.return_message}"
|
754
|
-
print_debug_level_3 "URL: #{response.url}"
|
755
|
-
print_debug_level_3 "Headers:\n#{response.headers_string}"
|
756
|
-
print_debug_level_3 "Parsed headers: #{response.headers}"
|
757
|
-
end
|
761
|
+
if response.timed_out?
|
762
|
+
print_debug_level_3 "Request timed-out! -- ID# #{response.request.id}"
|
763
|
+
@time_out_count += 1
|
764
|
+
end
|
758
765
|
|
759
|
-
|
760
|
-
print_debug_level_3 "Request timed-out! -- ID# #{response.request.id}"
|
761
|
-
@time_out_count += 1
|
766
|
+
print_debug_level_3 '------------'
|
762
767
|
end
|
763
|
-
|
764
|
-
print_debug_level_3 '------------'
|
765
768
|
end
|
766
769
|
end
|
767
770
|
|
data/lib/arachni/http/request.rb
CHANGED
@@ -97,6 +97,10 @@ class Request < Message
|
|
97
97
|
# @return [Bool]
|
98
98
|
attr_accessor :high_priority
|
99
99
|
|
100
|
+
# @return [Integer]
|
101
|
+
# Maximum HTTP response size to accept, in bytes.
|
102
|
+
attr_accessor :response_max_size
|
103
|
+
|
100
104
|
# @private
|
101
105
|
attr_accessor :root_redirect_id
|
102
106
|
|
@@ -447,8 +451,6 @@ class Request < Message
|
|
447
451
|
end
|
448
452
|
end
|
449
453
|
|
450
|
-
private
|
451
|
-
|
452
454
|
def prepare_headers
|
453
455
|
headers['Cookie'] = effective_cookies.
|
454
456
|
map { |k, v| "#{Cookie.encode( k )}=#{Cookie.encode( v )}" }.
|
@@ -462,6 +464,8 @@ class Request < Message
|
|
462
464
|
headers.each { |k, v| headers[k] = Header.encode( v ) if v }
|
463
465
|
end
|
464
466
|
|
467
|
+
private
|
468
|
+
|
465
469
|
def fill_in_data_from_typhoeus_response( response )
|
466
470
|
@headers_string = response.debug_info.header_out.first
|
467
471
|
@effective_body = response.debug_info.data_out.first
|
data/lib/arachni/issue.rb
CHANGED
@@ -112,6 +112,10 @@ class Issue
|
|
112
112
|
# Variations of this issue.
|
113
113
|
attr_accessor :variations
|
114
114
|
|
115
|
+
# @return [Issue,nil]
|
116
|
+
# Parent of variation.
|
117
|
+
attr_accessor :parent
|
118
|
+
|
115
119
|
# @param [Hash] options
|
116
120
|
# Configuration hash holding instance attributes.
|
117
121
|
def initialize( options = {} )
|
@@ -130,6 +134,42 @@ class Issue
|
|
130
134
|
@tags ||= []
|
131
135
|
@variations ||= []
|
132
136
|
@variation = nil
|
137
|
+
@parent = nil
|
138
|
+
end
|
139
|
+
|
140
|
+
# @note The whole environment needs to be fresh.
|
141
|
+
#
|
142
|
+
# Rechecks the existence of this issue.
|
143
|
+
#
|
144
|
+
# @param [Framework.nil] framework
|
145
|
+
# {Framework} to use, if `nil` is given a new {Framework} will be
|
146
|
+
# instantiated and used.
|
147
|
+
#
|
148
|
+
# @return [Issue,nil]
|
149
|
+
# Fresh {Issue} if the issue still exists, `nil` otherwise.
|
150
|
+
def recheck( framework = nil )
|
151
|
+
new_issue = nil
|
152
|
+
checker = proc do |f|
|
153
|
+
referring_page.update_element_audit_whitelist vector
|
154
|
+
|
155
|
+
f.options.url = referring_page.url
|
156
|
+
f.options.audit.elements vector.class.type
|
157
|
+
|
158
|
+
f.checks.load( parent ? parent.check[:shortname] : check[:shortname] )
|
159
|
+
f.push_to_page_queue referring_page
|
160
|
+
|
161
|
+
f.run
|
162
|
+
|
163
|
+
new_issue = Data.issues[digest]
|
164
|
+
end
|
165
|
+
|
166
|
+
if framework
|
167
|
+
checker.call framework
|
168
|
+
else
|
169
|
+
Arachni::Framework.new( &checker )
|
170
|
+
end
|
171
|
+
|
172
|
+
new_issue
|
133
173
|
end
|
134
174
|
|
135
175
|
# @return [HTTP::Response]
|
@@ -307,6 +347,8 @@ class Issue
|
|
307
347
|
h[:request] = request.to_h if request
|
308
348
|
end
|
309
349
|
|
350
|
+
h.delete :parent
|
351
|
+
|
310
352
|
h
|
311
353
|
end
|
312
354
|
alias :to_hash :to_h
|
@@ -358,6 +400,7 @@ class Issue
|
|
358
400
|
|
359
401
|
issue.unique_id = unique_id
|
360
402
|
issue.variation = false
|
403
|
+
issue.parent = nil
|
361
404
|
issue
|
362
405
|
end
|
363
406
|
|
@@ -376,6 +419,7 @@ class Issue
|
|
376
419
|
|
377
420
|
issue.unique_id = unique_id
|
378
421
|
issue.variation = true
|
422
|
+
issue.parent = self
|
379
423
|
issue
|
380
424
|
end
|
381
425
|
|
@@ -394,6 +438,7 @@ class Issue
|
|
394
438
|
|
395
439
|
@variations = []
|
396
440
|
@variation = nil
|
441
|
+
@parent = nil
|
397
442
|
|
398
443
|
self
|
399
444
|
end
|
@@ -425,9 +470,11 @@ class Issue
|
|
425
470
|
def to_rpc_data
|
426
471
|
data = {}
|
427
472
|
instance_variables.each do |ivar|
|
428
|
-
data[ivar.to_s.gsub('@','')] =
|
473
|
+
data[ivar.to_s.gsub('@','')] =
|
474
|
+
instance_variable_get( ivar ).to_rpc_data_or_self
|
429
475
|
end
|
430
476
|
|
477
|
+
data.delete 'parent'
|
431
478
|
|
432
479
|
if data['check'] && data['check'][:elements]
|
433
480
|
data['check'] = data['check'].dup
|
@@ -487,6 +534,13 @@ class Issue
|
|
487
534
|
|
488
535
|
instance.instance_variable_set( "@#{name}", value )
|
489
536
|
end
|
537
|
+
|
538
|
+
if instance.variations
|
539
|
+
instance.variations.each do |v|
|
540
|
+
v.parent = instance
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
490
544
|
instance
|
491
545
|
end
|
492
546
|
|
@@ -60,26 +60,29 @@ class Manager
|
|
60
60
|
windows: {}
|
61
61
|
}
|
62
62
|
|
63
|
-
DB =
|
64
|
-
:
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
63
|
+
DB = {
|
64
|
+
sql: {
|
65
|
+
mysql: {},
|
66
|
+
pgsql: {},
|
67
|
+
mssql: {},
|
68
|
+
oracle: {},
|
69
|
+
sqlite: {},
|
70
|
+
ingres: {},
|
71
|
+
emc: {},
|
72
|
+
db2: {},
|
73
|
+
interbase: {},
|
74
|
+
informix: {},
|
75
|
+
firebird: {},
|
76
|
+
maxdb: {},
|
77
|
+
sybase: {},
|
78
|
+
frontbase: {},
|
79
|
+
hsqldb: {},
|
80
|
+
access: {},
|
81
|
+
},
|
82
|
+
nosql: {
|
83
|
+
mongodb: {}
|
84
|
+
}
|
85
|
+
}
|
83
86
|
|
84
87
|
SERVERS = [
|
85
88
|
:apache,
|
@@ -114,6 +117,7 @@ class Manager
|
|
114
117
|
windows: 'MS Windows',
|
115
118
|
|
116
119
|
# Databases
|
120
|
+
sql: 'Generic SQL family',
|
117
121
|
mysql: 'MySQL',
|
118
122
|
pgsql: 'Postgresql',
|
119
123
|
mssql: 'MSSQL',
|
@@ -121,7 +125,6 @@ class Manager
|
|
121
125
|
sqlite: 'SQLite',
|
122
126
|
emc: 'EMC',
|
123
127
|
db2: 'DB2',
|
124
|
-
coldfusion: 'ColdFusion',
|
125
128
|
interbase: 'InterBase',
|
126
129
|
informix: 'Informix',
|
127
130
|
firebird: 'Firebird',
|
@@ -131,6 +134,7 @@ class Manager
|
|
131
134
|
ingres: 'IngresDB',
|
132
135
|
hsqldb: 'HSQLDB',
|
133
136
|
access: 'MS Access',
|
137
|
+
nosql: 'Generic NoSQL family',
|
134
138
|
mongodb: 'MongoDB',
|
135
139
|
|
136
140
|
# Web servers
|
@@ -227,7 +227,7 @@ class Framework
|
|
227
227
|
return false if aborting? || aborted?
|
228
228
|
|
229
229
|
if !running?
|
230
|
-
fail Error::StateNotAbortable, 'Cannot
|
230
|
+
fail Error::StateNotAbortable, 'Cannot abort an idle state.'
|
231
231
|
end
|
232
232
|
|
233
233
|
set_status_message :aborting
|
@@ -264,6 +264,12 @@ class Framework
|
|
264
264
|
@status == :aborting
|
265
265
|
end
|
266
266
|
|
267
|
+
# @return [Bool]
|
268
|
+
# `true` if the system has completed successfully, `false` otherwise.
|
269
|
+
def done?
|
270
|
+
@status == :done
|
271
|
+
end
|
272
|
+
|
267
273
|
# @param [Bool] block
|
268
274
|
# `true` if the method should block until a suspend has completed,
|
269
275
|
# `false` otherwise.
|
data/lib/arachni/utilities.rb
CHANGED
@@ -413,6 +413,16 @@ module Utilities
|
|
413
413
|
nil
|
414
414
|
end
|
415
415
|
|
416
|
+
def regexp_array_match( regexps, str )
|
417
|
+
regexps = [regexps].flatten.compact.
|
418
|
+
map { |s| s.is_a?( Regexp ) ? s : Regexp.new( s.to_s ) }
|
419
|
+
return true if regexps.empty?
|
420
|
+
|
421
|
+
cnt = 0
|
422
|
+
regexps.each { |filter| cnt += 1 if str =~ filter }
|
423
|
+
cnt == regexps.size
|
424
|
+
end
|
425
|
+
|
416
426
|
def remove_constants( mod, skip = [] )
|
417
427
|
return if skip.include?( mod )
|
418
428
|
return if !(mod.is_a?( Class ) || mod.is_a?( Module )) ||
|
data/lib/version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.6
|
@@ -1211,6 +1211,19 @@ describe Arachni::Browser do
|
|
1211
1211
|
inputs[:email]
|
1212
1212
|
end
|
1213
1213
|
|
1214
|
+
context 'when one of those inputs is a' do
|
1215
|
+
context 'select' do
|
1216
|
+
let(:url) { "#{@url}/fire_event/form/select" }
|
1217
|
+
|
1218
|
+
it 'selects it' do
|
1219
|
+
@browser.watir.div( id: 'container-name' ).text.should ==
|
1220
|
+
inputs[:name]
|
1221
|
+
@browser.watir.div( id: 'container-email' ).text.should ==
|
1222
|
+
inputs[:email]
|
1223
|
+
end
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
|
1214
1227
|
context 'but has missing values' do
|
1215
1228
|
let(:inputs) do
|
1216
1229
|
{ name: 'The Dude' }
|
@@ -13,6 +13,7 @@ describe Arachni::Check::Base do
|
|
13
13
|
after( :each ) do
|
14
14
|
@framework.reset
|
15
15
|
Arachni::Options.reset
|
16
|
+
described_class.clear_info_cache
|
16
17
|
end
|
17
18
|
|
18
19
|
subject { described_class.new( Factory[:page], framework ) }
|
@@ -30,4 +31,83 @@ describe Arachni::Check::Base do
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
34
|
+
describe '#has_platforms?' do
|
35
|
+
context 'when platforms are provided' do
|
36
|
+
before do
|
37
|
+
described_class.stub(:info) { { platforms: [ :unix ] } }
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns true' do
|
41
|
+
described_class.has_platforms?.should be_true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when platforms are not provided' do
|
46
|
+
before do
|
47
|
+
described_class.stub(:info) { { platforms: [] } }
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns false' do
|
51
|
+
described_class.has_platforms?.should be_false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#supports_platforms?' do
|
57
|
+
context 'when empty platforms are given' do
|
58
|
+
it 'returns true' do
|
59
|
+
described_class.supports_platforms?([]).should be_true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when no supported platforms are declared' do
|
64
|
+
before do
|
65
|
+
described_class.stub(:info) { { platforms: [] } }
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns true' do
|
69
|
+
described_class.supports_platforms?([]).should be_true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'when any of the given platforms are supported' do
|
74
|
+
before do
|
75
|
+
described_class.stub(:info) { { platforms: [:php] } }
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'returns true' do
|
79
|
+
described_class.supports_platforms?([:unix, :php]).should be_true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when a parent of any of the given platforms is supported' do
|
84
|
+
before do
|
85
|
+
described_class.stub(:info) { { platforms: [:unix] } }
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'returns true' do
|
89
|
+
described_class.supports_platforms?([:linux]).should be_true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when a child of any of the given platforms is supported' do
|
94
|
+
before do
|
95
|
+
described_class.stub(:info) { { platforms: [:linux] } }
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'returns true' do
|
99
|
+
described_class.supports_platforms?([:unix]).should be_true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when none of the given platforms are not provided' do
|
104
|
+
before do
|
105
|
+
described_class.stub(:info) { { platforms: [:windows] } }
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'returns false' do
|
109
|
+
described_class.supports_platforms?([:unix]).should be_false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
33
113
|
end
|