arachni 0.2.4 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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,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
|
-
|
48
|
-
|
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
|
-
|
73
|
+
# ap e
|
74
|
+
# ap e.backtrace
|
75
|
+
return nil
|
63
76
|
end
|
64
77
|
end
|
65
78
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
#
|
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
|