arachni 0.2.4 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/CHANGELOG.md +33 -0
  2. data/README.md +2 -4
  3. data/Rakefile +15 -4
  4. data/bin/arachni +0 -0
  5. data/bin/arachni_web +0 -0
  6. data/bin/arachni_web_autostart +0 -0
  7. data/bin/arachni_xmlrpc +0 -0
  8. data/bin/arachni_xmlrpcd +0 -0
  9. data/bin/arachni_xmlrpcd_monitor +0 -0
  10. data/lib/arachni.rb +1 -1
  11. data/lib/framework.rb +36 -6
  12. data/lib/http.rb +12 -5
  13. data/lib/module/auditor.rb +482 -59
  14. data/lib/module/base.rb +17 -0
  15. data/lib/module/manager.rb +26 -2
  16. data/lib/module/trainer.rb +1 -12
  17. data/lib/module/utilities.rb +12 -0
  18. data/lib/parser/auditable.rb +8 -3
  19. data/lib/parser/elements.rb +11 -0
  20. data/lib/parser/page.rb +3 -1
  21. data/lib/parser/parser.rb +130 -18
  22. data/lib/rpc/xml/server/dispatcher.rb +21 -0
  23. data/lib/spider.rb +141 -82
  24. data/lib/ui/cli/cli.rb +2 -3
  25. data/lib/ui/web/addon_manager.rb +273 -0
  26. data/lib/ui/web/addons/autodeploy.rb +172 -0
  27. data/lib/ui/web/addons/autodeploy/lib/manager.rb +291 -0
  28. data/lib/ui/web/addons/autodeploy/views/index.erb +124 -0
  29. data/lib/ui/web/addons/sample.rb +78 -0
  30. data/lib/ui/web/addons/sample/views/index.erb +4 -0
  31. data/lib/ui/web/addons/scheduler.rb +139 -0
  32. data/lib/ui/web/addons/scheduler/views/index.erb +131 -0
  33. data/lib/ui/web/addons/scheduler/views/options.erb +93 -0
  34. data/lib/ui/web/dispatcher_manager.rb +80 -13
  35. data/lib/ui/web/instance_manager.rb +87 -0
  36. data/lib/ui/web/scheduler.rb +166 -0
  37. data/lib/ui/web/server.rb +142 -202
  38. data/lib/ui/web/server/public/js/jquery-ui-timepicker.js +985 -0
  39. data/lib/ui/web/server/public/plugins/sample/style.css +0 -0
  40. data/lib/ui/web/server/public/style.css +42 -0
  41. data/lib/ui/web/server/views/addon.erb +15 -0
  42. data/lib/ui/web/server/views/addons.erb +46 -0
  43. data/lib/ui/web/server/views/dispatchers.erb +1 -1
  44. data/lib/ui/web/server/views/instance.erb +9 -11
  45. data/lib/ui/web/server/views/layout.erb +14 -1
  46. data/lib/ui/web/server/views/welcome.erb +7 -6
  47. data/lib/ui/web/utilities.rb +134 -0
  48. data/modules/audit/code_injection_timing.rb +6 -2
  49. data/modules/audit/code_injection_timing/payloads.txt +2 -2
  50. data/modules/audit/os_cmd_injection_timing.rb +7 -3
  51. data/modules/audit/os_cmd_injection_timing/payloads.txt +1 -1
  52. data/modules/audit/sqli_blind_rdiff.rb +18 -233
  53. data/modules/audit/sqli_blind_rdiff/payloads.txt +5 -0
  54. data/modules/audit/sqli_blind_timing.rb +9 -2
  55. data/path_extractors/anchors.rb +1 -1
  56. data/path_extractors/forms.rb +1 -1
  57. data/path_extractors/frames.rb +1 -1
  58. data/path_extractors/generic.rb +1 -1
  59. data/path_extractors/links.rb +1 -1
  60. data/path_extractors/meta_refresh.rb +1 -1
  61. data/path_extractors/scripts.rb +1 -1
  62. data/path_extractors/sitemap.rb +1 -1
  63. data/plugins/proxy/server.rb +3 -2
  64. data/plugins/waf_detector.rb +0 -3
  65. metadata +37 -34
  66. data/lib/anemone/cookie_store.rb +0 -35
  67. data/lib/anemone/core.rb +0 -371
  68. data/lib/anemone/exceptions.rb +0 -5
  69. data/lib/anemone/http.rb +0 -144
  70. data/lib/anemone/page.rb +0 -338
  71. data/lib/anemone/page_store.rb +0 -160
  72. data/lib/anemone/storage.rb +0 -34
  73. data/lib/anemone/storage/base.rb +0 -75
  74. data/lib/anemone/storage/exceptions.rb +0 -15
  75. data/lib/anemone/storage/mongodb.rb +0 -89
  76. data/lib/anemone/storage/pstore.rb +0 -50
  77. data/lib/anemone/storage/redis.rb +0 -90
  78. data/lib/anemone/storage/tokyo_cabinet.rb +0 -57
  79. data/lib/anemone/tentacle.rb +0 -40
