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.
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