arachni 0.2.2.1 → 0.2.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +30 -0
- data/CONTRIBUTORS.md +1 -0
- data/README.md +28 -8
- data/Rakefile +1 -0
- data/bin/arachni_web_autostart +46 -0
- data/lib/anemone/page.rb +1 -0
- data/lib/arachni.rb +1 -1
- data/lib/framework.rb +8 -3
- data/lib/http.rb +9 -39
- data/lib/mixins/observable.rb +87 -0
- data/lib/module/auditor.rb +14 -0
- data/lib/module/base.rb +0 -14
- data/lib/nokogiri/xml/node.rb +42 -0
- data/lib/ui/cli/cli.rb +1 -1
- data/lib/ui/web/log.rb +21 -14
- data/lib/ui/web/report_manager.rb +100 -15
- data/lib/ui/web/server.rb +24 -33
- data/lib/ui/web/server/public/reports/demo.testfire.net:Sun Mar 20 02:48:10 2011.afr +104829 -0
- data/lib/ui/web/server/views/layout.erb +1 -1
- data/lib/ui/web/server/views/options.erb +10 -2
- data/lib/ui/web/server/views/plugins.erb +1 -1
- data/lib/ui/web/server/views/reports.erb +8 -4
- data/lib/ui/xmlrpc/xmlrpc.rb +1 -1
- data/metamodules/autothrottle.rb +2 -2
- data/metamodules/timeout_notice.rb +1 -1
- data/modules/audit/sqli_blind_rdiff.rb +1 -1
- data/modules/recon/common_files/filenames.txt +2 -0
- data/modules/recon/directory_listing.rb +1 -0
- data/modules/recon/interesting_responses.rb +3 -3
- data/path_extractors/generic.rb +5 -1
- data/plugins/autologin.rb +15 -4
- data/plugins/content_types.rb +2 -2
- data/plugins/cookie_collector.rb +9 -16
- data/plugins/profiler.rb +237 -0
- data/reports/html.rb +21 -6
- data/reports/html/default.erb +4 -2
- data/reports/plugin_formatters/html/autologin.rb +63 -0
- data/reports/plugin_formatters/html/profiler.rb +71 -0
- data/reports/plugin_formatters/html/profiler/template.erb +177 -0
- data/reports/plugin_formatters/stdout/autologin.rb +55 -0
- data/reports/plugin_formatters/stdout/profiler.rb +90 -0
- data/reports/plugin_formatters/xml/autologin.rb +68 -0
- data/reports/plugin_formatters/xml/profiler.rb +120 -0
- metadata +23 -68
data/lib/module/auditor.rb
CHANGED
@@ -299,6 +299,20 @@ module Auditor
|
|
299
299
|
}
|
300
300
|
end
|
301
301
|
|
302
|
+
#
|
303
|
+
# ABSTRACT - OPTIONAL
|
304
|
+
#
|
305
|
+
# This is called right before an [Arachni::Parser::Element]
|
306
|
+
# is submitted/auditted and is used to determine whether to skip it or not.
|
307
|
+
#
|
308
|
+
# Implementation details are left up to the running module.
|
309
|
+
#
|
310
|
+
# @param [Arachni::Parser::Element] elem
|
311
|
+
#
|
312
|
+
def skip?( elem )
|
313
|
+
return false
|
314
|
+
end
|
315
|
+
|
302
316
|
|
303
317
|
#
|
304
318
|
# Audits elements using a 2 phase timing attack and logs results.
|
data/lib/module/base.rb
CHANGED
@@ -107,20 +107,6 @@ class Base
|
|
107
107
|
def run( )
|
108
108
|
end
|
109
109
|
|
110
|
-
#
|
111
|
-
# ABSTRACT - OPTIONAL
|
112
|
-
#
|
113
|
-
# This is called right before an [Arachni::Parser::Element]
|
114
|
-
# is submitted/auditted and is used to determine whether to skip it or not.
|
115
|
-
#
|
116
|
-
# Implementation details are left up to the running module.
|
117
|
-
#
|
118
|
-
# @param [Arachni::Parser::Element] elem
|
119
|
-
#
|
120
|
-
def skip?( elem )
|
121
|
-
return false
|
122
|
-
end
|
123
|
-
|
124
110
|
#
|
125
111
|
# ABSTRACT - OPTIONAL
|
126
112
|
#
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Nokogiri
|
2
|
+
module XML
|
3
|
+
class Node
|
4
|
+
|
5
|
+
###
|
6
|
+
# Serialize Node using +options+. Save options can also be set using a
|
7
|
+
# block. See SaveOptions.
|
8
|
+
#
|
9
|
+
# These two statements are equivalent:
|
10
|
+
#
|
11
|
+
# node.serialize(:encoding => 'UTF-8', :save_with => FORMAT | AS_XML)
|
12
|
+
#
|
13
|
+
# or
|
14
|
+
#
|
15
|
+
# node.serialize(:encoding => 'UTF-8') do |config|
|
16
|
+
# config.format.as_xml
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
def serialize *args, &block
|
20
|
+
options = args.first.is_a?(Hash) ? args.shift : {
|
21
|
+
:encoding => args[0],
|
22
|
+
:save_with => args[1] || SaveOptions::FORMAT
|
23
|
+
}
|
24
|
+
|
25
|
+
encoding = options[:encoding] || document.encoding
|
26
|
+
|
27
|
+
outstring = ""
|
28
|
+
if encoding && outstring.respond_to?(:force_encoding)
|
29
|
+
begin
|
30
|
+
outstring.force_encoding(Encoding.find(encoding))
|
31
|
+
rescue
|
32
|
+
outstring.force_encoding(Encoding.find('UTF-8'))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
io = StringIO.new(outstring)
|
36
|
+
write_to io, options, &block
|
37
|
+
io.string
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/ui/cli/cli.rb
CHANGED
@@ -554,7 +554,7 @@ class CLI
|
|
554
554
|
<zapotek@segfault.gr>
|
555
555
|
(With the support of the community and the Arachni Team.)
|
556
556
|
|
557
|
-
Website: http://github.com/Zapotek/arachni
|
557
|
+
Website: http://arachni.segfault.gr - http://github.com/Zapotek/arachni
|
558
558
|
Documentation: http://github.com/Zapotek/arachni/wiki'
|
559
559
|
print_line
|
560
560
|
print_line
|
data/lib/ui/web/log.rb
CHANGED
@@ -20,13 +20,17 @@ module Web
|
|
20
20
|
# @author: Tasos "Zapotek" Laskos
|
21
21
|
# <tasos.laskos@gmail.com>
|
22
22
|
# <zapotek@segfault.gr>
|
23
|
-
# @version: 0.1
|
23
|
+
# @version: 0.1.1
|
24
24
|
#
|
25
25
|
class Log
|
26
26
|
|
27
27
|
class Entry
|
28
28
|
include DataMapper::Resource
|
29
29
|
|
30
|
+
def self.default_repository_name
|
31
|
+
:log
|
32
|
+
end
|
33
|
+
|
30
34
|
property :id, Serial
|
31
35
|
property :action, String
|
32
36
|
property :object, String
|
@@ -42,14 +46,15 @@ class Log
|
|
42
46
|
@opts = opts
|
43
47
|
@settings = settings
|
44
48
|
|
45
|
-
DataMapper::setup( :
|
46
|
-
DataMapper.
|
47
|
-
|
48
|
-
|
49
|
+
DataMapper::setup( :log, "sqlite3://#{@settings.db}/log.db" )
|
50
|
+
DataMapper.repository( :log ) {
|
51
|
+
DataMapper.finalize
|
52
|
+
Entry.auto_upgrade!
|
53
|
+
}
|
49
54
|
end
|
50
55
|
|
51
56
|
def entry
|
52
|
-
Entry
|
57
|
+
DataMapper.repository( :log ) { Entry }
|
53
58
|
end
|
54
59
|
|
55
60
|
def method_missing( sym, *args, &block )
|
@@ -65,14 +70,16 @@ class Log
|
|
65
70
|
host = env['REMOTE_HOST']
|
66
71
|
end
|
67
72
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
DataMapper.repository( :log ) {
|
74
|
+
Entry.create(
|
75
|
+
:action => action,
|
76
|
+
:owner => owner,
|
77
|
+
:object => object,
|
78
|
+
:client_addr => addr,
|
79
|
+
:client_host => host,
|
80
|
+
:datestamp => Time.now.asctime
|
81
|
+
)
|
82
|
+
}
|
76
83
|
end
|
77
84
|
|
78
85
|
end
|
@@ -8,6 +8,8 @@
|
|
8
8
|
|
9
9
|
=end
|
10
10
|
|
11
|
+
require 'datamapper'
|
12
|
+
|
11
13
|
module Arachni
|
12
14
|
module UI
|
13
15
|
module Web
|
@@ -20,18 +22,58 @@ module Web
|
|
20
22
|
# @author: Tasos "Zapotek" Laskos
|
21
23
|
# <tasos.laskos@gmail.com>
|
22
24
|
# <zapotek@segfault.gr>
|
23
|
-
# @version: 0.1
|
25
|
+
# @version: 0.1.1
|
24
26
|
#
|
25
27
|
class ReportManager
|
26
28
|
|
27
29
|
FOLDERNAME = "reports"
|
28
30
|
EXTENSION = '.afr'
|
29
31
|
|
30
|
-
|
32
|
+
class Report
|
33
|
+
include DataMapper::Resource
|
34
|
+
|
35
|
+
property :id, Serial
|
36
|
+
property :host, String
|
37
|
+
property :issue_count, Integer
|
38
|
+
property :filename, String
|
39
|
+
property :datestamp, DateTime
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def initialize( opts, settings )
|
31
44
|
@opts = opts
|
32
45
|
@settings = settings
|
33
46
|
populate_available
|
34
|
-
|
47
|
+
|
48
|
+
DataMapper::setup( :default, "sqlite3://#{@settings.db}/default.db" )
|
49
|
+
DataMapper.finalize
|
50
|
+
|
51
|
+
Report.auto_upgrade!
|
52
|
+
|
53
|
+
migrate_files
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Migrates AFR reports from the savedir folder into the DB
|
58
|
+
# so that users will be able to manage them via the WebUI
|
59
|
+
#
|
60
|
+
def migrate_files
|
61
|
+
Dir.glob( "#{savedir}*" + EXTENSION ).each {
|
62
|
+
|file|
|
63
|
+
next if Report.first( :filename => File.basename( file, EXTENSION ) )
|
64
|
+
|
65
|
+
begin
|
66
|
+
data = File.read( file )
|
67
|
+
Report.create(
|
68
|
+
:issue_count => get_issue_count( data ),
|
69
|
+
:host => get_host( data ),
|
70
|
+
:filename => File.basename( file, EXTENSION ),
|
71
|
+
:datestamp => get_finish_datetime( data )
|
72
|
+
)
|
73
|
+
rescue
|
74
|
+
end
|
75
|
+
}
|
76
|
+
end
|
35
77
|
|
36
78
|
#
|
37
79
|
# @return [String] save directory
|
@@ -87,19 +129,29 @@ class ReportManager
|
|
87
129
|
#
|
88
130
|
# @return [Array]
|
89
131
|
#
|
90
|
-
def all
|
91
|
-
|
132
|
+
def all( *args )
|
133
|
+
Report.all( *args )
|
92
134
|
end
|
93
135
|
|
94
136
|
def delete_all
|
95
137
|
all.each {
|
96
138
|
|report|
|
97
|
-
delete( report )
|
139
|
+
delete( report.id )
|
98
140
|
}
|
141
|
+
all.destroy
|
99
142
|
end
|
100
143
|
|
101
|
-
def delete(
|
102
|
-
|
144
|
+
def delete( id )
|
145
|
+
report = Report.get( id )
|
146
|
+
begin
|
147
|
+
FileUtils.rm( savedir + Report.get( id ).filename + EXTENSION )
|
148
|
+
rescue
|
149
|
+
end
|
150
|
+
|
151
|
+
begin
|
152
|
+
report.destroy
|
153
|
+
rescue
|
154
|
+
end
|
103
155
|
end
|
104
156
|
|
105
157
|
#
|
@@ -112,23 +164,39 @@ class ReportManager
|
|
112
164
|
# @return [String] host:audit_date
|
113
165
|
#
|
114
166
|
def get_filename( report )
|
115
|
-
rep =
|
167
|
+
rep = unserialize( report )
|
116
168
|
filename = "#{URI(rep.options['url']).host}:#{rep.start_datetime}"
|
117
169
|
end
|
118
170
|
|
171
|
+
def get_issue_count( report )
|
172
|
+
unserialize( report ).issues.size
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_host( report )
|
176
|
+
return URI(unserialize( report ).options['url']).host
|
177
|
+
end
|
178
|
+
|
179
|
+
def get_finish_datetime( report )
|
180
|
+
return unserialize( report ).finish_datetime
|
181
|
+
end
|
182
|
+
|
119
183
|
#
|
120
184
|
# Returns a stored report as a <type> file. Basically a convertion/export method.
|
121
185
|
#
|
122
|
-
# @param [String] type
|
123
|
-
# @param [
|
186
|
+
# @param [String] type html, txt, xml, etc
|
187
|
+
# @param [Integer] id report id
|
124
188
|
#
|
125
189
|
# @return [String] the converted report
|
126
190
|
#
|
127
|
-
def get( type,
|
191
|
+
def get( type, id )
|
128
192
|
return if !valid_class?( type )
|
129
193
|
|
130
|
-
|
131
|
-
|
194
|
+
begin
|
195
|
+
location = savedir + Report.get( id ).filename + EXTENSION
|
196
|
+
convert( type, File.read( location ) )
|
197
|
+
rescue
|
198
|
+
return nil
|
199
|
+
end
|
132
200
|
end
|
133
201
|
|
134
202
|
#
|
@@ -151,11 +219,28 @@ class ReportManager
|
|
151
219
|
|
152
220
|
private
|
153
221
|
|
222
|
+
def unserialize( data )
|
223
|
+
begin
|
224
|
+
Marshal.load( data )
|
225
|
+
rescue
|
226
|
+
YAML.load( data )
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
154
230
|
def save_to_file( data, file )
|
231
|
+
return file if File.exists?( file )
|
232
|
+
|
155
233
|
f = File.new( file, 'w' )
|
156
234
|
f.write( data )
|
157
235
|
f.close
|
158
236
|
|
237
|
+
Report.create(
|
238
|
+
:issue_count => get_issue_count( data ),
|
239
|
+
:host => get_host( data ),
|
240
|
+
:filename => File.basename( f.path, EXTENSION ),
|
241
|
+
:datestamp => Time.now.asctime
|
242
|
+
)
|
243
|
+
|
159
244
|
return f.path
|
160
245
|
end
|
161
246
|
|
@@ -168,7 +253,7 @@ class ReportManager
|
|
168
253
|
}
|
169
254
|
opts['outfile'] = get_tmp_outfile_name( type, report )
|
170
255
|
|
171
|
-
classes[type].new(
|
256
|
+
classes[type].new( unserialize( report ), opts ).run
|
172
257
|
|
173
258
|
content = File.read( opts['outfile'] )
|
174
259
|
FileUtils.rm( opts['outfile'] )
|
data/lib/ui/web/server.rb
CHANGED
@@ -41,14 +41,14 @@ require Arachni::Options.instance.dir['lib'] + 'ui/web/output_stream'
|
|
41
41
|
# @author: Tasos "Zapotek" Laskos
|
42
42
|
# <tasos.laskos@gmail.com>
|
43
43
|
# <zapotek@segfault.gr>
|
44
|
-
# @version: 0.1-pre
|
44
|
+
# @version: 0.1.1-pre
|
45
45
|
#
|
46
46
|
# @see Arachni::RPC::XML::Client::Instance
|
47
47
|
# @see Arachni::RPC::XML::Client::Dispatcher
|
48
48
|
#
|
49
49
|
module Web
|
50
50
|
|
51
|
-
VERSION = '0.1-pre'
|
51
|
+
VERSION = '0.1.1-pre'
|
52
52
|
|
53
53
|
class Server < Sinatra::Base
|
54
54
|
|
@@ -457,7 +457,7 @@ class Server < Sinatra::Base
|
|
457
457
|
#
|
458
458
|
def save_shutdown_and_show( arachni )
|
459
459
|
report = save_and_shutdown( arachni )
|
460
|
-
settings.reports.get( 'html',
|
460
|
+
settings.reports.get( 'html', settings.reports.all.last.id )
|
461
461
|
end
|
462
462
|
|
463
463
|
#
|
@@ -466,7 +466,7 @@ class Server < Sinatra::Base
|
|
466
466
|
# @param [Arachni::RPC::XML::Client::Instance] arachni
|
467
467
|
#
|
468
468
|
def save_and_shutdown( arachni )
|
469
|
-
arachni.framework.clean_up!
|
469
|
+
arachni.framework.clean_up!( true )
|
470
470
|
report_path = settings.reports.save( arachni.framework.auditstore )
|
471
471
|
arachni.service.shutdown!
|
472
472
|
return report_path
|
@@ -641,7 +641,7 @@ class Server < Sinatra::Base
|
|
641
641
|
get "/plugins" do
|
642
642
|
fill_component_cache
|
643
643
|
prep_session
|
644
|
-
erb :plugins, { :layout => true }
|
644
|
+
erb :plugins, { :layout => true }, :session_options => YAML::load( session['opts']['plugins'] )
|
645
645
|
end
|
646
646
|
|
647
647
|
#
|
@@ -650,7 +650,7 @@ class Server < Sinatra::Base
|
|
650
650
|
post "/plugins" do
|
651
651
|
session['opts']['plugins'] = YAML::dump( prep_plugins( escape_hash( params ) ) )
|
652
652
|
flash.now[:ok] = "Plugins updated."
|
653
|
-
|
653
|
+
erb :plugins, { :layout => true }, :session_options => YAML::load( session['opts']['plugins'] )
|
654
654
|
end
|
655
655
|
|
656
656
|
get "/settings" do
|
@@ -697,12 +697,16 @@ class Server < Sinatra::Base
|
|
697
697
|
{ 'data' => OutputStream.new( arachni, 38 ).data }.to_json
|
698
698
|
else
|
699
699
|
settings.log.instance_shutdown( env, port_to_url( params[:port] ) )
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
700
|
+
save_and_shutdown( arachni )
|
701
|
+
|
702
|
+
# {
|
703
|
+
# 'report' => '"data:text/html;base64, ' +
|
704
|
+
# Base64.encode64( save_shutdown_and_show( arachni ) ) + '"'
|
705
|
+
# }.to_json
|
706
|
+
|
707
|
+
{ 'status' => 'finished', 'data' => "The server has been shut down." }.to_json
|
704
708
|
end
|
705
|
-
rescue
|
709
|
+
rescue
|
706
710
|
{ 'status' => 'finished', 'data' => "The server has been shut down." }.to_json
|
707
711
|
end
|
708
712
|
end
|
@@ -715,7 +719,7 @@ class Server < Sinatra::Base
|
|
715
719
|
out = erb( :output_results, { :layout => false }, :issues => YAML.load( arachni.framework.auditstore ).issues)
|
716
720
|
{ 'data' => out }.to_json
|
717
721
|
end
|
718
|
-
rescue
|
722
|
+
rescue
|
719
723
|
{ 'data' => "The server has been shut down." }.to_json
|
720
724
|
end
|
721
725
|
end
|
@@ -727,7 +731,7 @@ class Server < Sinatra::Base
|
|
727
731
|
stats = arachni.framework.stats
|
728
732
|
stats['current_page'] = escape( stats['current_page'] )
|
729
733
|
{ 'refresh' => true, 'stats' => stats }.to_json
|
730
|
-
rescue
|
734
|
+
rescue
|
731
735
|
{ 'refresh' => false }.to_json
|
732
736
|
end
|
733
737
|
end
|
@@ -786,20 +790,7 @@ class Server < Sinatra::Base
|
|
786
790
|
end
|
787
791
|
|
788
792
|
get "/reports" do
|
789
|
-
|
790
|
-
reports = []
|
791
|
-
settings.reports.all.each {
|
792
|
-
|report|
|
793
|
-
name = File.basename( report, '.afr' )
|
794
|
-
host, date = name.split( ':', 2 )
|
795
|
-
reports << {
|
796
|
-
'host' => host,
|
797
|
-
'date' => date,
|
798
|
-
'name' => name
|
799
|
-
}
|
800
|
-
}
|
801
|
-
|
802
|
-
erb :reports, { :layout => true }, :reports => reports,
|
793
|
+
erb :reports, { :layout => true }, :reports => settings.reports.all( :order => :datestamp.desc ),
|
803
794
|
:available => settings.reports.available
|
804
795
|
end
|
805
796
|
|
@@ -814,17 +805,17 @@ class Server < Sinatra::Base
|
|
814
805
|
redirect '/reports'
|
815
806
|
end
|
816
807
|
|
817
|
-
post '/report/:
|
818
|
-
settings.reports.delete( params[:
|
819
|
-
settings.log.report_deleted( env, params[:
|
808
|
+
post '/report/:id/delete' do
|
809
|
+
settings.reports.delete( params[:id] )
|
810
|
+
settings.log.report_deleted( env, params[:id] )
|
820
811
|
|
821
812
|
redirect '/reports'
|
822
813
|
end
|
823
814
|
|
824
|
-
get '/report/:
|
825
|
-
settings.log.report_converted( env, params[:
|
815
|
+
get '/report/:id.:type' do
|
816
|
+
settings.log.report_converted( env, params[:id] + '.' + params[:type] )
|
826
817
|
content_type( params[:type], :default => 'application/octet-stream' )
|
827
|
-
settings.reports.get( params[:type], params[:
|
818
|
+
settings.reports.get( params[:type], params[:id] )
|
828
819
|
end
|
829
820
|
|
830
821
|
get '/log' do
|