@@ -0,0 +1,93 @@
1
+
2
+
3
+ <div style="display: none" id="dialog_options_<%=job.id%>" title="Options">
4
+
5
+ <div class="left options">
6
+ <fieldset>
7
+ <legend>Auditor</legend>
8
+ <p>
9
+ Audit links: <input disabled type="checkbox" name="audit_links" <% if opts['audit_links'] == true %> checked="checked" <% end %> />
10
+ </p>
11
+ <p>
12
+ Audit forms: <input disabled type="checkbox" name="audit_forms" <% if opts['audit_forms'] == true %> checked="checked" <% end %> />
13
+ </p>
14
+ <p>
15
+ Audit cookies: <input disabled type="checkbox" name="audit_cookies" <% if opts['audit_cookies'] == true %> checked="checked" <% end %> />
16
+ </p>
17
+ <p>
18
+ Audit headers: <input disabled type="checkbox" name="audit_headers" <% if opts['audit_headers'] == true %> checked="checked" <% end %> />
19
+ </p>
20
+ <p>
21
+ Cookies to exclude: <textarea disabled rows="2" cols="20" name="exclude_cookies"><%=opts['exclude_cookies']%></textarea>
22
+ <br/>(Newline separated)
23
+ </p>
24
+ </fieldset>
25
+
26
+ <fieldset>
27
+ <legend>HTTP options</legend>
28
+ <p>
29
+ Cocurrent HTTP request limit: <label for="http_req_limit"></label>
30
+ <input disabled id="http_req_limit" name="http_req_limit"/>
31
+ <div id="slider"></div>
32
+ <br/>
33
+ </p>
34
+ <p>
35
+ HTTP harvest last: <input disabled type="checkbox" name="http_harvest_last" <% if opts['http_harvest_last'] == true %> checked="checked" <% end %> />
36
+ </p>
37
+ <p>
38
+ Cookie jar: <input disabled type="file" name="cookiejar" size="25" />
39
+ </p>
40
+ <p>
41
+ User agent: <input disabled name="user_agent" value="<%=opts['user_agent']%>"/>
42
+ </p>
43
+ <p>
44
+ Authorized by: <input disabled name="authed_by" value="<%=opts['authed_by']%>"/>
45
+ </p>
46
+
47
+ </fieldset>
48
+ </div>
49
+
50
+ <div class="right options">
51
+
52
+ <fieldset>
53
+ <legend>Crawler options</legend>
54
+ <p>
55
+ Exclude rules: <textarea disabled rows="2" cols="20" name="exclude"><% if opts['exclude']%><%=opts['exclude'].join( "\r\n" )%><%end%></textarea>
56
+ <br/>(Newline separated)
57
+ </p>
58
+ <p>
59
+ Include rules: <textarea disabled rows="2" cols="20" name="include"><% if opts['include']%><%=opts['include'].join( "\r\n" )%><%end%></textarea>
60
+ <br/>(Newline separated)
61
+ </p>
62
+ <p>
63
+ Redundant rules: <textarea disabled rows="2" cols="20" name="redundant"><%=format_redundants( opts['redundant'] )%></textarea>
64
+ <br/>(Newline separated)
65
+ <br/>(<em>regexp:counter</em>)
66
+ </p>
67
+ <p>
68
+ Depth: <input disabled name="depth_limit" value="<%=opts['depth_limit']%>"/>
69
+ <br/>(Default: infinite)
70
+ </p>
71
+ <p>
72
+ Link count limit: <input disabled name="link_count_limit" value="<%=opts['link_count_limit']%>"/>
73
+ <br/>(Default: infinite)
74
+ </p>
75
+ <p>
76
+ Redirect limit: <input disabled name="redirect_limit" value="<%=opts['redirect_limit']%>"/>
77
+ <br/>(Default: infinite)
78
+ </p>
79
+ <p>
80
+ Follow subdomain: <input disabled type="checkbox" name="follow_subdomains" <% if opts['follow_subdomains'] == true %> checked="checked" <% end %> />
81
+ </p>
82
+ <p>
83
+ Obey robot.txt file: <input disabled type="checkbox" name="obey_robots_txt" <% if opts['obey_robots_txt'] == true %> checked="checked" <% end %> />
84
+ </p>
85
+ <p>
86
+ Spider first: <input disabled type="checkbox" name="spider_first" <% if opts['spider_first'] == true %> checked="checked" <% end %> />
87
+ </p>
88
+ </fieldset>
89
+
90
+ </div>
91
+
92
+ </div>
93
+
@@ -15,9 +15,7 @@ module UI
15
15
  module Web
