arachni 0.2.4 → 0.3

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 (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,172 @@
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
+ module Arachni
12
+ module UI
13
+ module Web
14
+ module Addons
15
+
16
+ #
17
+ #
18
+ # Auto-deploy add-on.
19
+ #
20
+ # Allows users to automatically convert any SSH enabled Linux box into an Arachni Dispatcher.
21
+ #
22
+ # @author: Tasos "Zapotek" Laskos
23
+ # <tasos.laskos@gmail.com>
24
+ # <zapotek@segfault.gr>
25
+ #
26
+ # @version: 0.1
27
+ #
28
+ class AutoDeploy < Base
29
+
30
+ def run
31
+
32
+ settings.helpers do
33
+ require File.dirname( __FILE__ ) + '/autodeploy/lib/manager'
34
+
35
+ def autodeploy
36
+ @@autodeploy ||= Manager.new( Options.instance, settings )
37
+ end
38
+
39
+ end
40
+
41
+ get "/" do
42
+ present :index, :deployments => autodeploy.list,
43
+ :root => current_addon.path_root, :show_output => false, :ret => {}
44
+
45
+ end
46
+
47
+ post "/" do
48
+
49
+ if !params[:host] || params[:host].empty? || !params[:username] ||
50
+ params[:username].empty? || !params[:password] || params[:password].empty? ||
51
+ !params[:port] || params[:port].empty? ||
52
+ !params[:dispatcher_port] || params[:dispatcher_port].empty?
53
+
54
+ flash[:err] = "Please fill in all the fields."
55
+
56
+ present :index, :deployments => autodeploy.list,
57
+ :root => current_addon.path_root, :show_output => false,
58
+ :ret => {}
59
+ else
60
+ deployment = Manager::Deployment.new( :host => params[:host],
61
+ :port => params[:port], :user => params[:username],
62
+ :dispatcher_port => params[:dispatcher_port] )
63
+
64
+ settings.log.autodeploy_setup_started( env, autodeploy.get_url( deployment ) )
65
+ channel = autodeploy.setup( deployment, params[:password] )
66
+
67
+ present :index, :deployments => autodeploy.list,
68
+ :root => current_addon.path_root, :channel => channel,
69
+ :show_output => true, :ret => {}
70
+ end
71
+
72
+ end
73
+
74
+ get '/channel/:channel' do
75
+ content_type :json
76
+ autodeploy.output( params[:channel] ).to_json
77
+ end
78
+
79
+ get '/channel/:channel/finalize' do
80
+
81
+ deployment = autodeploy.finalize_setup( params[:channel] )
82
+ log.autodeploy_deployment_saved( env,
83
+ "ID: #{deployment.id} [#{autodeploy.get_url( deployment )}]" )
84
+
85
+ flash[:ok] = "Deployment was successful."
86
+
87
+ present :index, :deployments => autodeploy.list, :ret => {},
88
+ :root => current_addon.path_root, :show_output => false
89
+ end
90
+
91
+
92
+ post '/:id' do
93
+
94
+ ret = {}
95
+ if !params[:password] || params[:password].empty?
96
+ flash[:err] = "The password field is required."
97
+ else
98
+ if params[:action] == 'delete'
99
+
100
+ ret = autodeploy.delete( params[:id], params[:password] )
101
+
102
+ if ret[:code]
103
+ flash[:err] = "Uninstall process aborted because the last command failed.<br/>" +
104
+ " Please ensure that the password is correct and the network is up."
105
+ else
106
+ log.autodeploy_deployment_deleted( env, params[:id] )
107
+ flash[:ok] = "Uninstall process was successful."
108
+ end
109
+
110
+ elsif params[:action] == 'run'
111
+ deployment = autodeploy.get( params[:id] )
112
+ ret = autodeploy.run( deployment, params[:password] )
113
+
114
+ url = 'https://' + deployment.host + ':' + deployment.dispatcher_port
115
+
116
+ if settings.dispatchers.alive?( url )
117
+ flash[:ok] = "Dispatcher is up and running."
118
+ DispatcherManager::Dispatcher.first_or_create( :url => url )
119
+ settings.log.autodeploy_dispatcher_enabled( env,
120
+ "ID: #{deployment.id} [#{autodeploy.get_url( deployment )}]" )
121
+
122
+ ret = {}
123
+ else
124
+ flash[:err] = "Could not run the Dispatcher.<br/>" +
125
+ " Please ensure that the password is correct and the network is up."
126
+ end
127
+ elsif params[:action] == 'shutdown'
128
+ deployment = autodeploy.get( params[:id] )
129
+ ret = autodeploy.shutdown( deployment, params[:password] )
130
+
131
+ if ret[:code] == 0 && !settings.dispatchers.alive?( url )
132
+ flash[:ok] = "Dispatcher has been shutdown."
133
+ settings.log.autodeploy_dispatcher_shutdown( env,
134
+ "ID: #{deployment.id} [#{autodeploy.get_url( deployment )}]" )
135
+
136
+ ret = {}
137
+ else
138
+ flash[:err] = "Could not shutdown the Dispatcher.<br/>" +
139
+ " Please ensure that the password is correct and the network is up."
140
+ end
141
+
142
+
143
+ end
144
+ end
145
+
146
+ present :index, :deployments => autodeploy.list,
147
+ :root => current_addon.path_root, :ret => ret, :show_output => false
148
+ end
149
+
150
+
151
+ end
152
+
153
+ def title
154
+ "Auto-deploy [#{Manager.new( Options.instance, settings ).list.size}]"
155
+ end
156
+
157
+ def self.info
158
+ {
159
+ :name => 'Auto-deploy',
160
+ :description => %q{Enables you to automatically convert any SSH enabled Linux box into an Arachni Dispatcher.},
161
+ :author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com> ',
162
+ :version => '0.1'
163
+ }
164
+ end
165
+
166
+
167
+ end
168
+
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,291 @@
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 'net/ssh'
13
+ require 'digest/md5'
14
+
15
+
16
+ module Arachni
17
+ module UI
18
+ module Web
19
+ module Addons
20
+
21
+ class AutoDeploy
22
+
23
+ #
24
+ #
25
+ # @author: Tasos "Zapotek" Laskos
26
+ # <tasos.laskos@gmail.com>
27
+ # <zapotek@segfault.gr>
28
+ # @version: 0.1
29
+ #
30
+ class Manager
31
+
32
+ include Utilities
33
+
34
+ ARCHIVE_PATH = 'https://github.com/downloads/Zapotek/arachni/'
35
+ ARCHIVE_NAME = 'arachni-v0.3-autodeploy'
36
+ ARCHIVE_EXT = '.tar.gz'
37
+
38
+ EXEC = 'arachni_xmlrpcd'
39
+
40
+ class Deployment
41
+ include DataMapper::Resource
42
+
43
+ property :id, Serial
44
+ property :host, String
45
+ property :port, String
46
+ property :dispatcher_port, String
47
+ property :user, String
48
+ property :created_at, DateTime, :default => Time.now
49
+ end
50
+
51
+ #
52
+ # Initializes the Scheduler and starts the clock.
53
+ #
54
+ #
55
+ def initialize( opts, settings )
56
+ @opts = opts
57
+ @settings = settings
58
+
59
+ DataMapper::setup( :default, "sqlite3://#{@settings.db}/default.db" )
60
+ DataMapper.finalize
61
+
62
+ Deployment.auto_upgrade!
63
+ end
64
+
65
+ def setup( deployment, password )
66
+
67
+ @@setup ||= {}
68
+ url = get_url( deployment )
69
+ @@setup[url] ||= {}
70
+
71
+ Thread.new {
72
+ @@setup[url][:deployment] ||= deployment
73
+ @@setup[url][:status] = 'working'
74
+
75
+ begin
76
+ session = ssh( deployment, password )
77
+ rescue Exception => e
78
+ @@setup[url][:status] = 'failed'
79
+ @@setup[url][:output] = e.class.name + ': ' + e.to_s + "\n" + e.backtrace.join( "\n" )
80
+ @@setup[url][:code] = 1
81
+ return
82
+ end
83
+
84
+
85
+ wget = 'wget --output-document=' + ARCHIVE_NAME + '-' + deployment.dispatcher_port +
86
+ ARCHIVE_EXT + ' ' + ARCHIVE_PATH + ARCHIVE_NAME + ARCHIVE_EXT
87
+ ret = ssh_exec!( deployment, session, wget )
88
+
89
+ if ret[:code] != 0
90
+ @@setup[url][:status] = 'failed'
91
+ return
92
+ end
93
+
94
+ mkdir = 'mkdir ' + ARCHIVE_NAME + '-' + deployment.dispatcher_port
95
+ ret = ssh_exec!( deployment, session, mkdir )
96
+
97
+ if ret[:code] != 0
98
+ @@setup[url][:status] = 'failed'
99
+ return
100
+ end
101
+
102
+
103
+ tar = 'tar xvf ' + ARCHIVE_NAME + '-' + deployment.dispatcher_port + ARCHIVE_EXT +
104
+ ' -C ' + ARCHIVE_NAME + '-' + deployment.dispatcher_port
105
+ ret = ssh_exec!( deployment, session, tar )
106
+
107
+ if ret[:code] != 0
108
+ @@setup[url][:status] = 'failed'
109
+ return
110
+ end
111
+
112
+
113
+ chmod = 'chmod +x ' + ARCHIVE_NAME + '-' + deployment.dispatcher_port + '/' +
114
+ ARCHIVE_NAME + '/' + EXEC
115
+ ret = ssh_exec!( deployment, session, chmod )
116
+
117
+ if ret[:code] != 0
118
+ @@setup[url][:status] = 'failed'
119
+ return
120
+ end
121
+
122
+ @@setup[url][:status] = 'finished'
123
+ }
124
+
125
+ return get_url( deployment )
126
+ end
127
+
128
+ def output( channel )
129
+ return @@setup[channel]
130
+ end
131
+
132
+ def finalize_setup( channel )
133
+ @@setup[channel][:deployment].save
134
+ return @@setup[channel][:deployment]
135
+ end
136
+
137
+ def uninstall( deployment, password )
138
+
139
+ begin
140
+ session = ssh( deployment, password )
141
+ rescue Exception => e
142
+ return {
143
+ :output => e.class.name + ': ' + e.to_s + "\n" + e.backtrace.join( "\n" ),
144
+ :status => 'failed',
145
+ :code => 1
146
+ }
147
+ end
148
+
149
+ out = "\n" + rm = "rm -rf #{ARCHIVE_NAME}-#{deployment.dispatcher_port}*"
150
+ ret = ssh_exec!( deployment, session, rm )
151
+ out += "\n" + ret[:stdout] + "\n" + ret[:stderr]
152
+
153
+ return { :output => out, :code => ret[:code], :status => 'failed', } if ret[:code] != 0
154
+
155
+ return { :output => out }
156
+ end
157
+
158
+ def run( deployment, password )
159
+ begin
160
+ session = ssh( deployment, password )
161
+ rescue Exception => e
162
+ return {
163
+ :output => e.class.name + ': ' + e.to_s + "\n" + e.backtrace.join( "\n" ),
164
+ :status => 'failed',
165
+ :code => 1
166
+ }
167
+ end
168
+
169
+ session.exec!( 'nohup ./' + ARCHIVE_NAME + '-' + deployment.dispatcher_port + '/' +
170
+ ARCHIVE_NAME + '/' + EXEC + ' --port=' + deployment.dispatcher_port +
171
+ ' > arachni-xmlrpcd-startup.log 2>&1 &' )
172
+
173
+ sleep( 5 )
174
+ { :code => 0 }
175
+ end
176
+
177
+ def shutdown( deployment, password )
178
+
179
+ url = "https://#{deployment.host}:#{deployment.dispatcher_port}"
180
+ proc = @settings.dispatchers.connect( url ).proc_info
181
+
182
+ begin
183
+ session = ssh( deployment, password )
184
+ rescue Exception => e
185
+ return {
186
+ :output => e.class.name + ': ' + e.to_s + "\n" + e.backtrace.join( "\n" ),
187
+ :status => 'failed',
188
+ :code => 1
189
+ }
190
+ end
191
+
192
+ ssh_exec!( deployment, session, 'kill -9 -' + proc['pgrp'] )
193
+ end
194
+
195
+
196
+ def list
197
+ Deployment.all.reverse
198
+ end
199
+
200
+ def get( id )
201
+ Deployment.get( id )
202
+ end
203
+
204
+ def delete( id, password )
205
+ deployment = get( id )
206
+ ret = uninstall( deployment, password )
207
+ return ret if ret[:code]
208
+ deployment.destroy
209
+ return ret
210
+ end
211
+
212
+ def ssh( deployment, password )
213
+ @@ssh ||= {}
214
+ @@ssh[get_url( deployment ) + '$' + Digest::MD5.hexdigest( password ) ] ||=
215
+ Net::SSH.start( deployment.host, deployment.user,
216
+ {
217
+ :port => deployment.port,
218
+ :password => password
219
+ }
220
+ )
221
+ end
222
+
223
+ def get_url( deployment )
224
+ deployment.user + '@' + deployment.host + ':' + deployment.port.to_s +
225
+ '$' + deployment.dispatcher_port.to_s
226
+ end
227
+
228
+ def ssh_exec!( deployment, ssh, command )
229
+
230
+ stdout_data = ""
231
+ stderr_data = ""
232
+
233
+ exit_code = nil
234
+ exit_signal = nil
235
+
236
+ @@setup ||= {}
237
+
238
+ url = get_url( deployment )
239
+
240
+ @@setup[url] ||= {}
241
+ @@setup[url][:code] = 0
242
+ @@setup[url][:output] ||= ''
243
+ @@setup[url][:output] += "\n" + command + "\n"
244
+
245
+ ssh.open_channel do |channel|
246
+ channel.exec(command) do |ch, success|
247
+ unless success
248
+ abort "FAILED: couldn't execute command (ssh.channel.exec)"
249
+ end
250
+
251
+ channel.on_data {
252
+ |ch, data|
253
+ stdout_data += data
254
+ @@setup[url][:output] += data
255
+ }
256
+
257
+ channel.on_extended_data {
258
+ |ch, type, data|
259
+ stderr_data += data
260
+ @@setup[url][:output] += data
261
+ }
262
+
263
+ channel.on_request( "exit-status" ) {
264
+ |ch, data|
265
+ exit_code = data.read_long
266
+ @@setup[url][:code] = data.read_long
267
+ }
268
+
269
+ channel.on_request( "exit-signal" ) {
270
+ |ch, data|
271
+ exit_signal = data.read_long
272
+ }
273
+
274
+ end
275
+ end
276
+
277
+ ssh.loop
278
+ return {
279
+ :stdout => stdout_data,
280
+ :stderr => stderr_data,
281
+ :code => exit_code,
282
+ :signal => exit_signal
283
+ }
284
+ end
285
+
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end
291
+ end