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.
- data/CHANGELOG.md +33 -0
- data/README.md +2 -4
- data/Rakefile +15 -4
- data/bin/arachni +0 -0
- data/bin/arachni_web +0 -0
- data/bin/arachni_web_autostart +0 -0
- data/bin/arachni_xmlrpc +0 -0
- data/bin/arachni_xmlrpcd +0 -0
- data/bin/arachni_xmlrpcd_monitor +0 -0
- data/lib/arachni.rb +1 -1
- data/lib/framework.rb +36 -6
- data/lib/http.rb +12 -5
- data/lib/module/auditor.rb +482 -59
- data/lib/module/base.rb +17 -0
- data/lib/module/manager.rb +26 -2
- data/lib/module/trainer.rb +1 -12
- data/lib/module/utilities.rb +12 -0
- data/lib/parser/auditable.rb +8 -3
- data/lib/parser/elements.rb +11 -0
- data/lib/parser/page.rb +3 -1
- data/lib/parser/parser.rb +130 -18
- data/lib/rpc/xml/server/dispatcher.rb +21 -0
- data/lib/spider.rb +141 -82
- data/lib/ui/cli/cli.rb +2 -3
- data/lib/ui/web/addon_manager.rb +273 -0
- data/lib/ui/web/addons/autodeploy.rb +172 -0
- data/lib/ui/web/addons/autodeploy/lib/manager.rb +291 -0
- data/lib/ui/web/addons/autodeploy/views/index.erb +124 -0
- data/lib/ui/web/addons/sample.rb +78 -0
- data/lib/ui/web/addons/sample/views/index.erb +4 -0
- data/lib/ui/web/addons/scheduler.rb +139 -0
- data/lib/ui/web/addons/scheduler/views/index.erb +131 -0
- data/lib/ui/web/addons/scheduler/views/options.erb +93 -0
- data/lib/ui/web/dispatcher_manager.rb +80 -13
- data/lib/ui/web/instance_manager.rb +87 -0
- data/lib/ui/web/scheduler.rb +166 -0
- data/lib/ui/web/server.rb +142 -202
- data/lib/ui/web/server/public/js/jquery-ui-timepicker.js +985 -0
- data/lib/ui/web/server/public/plugins/sample/style.css +0 -0
- data/lib/ui/web/server/public/style.css +42 -0
- data/lib/ui/web/server/views/addon.erb +15 -0
- data/lib/ui/web/server/views/addons.erb +46 -0
- data/lib/ui/web/server/views/dispatchers.erb +1 -1
- data/lib/ui/web/server/views/instance.erb +9 -11
- data/lib/ui/web/server/views/layout.erb +14 -1
- data/lib/ui/web/server/views/welcome.erb +7 -6
- data/lib/ui/web/utilities.rb +134 -0
- data/modules/audit/code_injection_timing.rb +6 -2
- data/modules/audit/code_injection_timing/payloads.txt +2 -2
- data/modules/audit/os_cmd_injection_timing.rb +7 -3
- data/modules/audit/os_cmd_injection_timing/payloads.txt +1 -1
- data/modules/audit/sqli_blind_rdiff.rb +18 -233
- data/modules/audit/sqli_blind_rdiff/payloads.txt +5 -0
- data/modules/audit/sqli_blind_timing.rb +9 -2
- data/path_extractors/anchors.rb +1 -1
- data/path_extractors/forms.rb +1 -1
- data/path_extractors/frames.rb +1 -1
- data/path_extractors/generic.rb +1 -1
- data/path_extractors/links.rb +1 -1
- data/path_extractors/meta_refresh.rb +1 -1
- data/path_extractors/scripts.rb +1 -1
- data/path_extractors/sitemap.rb +1 -1
- data/plugins/proxy/server.rb +3 -2
- data/plugins/waf_detector.rb +0 -3
- metadata +37 -34
- data/lib/anemone/cookie_store.rb +0 -35
- data/lib/anemone/core.rb +0 -371
- data/lib/anemone/exceptions.rb +0 -5
- data/lib/anemone/http.rb +0 -144
- data/lib/anemone/page.rb +0 -338
- data/lib/anemone/page_store.rb +0 -160
- data/lib/anemone/storage.rb +0 -34
- data/lib/anemone/storage/base.rb +0 -75
- data/lib/anemone/storage/exceptions.rb +0 -15
- data/lib/anemone/storage/mongodb.rb +0 -89
- data/lib/anemone/storage/pstore.rb +0 -50
- data/lib/anemone/storage/redis.rb +0 -90
- data/lib/anemone/storage/tokyo_cabinet.rb +0 -57
- 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
|