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