arachni 0.2.2.1 → 0.2.2.2
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 +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
|