16
16
 
17
17
  #
18
- #
19
- # Provides nice little wrapper for the Arachni::Report::Manager while also handling<br/>
20
- # conversions, storing etc.
18
+ # Provides methods for dispatcher management.
21
19
  #
22
20
  # @author: Tasos "Zapotek" Laskos
23
21
  # <tasos.laskos@gmail.com>
@@ -44,10 +42,23 @@ class DispatcherManager
44
42
  Dispatcher.auto_upgrade!
45
43
  end
46
44
 
47
- def new( opts )
48
- Dispatcher.create( :url => opts[:url] )
45
+ #
46
+ # Puts a new dispatcher in the DB.
47
+ #
48
+ # @param [String] url URL of the dispatcher
49
+ #
50
+ def new( url )
51
+ Dispatcher.first_or_create( :url => url )
49
52
  end
50
53
 
54
+ #
55
+ # Provides an easy way to connect to a dispatcher and caches connections
56
+ # to reduce overhead.
57
+ #
58
+ # @param [String] url
59
+ #
60
+ # @return [Arachni::RPC::XML::Client::Dispatcher]
61
+ #
51
62
  def connect( url )
52
63
  @@cache ||= {}
53
64
 
@@ -59,20 +70,68 @@ class DispatcherManager
59
70
  return @@cache[url] = tmp
60
71
  end
61
72
  rescue Exception => e
62
- return nil
73
+ # ap e
74
+ # ap e.backtrace
75
+ return nil
63
76
  end
64
77
  end
65
78
 
66
- def alive?( url )
67
- begin
68
- return connect( url ).alive?
69
- rescue
70
- return false
71
- end
79
+ #
80
+ # Checks wether the dispatcher is alive.
81
+ #
82
+ # @param [String] url URL of the dispatcher
83
+ #
84
+ def alive?( url, tries = 5 )
85
+ tries.times {
86
+ begin
87
+ return connect( url ).alive?
88
+ rescue Exception => e
89
+ # ap e
90
+ # ap e.backtrace
91
+ end
92
+ }
93
+
94
+ return false
95
+ end
96
+
97
+ def first_alive
98
+ all.each {
99
+ |dispatcher|
100
+ return dispatcher if alive?( dispatcher.url )
101
+ }
102
+
103
+ return nil
72
104
  end
73
105
 
74
106
  #
75
- # Returns the paths of all saved report files as an array
107
+ # Provides statistics about running jobs etc using the dispatcher
108
+ #
109
+ # @return [Hash]
110
+ #
111
+ def stats
112
+ stats_h = {}
113
+ all.each {
114
+ |dispatcher|
115
+
116
+ begin
117
+ stats_h[dispatcher['url']] = connect( dispatcher['url'] ).stats
118
+ stats_h[dispatcher['url']]['running_jobs'].each {
119
+ |job|
120
+ begin
121
+ instance = @settings.instances.port_to_url( job['port'], dispatcher['url'] )
122
+ job['paused'] = @settings.instances.connect( instance ).framework.paused?
123
+ rescue
124
+ end
125
+ }
126
+ rescue
127
+ end
128
+ }
129
+
130
+ return stats_h
131
+ end
132
+
133
+ #
134
+ # Returns all dispatchers stored in the DB.
76
135
  #
