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.
Files changed (44) hide show
  1. data/CHANGELOG.md +30 -0
  2. data/CONTRIBUTORS.md +1 -0
  3. data/README.md +28 -8
  4. data/Rakefile +1 -0
  5. data/bin/arachni_web_autostart +46 -0
  6. data/lib/anemone/page.rb +1 -0
  7. data/lib/arachni.rb +1 -1
  8. data/lib/framework.rb +8 -3
  9. data/lib/http.rb +9 -39
  10. data/lib/mixins/observable.rb +87 -0
  11. data/lib/module/auditor.rb +14 -0
  12. data/lib/module/base.rb +0 -14
  13. data/lib/nokogiri/xml/node.rb +42 -0
  14. data/lib/ui/cli/cli.rb +1 -1
  15. data/lib/ui/web/log.rb +21 -14
  16. data/lib/ui/web/report_manager.rb +100 -15
  17. data/lib/ui/web/server.rb +24 -33
  18. data/lib/ui/web/server/public/reports/demo.testfire.net:Sun Mar 20 02:48:10 2011.afr +104829 -0
  19. data/lib/ui/web/server/views/layout.erb +1 -1
  20. data/lib/ui/web/server/views/options.erb +10 -2
  21. data/lib/ui/web/server/views/plugins.erb +1 -1
  22. data/lib/ui/web/server/views/reports.erb +8 -4
  23. data/lib/ui/xmlrpc/xmlrpc.rb +1 -1
  24. data/metamodules/autothrottle.rb +2 -2
  25. data/metamodules/timeout_notice.rb +1 -1
  26. data/modules/audit/sqli_blind_rdiff.rb +1 -1
  27. data/modules/recon/common_files/filenames.txt +2 -0
  28. data/modules/recon/directory_listing.rb +1 -0
  29. data/modules/recon/interesting_responses.rb +3 -3
  30. data/path_extractors/generic.rb +5 -1
  31. data/plugins/autologin.rb +15 -4
  32. data/plugins/content_types.rb +2 -2
  33. data/plugins/cookie_collector.rb +9 -16
  34. data/plugins/profiler.rb +237 -0
  35. data/reports/html.rb +21 -6
  36. data/reports/html/default.erb +4 -2
  37. data/reports/plugin_formatters/html/autologin.rb +63 -0
  38. data/reports/plugin_formatters/html/profiler.rb +71 -0
  39. data/reports/plugin_formatters/html/profiler/template.erb +177 -0
  40. data/reports/plugin_formatters/stdout/autologin.rb +55 -0
  41. data/reports/plugin_formatters/stdout/profiler.rb +90 -0
  42. data/reports/plugin_formatters/xml/autologin.rb +68 -0
  43. data/reports/plugin_formatters/xml/profiler.rb +120 -0
  44. metadata +23 -68
@@ -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( :default, "sqlite3://#{@settings.db}/log.db" )
46
- DataMapper.finalize
47
-
48
- Entry.auto_upgrade!
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
- Entry.create(
69
- :action => action,
70
- :owner => owner,
71
- :object => object,
72
- :client_addr => addr,
73
- :client_host => host,
74
- :datestamp => Time.now
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
- def initialize( opts, settings )
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
- end
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
- Dir.glob( savedir + "*#{EXTENSION}" )
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( report )
102
- FileUtils.rm( savedir + File.basename( report, '.afr' ) + '.afr' )
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 = YAML::load( report )
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 html, txt, xml, etc
123
- # @param [String] report_file path to the report
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, report_file )
191
+ def get( type, id )
128
192
  return if !valid_class?( type )
129
193
 
130
- location = savedir + report_file + EXTENSION
131
- convert( type, File.read( location ) )
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( YAML::load( report ), opts ).run
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', File.basename( report, '.afr' ) )
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
- show :plugins, true
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
- 'report' => '"data:text/html;base64, ' +
702
- Base64.encode64( save_shutdown_and_show( arachni ) ) + '"'
703
- }.to_json
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 Errno::ECONNREFUSED
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 Errno::ECONNREFUSED
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 Errno::ECONNREFUSED
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/:name/delete' do
818
- settings.reports.delete( params[:name] )
819
- settings.log.report_deleted( env, params[:name] )
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/:name.:type' do
825
- settings.log.report_converted( env, params[:name] + '.' + params[:type] )
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[:name] )
818
+ settings.reports.get( params[:type], params[:id] )
828
819
  end
829
820
 
830
821
  get '/log' do