77
136
  # @return [Array]
78
137
  #
@@ -80,6 +139,9 @@ class DispatcherManager
80
139
  Dispatcher.all( *args )
81
140
  end
82
141
 
142
+ #
143
+ # Removed all dispatchers from the DB.
144
+ #
83
145
  def delete_all
84
146
  all.each {
85
147
  |report|
@@ -88,6 +150,11 @@ class DispatcherManager
88
150
  all.destroy
89
151
  end
90
152
 
153
+ #
154
+ # Removed a dispatcher from the DB.
155
+ #
156
+ # @param [Integer] id
157
+ #
91
158
  def delete( id )
92
159
  Dispatcher.get( id ).destroy
93
160
  end
@@ -0,0 +1,87 @@
1
+ =begin
2
+ Arachni
3
+ Copyright (c) 2010-2011 Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
4
+
5
+ This is free software; you can copy and distribute and modify
6
+ this program under the term of the GPL v2.0 License
7
+ (See LICENSE file for details)
8
+
9
+ =end
10
+
11
+ require Arachni::Options.instance.dir['lib'] + 'rpc/xml/client/instance'
12
+ require Arachni::Options.instance.dir['lib'] + 'ui/web/utilities'
13
+
14
+ module Arachni
15
+ module UI
16
+ module Web
17
+
18
+ #
19
+ # Provides methods for instance management.
20
+ #
21
+ # @author: Tasos "Zapotek" Laskos
22
+ # <tasos.laskos@gmail.com>
23
+ # <zapotek@segfault.gr>
24
+ # @version: 0.1
25
+ #
26
+ class InstanceManager
27
+
28
+ include Utilities
29
+
30
+ def initialize( opts, settings )
31
+ @opts = opts
32
+ @settings = settings
33
+ end
34
+
35
+ #
36
+ # Provides an easy way to connect to an instance and caches connections
37
+ # to reduce overhead.
38
+ #
39
+ # @param [String] url
40
+ # @param [Hash] session session of the current user (optional)
41
+ # @param [String] token authentication token (optional)
42
+ #
43
+ # @return [Arachni::RPC::XML::Client::Instance]
44
+ #
45
+ def connect( url, session = nil, token = nil )
46
+ url = 'https://' + url if !url.include?( 'https' )
47
+
48
+ @@connections ||= {}
49
+
50
+ begin
51
+ if @@connections[url] && @@connections[url].alive?
52
+ return @@connections[url]
53
+ end
54
+ rescue
55
+ end
56
+
57
+ begin
58
+
59
+ #
60
+ # Sync up the session authentication tokens with the ones in the
61
+ # class variables.
62
+ #
63
+ # This will allow users to still connect to instances even if they
64
+ # shutdown the WebUI or removed their cookies.
65
+ #
66
+
67
+ @@tokens ||= {}
68
+ session['tokens'] ||= {} if session
69
+ @@tokens[url] = token if token
70
+
71
+ session['tokens'].merge!( @@tokens ) if session
72
+ @@tokens.merge!( session['tokens'] ) if session
73
+ session['tokens'].merge!( @@tokens ) if session
74
+
75
+ tmp_token = session ? session['tokens'][url] : @@tokens[url]
76
+
77
+ return @@connections[url] =
78
+ Arachni::RPC::XML::Client::Instance.new( @opts, url, tmp_token )
79
+ rescue Exception => e
80
+ raise "Instance at #{url} has shutdown."
81
+ end
82
+ end
83
+
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,166 @@
1
+ =begin
2
+ Arachni
3
+ Copyright (c) 2010-2011 Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
4
+
5
+ This is free software; you can copy and distribute and modify
6
+ this program under the term of the GPL v2.0 License
7
+ (See LICENSE file for details)
8
+
9
+ =end
10
+
11
+ require 'datamapper'
12
+ require Arachni::Options.instance.dir['lib'] + 'rpc/xml/client/dispatcher'
13
+ require Arachni::Options.instance.dir['lib'] + 'ui/web/utilities'
14
+
15
+ module Arachni
16
+ module UI
17
+ module Web
18
+
19
+ #
20
+ # Schedules and executes scan jobs.
21
+ #
22
+ #
23
+ # @author: Tasos "Zapotek" Laskos
24
+ # <tasos.laskos@gmail.com>
25
+ # <zapotek@segfault.gr>
26
+ # @version: 0.1.1
27
+ #
28
+ class Scheduler
29
+
30
+ include Utilities
31
+
32
+ class Job
33
+ include DataMapper::Resource
34
+
35
+ property :id, Serial
36
+ property :dispatcher, String
37
+ property :url, String
38
+ property :opts, Text
39
+ property :datetime, DateTime
40
+
41
+ property :owner_addr, String
42
+ property :owner_host, String
43
+
44
+ property :created_at, DateTime
45
+ end
46
+
47
+ #
48
+ # Initializes the Scheduler and starts the clock.
49
+ #
50
+ #
51
+ def initialize( opts, settings )
52
+ @opts = opts
53
+ @settings = settings
54
+
55
+ DataMapper::setup( :default, "sqlite3://#{@settings.db}/default.db" )
56
+ DataMapper.finalize
57
+
58
+ Job.auto_upgrade!
59
+
60
+ begin
61
+ ticktock!
62
+ rescue Exception => e
63
+ ap e
64
+ ap e.backtrace
65
+ end
66
+ end
67
+
68
+ #
69
+ # Runs a job.
70
+ #
71
+ # @param [Job] job
72
+ # @param [Hash] Sinatra environment
73
+ # @param [Hash] Rack session
74
+ #
75
+ # @return [String] URL of the laucnhed scanner instance
76
+ #
77
+ def run( job, env = nil, session = nil )
78
+ instance = @settings.dispatchers.connect( job.dispatcher ).dispatch( job.url )
79
+ instance_url = @settings.instances.port_to_url( instance['port'], job.dispatcher )
80
+
81
+ env = {
82
+ 'REMOTE_ADDR' => job.owner_addr,
83
+ 'REMOTE_HOST' => job.owner_host
84
+ } if env.nil?
85
+
86
+ @settings.log.scheduler_instance_dispatched( env, instance_url )
87
+ @settings.log.scheduler_instance_owner_assigned( env, job.url )
88
+
89
+ arachni = @settings.instances.connect( instance_url, session, instance['token'] )
90
+
91
+ opts = YAML::load( job.opts )
92
+
93
+ arachni.opts.set( opts['settings'] )
94
+ arachni.modules.load( opts['modules'] )
95
+ arachni.plugins.load( opts['plugins'] )
96
+
97
+ arachni.framework.run
98
+
99
+ @settings.log.scheduler_scan_started( env, job.url )
100
+
101
+ return instance_url
102
+ end
103
+
104
+ #
105
+ # Runs a job and removed it from the DB.
106
+ #
107
+ # @param [Job] job
108
+ #
109
+ def run_and_destroy( job )
110
+ run( job )
111
+ job.destroy
112
+ end
113
+
114
+ #
115
+ # Returns all scheduled jobs.
116
+ #
117
+ # @return [Array]
118
+ #
119
+ def jobs( *args )
120
+ Job.all( *args )
121
+ end
122
+
123
+ #
124
+ # Removes all jobs.
125
+ #
126
+ def delete_all
127
+ jobs.destroy
128
+ end
129
+
130
+ #
131
+ # Removed a job.
132
+ #
133
+ # @param [Integer] id
134
+ #
135
+ def delete( id )
136
+ Job.get( id ).destroy
137
+ end
138
+
139
+
140
+ private
141
+
142
+ def ticktock!
143
+ @reaper ||= Thread.new {
144
+ while( true )
145
+ jobs.each {
146
+ |job|
147
+
148
+ begin
149
+ run_and_destroy( job ) if job.datetime <= Time.now
150
+ rescue Exception => e
151
+ ap e
152
+ ap e.backtrace
153
+ end
154
+
155
+ }
156
+
157
+ ::IO::select( nil, nil, nil, 5 )
158
+ end
159
+ }
160
+
161
+ end
162
+
163
+ end
164
+ end
165
+ end
166
+ end