arachni 0.2.2.1
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/ACKNOWLEDGMENTS.md +14 -0
- data/AUTHORS.md +6 -0
- data/CHANGELOG.md +162 -0
- data/CONTRIBUTORS.md +10 -0
- data/EXPLOITATION.md +429 -0
- data/HACKING.md +101 -0
- data/LICENSE.md +341 -0
- data/README.md +350 -0
- data/Rakefile +86 -0
- data/bin/arachni +22 -0
- data/bin/arachni_web +77 -0
- data/bin/arachni_xmlrpc +21 -0
- data/bin/arachni_xmlrpcd +82 -0
- data/bin/arachni_xmlrpcd_monitor +74 -0
- data/conf/README.webui.yaml.txt +44 -0
- data/conf/webui.yaml +11 -0
- data/external/metasploit/LICENSE +24 -0
- data/external/metasploit/modules/exploits/unix/webapp/arachni_exec.rb +142 -0
- data/external/metasploit/modules/exploits/unix/webapp/arachni_path_traversal.rb +113 -0
- data/external/metasploit/modules/exploits/unix/webapp/arachni_php_eval.rb +150 -0
- data/external/metasploit/modules/exploits/unix/webapp/arachni_php_include.rb +141 -0
- data/external/metasploit/modules/exploits/unix/webapp/arachni_sqlmap.rb +92 -0
- data/external/metasploit/plugins/arachni.rb +536 -0
- data/getoptslong.rb +241 -0
- data/lib/anemone.rb +2 -0
- data/lib/anemone/cookie_store.rb +35 -0
- data/lib/anemone/core.rb +371 -0
- data/lib/anemone/exceptions.rb +5 -0
- data/lib/anemone/http.rb +144 -0
- data/lib/anemone/page.rb +337 -0
- data/lib/anemone/page_store.rb +160 -0
- data/lib/anemone/storage.rb +34 -0
- data/lib/anemone/storage/base.rb +75 -0
- data/lib/anemone/storage/exceptions.rb +15 -0
- data/lib/anemone/storage/mongodb.rb +89 -0
- data/lib/anemone/storage/pstore.rb +50 -0
- data/lib/anemone/storage/redis.rb +90 -0
- data/lib/anemone/storage/tokyo_cabinet.rb +57 -0
- data/lib/anemone/tentacle.rb +40 -0
- data/lib/arachni.rb +16 -0
- data/lib/audit_store.rb +346 -0
- data/lib/component_manager.rb +293 -0
- data/lib/component_options.rb +395 -0
- data/lib/exceptions.rb +76 -0
- data/lib/framework.rb +637 -0
- data/lib/http.rb +809 -0
- data/lib/issue.rb +302 -0
- data/lib/module.rb +4 -0
- data/lib/module/auditor.rb +455 -0
- data/lib/module/base.rb +188 -0
- data/lib/module/element_db.rb +158 -0
- data/lib/module/key_filler.rb +87 -0
- data/lib/module/manager.rb +87 -0
- data/lib/module/output.rb +68 -0
- data/lib/module/trainer.rb +240 -0
- data/lib/module/utilities.rb +110 -0
- data/lib/options.rb +547 -0
- data/lib/parser.rb +2 -0
- data/lib/parser/auditable.rb +522 -0
- data/lib/parser/elements.rb +296 -0
- data/lib/parser/page.rb +149 -0
- data/lib/parser/parser.rb +717 -0
- data/lib/plugin.rb +4 -0
- data/lib/plugin/base.rb +110 -0
- data/lib/plugin/manager.rb +162 -0
- data/lib/report.rb +4 -0
- data/lib/report/base.rb +119 -0
- data/lib/report/manager.rb +92 -0
- data/lib/rpc/xml/client/base.rb +71 -0
- data/lib/rpc/xml/client/dispatcher.rb +49 -0
- data/lib/rpc/xml/client/instance.rb +88 -0
- data/lib/rpc/xml/server/base.rb +90 -0
- data/lib/rpc/xml/server/dispatcher.rb +357 -0
- data/lib/rpc/xml/server/framework.rb +206 -0
- data/lib/rpc/xml/server/instance.rb +191 -0
- data/lib/rpc/xml/server/module/manager.rb +46 -0
- data/lib/rpc/xml/server/options.rb +124 -0
- data/lib/rpc/xml/server/output.rb +299 -0
- data/lib/rpc/xml/server/plugin/manager.rb +58 -0
- data/lib/ruby.rb +5 -0
- data/lib/ruby/object.rb +32 -0
- data/lib/ruby/string.rb +74 -0
- data/lib/ruby/xmlrpc/server.rb +27 -0
- data/lib/spider.rb +200 -0
- data/lib/typhoeus/request.rb +91 -0
- data/lib/typhoeus/response.rb +34 -0
- data/lib/ui/cli/cli.rb +744 -0
- data/lib/ui/cli/output.rb +279 -0
- data/lib/ui/web/log.rb +82 -0
- data/lib/ui/web/output_stream.rb +94 -0
- data/lib/ui/web/report_manager.rb +222 -0
- data/lib/ui/web/server.rb +903 -0
- data/lib/ui/web/server/db/placeholder +0 -0
- data/lib/ui/web/server/public/banner.png +0 -0
- data/lib/ui/web/server/public/bodybg-small.png +0 -0
- data/lib/ui/web/server/public/bodybg.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/pbar-ani.gif +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/lib/ui/web/server/public/css/smoothness/jquery-ui-1.8.9.custom.css +573 -0
- data/lib/ui/web/server/public/favicon.ico +0 -0
- data/lib/ui/web/server/public/footer.jpg +0 -0
- data/lib/ui/web/server/public/icons/error.png +0 -0
- data/lib/ui/web/server/public/icons/info.png +0 -0
- data/lib/ui/web/server/public/icons/ok.png +0 -0
- data/lib/ui/web/server/public/icons/status.png +0 -0
- data/lib/ui/web/server/public/js/jquery-1.4.4.min.js +167 -0
- data/lib/ui/web/server/public/js/jquery-ui-1.8.9.custom.min.js +781 -0
- data/lib/ui/web/server/public/logo.png +0 -0
- data/lib/ui/web/server/public/nav-left.jpg +0 -0
- data/lib/ui/web/server/public/nav-right.jpg +0 -0
- data/lib/ui/web/server/public/nav-selected-left.jpg +0 -0
- data/lib/ui/web/server/public/nav-selected-right.jpg +0 -0
- data/lib/ui/web/server/public/reports/placeholder +1 -0
- data/lib/ui/web/server/public/sidebar-bottom.jpg +0 -0
- data/lib/ui/web/server/public/sidebar-h4.jpg +0 -0
- data/lib/ui/web/server/public/sidebar-top.jpg +0 -0
- data/lib/ui/web/server/public/spider.png +0 -0
- data/lib/ui/web/server/public/style.css +604 -0
- data/lib/ui/web/server/tmp/placeholder +0 -0
- data/lib/ui/web/server/views/dispatcher.erb +85 -0
- data/lib/ui/web/server/views/dispatcher_error.erb +14 -0
- data/lib/ui/web/server/views/error.erb +1 -0
- data/lib/ui/web/server/views/flash.erb +18 -0
- data/lib/ui/web/server/views/home.erb +14 -0
- data/lib/ui/web/server/views/instance.erb +213 -0
- data/lib/ui/web/server/views/layout.erb +95 -0
- data/lib/ui/web/server/views/log.erb +40 -0
- data/lib/ui/web/server/views/modules.erb +71 -0
- data/lib/ui/web/server/views/options.erb +23 -0
- data/lib/ui/web/server/views/output_results.erb +51 -0
- data/lib/ui/web/server/views/plugins.erb +42 -0
- data/lib/ui/web/server/views/report_formats.erb +30 -0
- data/lib/ui/web/server/views/reports.erb +55 -0
- data/lib/ui/web/server/views/settings.erb +120 -0
- data/lib/ui/web/server/views/welcome.erb +38 -0
- data/lib/ui/xmlrpc/dispatcher_monitor.rb +204 -0
- data/lib/ui/xmlrpc/xmlrpc.rb +843 -0
- data/logs/placeholder +0 -0
- data/metamodules/autothrottle.rb +74 -0
- data/metamodules/timeout_notice.rb +118 -0
- data/metamodules/uniformity.rb +98 -0
- data/modules/audit/code_injection.rb +136 -0
- data/modules/audit/code_injection_timing.rb +115 -0
- data/modules/audit/code_injection_timing/payloads.txt +4 -0
- data/modules/audit/csrf.rb +301 -0
- data/modules/audit/ldapi.rb +103 -0
- data/modules/audit/ldapi/errors.txt +26 -0
- data/modules/audit/os_cmd_injection.rb +103 -0
- data/modules/audit/os_cmd_injection/payloads.txt +2 -0
- data/modules/audit/os_cmd_injection_timing.rb +104 -0
- data/modules/audit/os_cmd_injection_timing/payloads.txt +3 -0
- data/modules/audit/path_traversal.rb +141 -0
- data/modules/audit/response_splitting.rb +105 -0
- data/modules/audit/rfi.rb +193 -0
- data/modules/audit/sqli.rb +120 -0
- data/modules/audit/sqli/regexp_ids.txt +90 -0
- data/modules/audit/sqli_blind_rdiff.rb +321 -0
- data/modules/audit/sqli_blind_timing.rb +103 -0
- data/modules/audit/sqli_blind_timing/payloads.txt +51 -0
- data/modules/audit/trainer.rb +89 -0
- data/modules/audit/unvalidated_redirect.rb +90 -0
- data/modules/audit/xpath.rb +104 -0
- data/modules/audit/xpath/errors.txt +26 -0
- data/modules/audit/xss.rb +99 -0
- data/modules/audit/xss_event.rb +134 -0
- data/modules/audit/xss_path.rb +125 -0
- data/modules/audit/xss_script_tag.rb +112 -0
- data/modules/audit/xss_tag.rb +112 -0
- data/modules/audit/xss_uri.rb +125 -0
- data/modules/recon/allowed_methods.rb +104 -0
- data/modules/recon/backdoors.rb +131 -0
- data/modules/recon/backdoors/filenames.txt +16 -0
- data/modules/recon/backup_files.rb +177 -0
- data/modules/recon/backup_files/extensions.txt +28 -0
- data/modules/recon/common_directories.rb +138 -0
- data/modules/recon/common_directories/directories.txt +265 -0
- data/modules/recon/common_files.rb +138 -0
- data/modules/recon/common_files/filenames.txt +17 -0
- data/modules/recon/directory_listing.rb +171 -0
- data/modules/recon/grep/captcha.rb +62 -0
- data/modules/recon/grep/credit_card.rb +85 -0
- data/modules/recon/grep/cvs_svn_users.rb +73 -0
- data/modules/recon/grep/emails.rb +59 -0
- data/modules/recon/grep/html_objects.rb +53 -0
- data/modules/recon/grep/private_ip.rb +54 -0
- data/modules/recon/grep/ssn.rb +53 -0
- data/modules/recon/htaccess_limit.rb +82 -0
- data/modules/recon/http_put.rb +95 -0
- data/modules/recon/interesting_responses.rb +118 -0
- data/modules/recon/unencrypted_password_forms.rb +119 -0
- data/modules/recon/webdav.rb +126 -0
- data/modules/recon/xst.rb +107 -0
- data/path_extractors/anchors.rb +35 -0
- data/path_extractors/forms.rb +35 -0
- data/path_extractors/frames.rb +38 -0
- data/path_extractors/generic.rb +39 -0
- data/path_extractors/links.rb +35 -0
- data/path_extractors/meta_refresh.rb +39 -0
- data/path_extractors/scripts.rb +37 -0
- data/path_extractors/sitemap.rb +31 -0
- data/plugins/autologin.rb +137 -0
- data/plugins/content_types.rb +90 -0
- data/plugins/cookie_collector.rb +99 -0
- data/plugins/form_dicattack.rb +185 -0
- data/plugins/healthmap.rb +94 -0
- data/plugins/http_dicattack.rb +133 -0
- data/plugins/metamodules.rb +118 -0
- data/plugins/proxy.rb +248 -0
- data/plugins/proxy/server.rb +66 -0
- data/plugins/waf_detector.rb +184 -0
- data/profiles/comprehensive.afp +74 -0
- data/profiles/full.afp +75 -0
- data/reports/afr.rb +59 -0
- data/reports/ap.rb +55 -0
- data/reports/html.rb +179 -0
- data/reports/html/default.erb +967 -0
- data/reports/metareport.rb +139 -0
- data/reports/metareport/arachni_metareport.rb +174 -0
- data/reports/plugin_formatters/html/content_types.rb +82 -0
- data/reports/plugin_formatters/html/cookie_collector.rb +66 -0
- data/reports/plugin_formatters/html/form_dicattack.rb +54 -0
- data/reports/plugin_formatters/html/healthmap.rb +76 -0
- data/reports/plugin_formatters/html/http_dicattack.rb +54 -0
- data/reports/plugin_formatters/html/metaformatters/timeout_notice.rb +65 -0
- data/reports/plugin_formatters/html/metaformatters/uniformity.rb +71 -0
- data/reports/plugin_formatters/html/metamodules.rb +93 -0
- data/reports/plugin_formatters/html/waf_detector.rb +54 -0
- data/reports/plugin_formatters/stdout/content_types.rb +73 -0
- data/reports/plugin_formatters/stdout/cookie_collector.rb +61 -0
- data/reports/plugin_formatters/stdout/form_dicattack.rb +52 -0
- data/reports/plugin_formatters/stdout/healthmap.rb +72 -0
- data/reports/plugin_formatters/stdout/http_dicattack.rb +53 -0
- data/reports/plugin_formatters/stdout/metaformatters/timeout_notice.rb +55 -0
- data/reports/plugin_formatters/stdout/metaformatters/uniformity.rb +68 -0
- data/reports/plugin_formatters/stdout/metamodules.rb +89 -0
- data/reports/plugin_formatters/stdout/waf_detector.rb +48 -0
- data/reports/plugin_formatters/xml/content_types.rb +91 -0
- data/reports/plugin_formatters/xml/cookie_collector.rb +70 -0
- data/reports/plugin_formatters/xml/form_dicattack.rb +57 -0
- data/reports/plugin_formatters/xml/healthmap.rb +82 -0
- data/reports/plugin_formatters/xml/http_dicattack.rb +57 -0
- data/reports/plugin_formatters/xml/metaformatters/timeout_notice.rb +67 -0
- data/reports/plugin_formatters/xml/metaformatters/uniformity.rb +82 -0
- data/reports/plugin_formatters/xml/metamodules.rb +91 -0
- data/reports/plugin_formatters/xml/waf_detector.rb +58 -0
- data/reports/stdout.rb +182 -0
- data/reports/txt.rb +77 -0
- data/reports/xml.rb +231 -0
- data/reports/xml/buffer.rb +98 -0
- metadata +516 -0
data/lib/exceptions.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
|
|
13
|
+
#
|
|
14
|
+
# Arachni::Exceptions module<br/>
|
|
15
|
+
# It holds the framework's exceptions.
|
|
16
|
+
#
|
|
17
|
+
# @author: Tasos "Zapotek" Laskos
|
|
18
|
+
# <tasos.laskos@gmail.com>
|
|
19
|
+
# <zapotek@segfault.gr>
|
|
20
|
+
# @version: 0.1
|
|
21
|
+
#
|
|
22
|
+
module Exceptions
|
|
23
|
+
|
|
24
|
+
def initialize( msg )
|
|
25
|
+
super( msg )
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class NoAuditOpts < StandardError
|
|
30
|
+
include Exceptions
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class NoMods < StandardError
|
|
35
|
+
include Exceptions
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class ComponentNotFound < StandardError
|
|
40
|
+
include Exceptions
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class ModNotFound < StandardError
|
|
45
|
+
include Exceptions
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class DepModNotFound < StandardError
|
|
50
|
+
include Exceptions
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class ReportNotFound < StandardError
|
|
55
|
+
include Exceptions
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class NoURL < StandardError
|
|
60
|
+
include Exceptions
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class InvalidURL < StandardError
|
|
65
|
+
include Exceptions
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class NoCookieJar < StandardError
|
|
70
|
+
include Exceptions
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
data/lib/framework.rb
ADDED
|
@@ -0,0 +1,637 @@
|
|
|
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
|
+
|
|
12
|
+
require 'rubygems'
|
|
13
|
+
|
|
14
|
+
require File.expand_path( File.dirname( __FILE__ ) ) + '/options'
|
|
15
|
+
opts = Arachni::Options.instance
|
|
16
|
+
|
|
17
|
+
require opts.dir['lib'] + 'arachni'
|
|
18
|
+
require opts.dir['lib'] + 'ruby'
|
|
19
|
+
require opts.dir['lib'] + 'exceptions'
|
|
20
|
+
require opts.dir['lib'] + 'spider'
|
|
21
|
+
require opts.dir['lib'] + 'parser'
|
|
22
|
+
require opts.dir['lib'] + 'audit_store'
|
|
23
|
+
require opts.dir['lib'] + 'module'
|
|
24
|
+
require opts.dir['lib'] + 'plugin'
|
|
25
|
+
require opts.dir['lib'] + 'http'
|
|
26
|
+
require opts.dir['lib'] + 'report'
|
|
27
|
+
require opts.dir['lib'] + 'component_manager'
|
|
28
|
+
require 'yaml'
|
|
29
|
+
require 'ap'
|
|
30
|
+
require 'pp'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
module Arachni
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# Resets the Framework providing a clean slate.
|
|
37
|
+
#
|
|
38
|
+
# This is useful to user interfaces that require the framework to be reused.
|
|
39
|
+
#
|
|
40
|
+
def self.reset
|
|
41
|
+
Element::Auditable.reset
|
|
42
|
+
Module::Manager.reset
|
|
43
|
+
Report::Manager.reset
|
|
44
|
+
Arachni::HTTP.instance.reset
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#
|
|
48
|
+
# Arachni::Framework class
|
|
49
|
+
#
|
|
50
|
+
# The Framework class ties together all the components.<br/>
|
|
51
|
+
# It should be wrapped by a UI class.
|
|
52
|
+
#
|
|
53
|
+
# It's the brains of the operation, it bosses the rest of the classes around.<br/>
|
|
54
|
+
# It runs the audit, loads modules and reports and runs them according to
|
|
55
|
+
# user options.
|
|
56
|
+
#
|
|
57
|
+
# @author: Tasos "Zapotek" Laskos
|
|
58
|
+
# <tasos.laskos@gmail.com>
|
|
59
|
+
# <zapotek@segfault.gr>
|
|
60
|
+
# @version: 0.2.1
|
|
61
|
+
#
|
|
62
|
+
class Framework
|
|
63
|
+
|
|
64
|
+
#
|
|
65
|
+
# include the output interface but try to use it as little as possible
|
|
66
|
+
#
|
|
67
|
+
# the UI classes should take care of communicating with the user
|
|
68
|
+
#
|
|
69
|
+
include Arachni::UI::Output
|
|
70
|
+
include Arachni::Module::Utilities
|
|
71
|
+
|
|
72
|
+
# the version of *this* class
|
|
73
|
+
REVISION = '0.2.1'
|
|
74
|
+
|
|
75
|
+
#
|
|
76
|
+
# Instance options
|
|
77
|
+
#
|
|
78
|
+
# @return [Options]
|
|
79
|
+
#
|
|
80
|
+
attr_reader :opts
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# @return [Arachni::Report::Manager] report manager
|
|
84
|
+
#
|
|
85
|
+
attr_reader :reports
|
|
86
|
+
|
|
87
|
+
#
|
|
88
|
+
# @return [Arachni::Module::Manager] module manager
|
|
89
|
+
#
|
|
90
|
+
attr_reader :modules
|
|
91
|
+
|
|
92
|
+
#
|
|
93
|
+
# @return [Arachni::Plugin::Manager] plugin manager
|
|
94
|
+
#
|
|
95
|
+
attr_reader :plugins
|
|
96
|
+
|
|
97
|
+
#
|
|
98
|
+
# @return [Arachni::Spider] spider
|
|
99
|
+
#
|
|
100
|
+
attr_reader :spider
|
|
101
|
+
|
|
102
|
+
#
|
|
103
|
+
# @return [Arachni::HTTP]
|
|
104
|
+
#
|
|
105
|
+
attr_reader :http
|
|
106
|
+
|
|
107
|
+
attr_reader :sitemap
|
|
108
|
+
attr_reader :auditmap
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
# Holds candidate pages to be audited.
|
|
112
|
+
#
|
|
113
|
+
# Pages in the queue are pushed in by the trainer, the queue doesn't hold
|
|
114
|
+
# pages returned by the spider.
|
|
115
|
+
#
|
|
116
|
+
# Plug-ins can push their own pages to be audited if they wish to...
|
|
117
|
+
#
|
|
118
|
+
# @return [Queue<Arachni::Parser::Page>] page queue
|
|
119
|
+
#
|
|
120
|
+
attr_reader :page_queue
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
#
|
|
124
|
+
# Initializes system components.
|
|
125
|
+
#
|
|
126
|
+
# @param [Options] opts
|
|
127
|
+
#
|
|
128
|
+
def initialize( opts )
|
|
129
|
+
|
|
130
|
+
Encoding.default_external = "BINARY"
|
|
131
|
+
Encoding.default_internal = "BINARY"
|
|
132
|
+
|
|
133
|
+
@opts = opts
|
|
134
|
+
|
|
135
|
+
@modules = Arachni::Module::Manager.new( @opts )
|
|
136
|
+
@reports = Arachni::Report::Manager.new( @opts )
|
|
137
|
+
@plugins = Arachni::Plugin::Manager.new( self )
|
|
138
|
+
|
|
139
|
+
@page_queue = Queue.new
|
|
140
|
+
|
|
141
|
+
prepare_cookie_jar( )
|
|
142
|
+
prepare_user_agent( )
|
|
143
|
+
|
|
144
|
+
# deep clone the redundancy rules to preserve their counter
|
|
145
|
+
# for the reports
|
|
146
|
+
@orig_redundant = @opts.redundant.deep_clone
|
|
147
|
+
|
|
148
|
+
@running = false
|
|
149
|
+
@paused = []
|
|
150
|
+
|
|
151
|
+
@plugin_store = {}
|
|
152
|
+
|
|
153
|
+
@current_url = ''
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def http
|
|
157
|
+
Arachni::HTTP.instance
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
#
|
|
161
|
+
# Runs the system
|
|
162
|
+
#
|
|
163
|
+
# It parses the instanse options and runs the audit
|
|
164
|
+
#
|
|
165
|
+
# @param [Block] &block a block to call after the audit has finished
|
|
166
|
+
# but before running the reports
|
|
167
|
+
#
|
|
168
|
+
def run( &block )
|
|
169
|
+
@running = true
|
|
170
|
+
|
|
171
|
+
@opts.start_datetime = Time.now
|
|
172
|
+
|
|
173
|
+
# run all plugins
|
|
174
|
+
@plugins.run
|
|
175
|
+
|
|
176
|
+
# catch exceptions so that if something breaks down or the user opted to
|
|
177
|
+
# exit the reports will still run with whatever results
|
|
178
|
+
# Arachni managed to gather
|
|
179
|
+
begin
|
|
180
|
+
# start the audit
|
|
181
|
+
audit( )
|
|
182
|
+
rescue Exception
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
clean_up!
|
|
186
|
+
begin
|
|
187
|
+
block.call if block
|
|
188
|
+
rescue Exception
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# run reports
|
|
192
|
+
if( @opts.reports && !@opts.reports.empty? )
|
|
193
|
+
exception_jail{ @reports.run( audit_store( ) ) }
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
return true
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def stats( refresh_time = false )
|
|
200
|
+
req_cnt = http.request_count
|
|
201
|
+
res_cnt = http.response_count
|
|
202
|
+
|
|
203
|
+
@auditmap ||= []
|
|
204
|
+
@sitemap ||= []
|
|
205
|
+
if !refresh_time || @auditmap.size == @sitemap.size
|
|
206
|
+
@opts.delta_time ||= Time.now - @opts.start_datetime
|
|
207
|
+
else
|
|
208
|
+
@opts.delta_time = Time.now - @opts.start_datetime
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
curr_avg = 0
|
|
212
|
+
if http.curr_res_cnt > 0
|
|
213
|
+
curr_avg = (http.curr_res_cnt / http.curr_res_time).to_i.to_s
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
:requests => req_cnt,
|
|
218
|
+
:responses => res_cnt,
|
|
219
|
+
:time => audit_store.delta_time,
|
|
220
|
+
:avg => ( res_cnt / @opts.delta_time ).to_i.to_s,
|
|
221
|
+
:sitemap_size => @sitemap.size,
|
|
222
|
+
:auditmap_size => @auditmap.size,
|
|
223
|
+
:curr_res_time => http.curr_res_time,
|
|
224
|
+
:curr_res_cnt => http.curr_res_cnt,
|
|
225
|
+
:curr_avg => curr_avg,
|
|
226
|
+
:average_res_time => http.average_res_time,
|
|
227
|
+
:max_concurrency => http.max_concurrency,
|
|
228
|
+
:current_page => @current_url
|
|
229
|
+
}
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
#
|
|
233
|
+
# Audits the site.
|
|
234
|
+
#
|
|
235
|
+
# Runs the spider, analyzes each page as it appears and passes it
|
|
236
|
+
# to (#run_mods} to be audited.
|
|
237
|
+
#
|
|
238
|
+
def audit
|
|
239
|
+
|
|
240
|
+
wait_if_paused
|
|
241
|
+
|
|
242
|
+
@spider = Arachni::Spider.new( @opts )
|
|
243
|
+
|
|
244
|
+
@sitemap ||= []
|
|
245
|
+
@auditmap ||= []
|
|
246
|
+
|
|
247
|
+
# initiates the crawl
|
|
248
|
+
@spider.run {
|
|
249
|
+
|page|
|
|
250
|
+
|
|
251
|
+
@sitemap |= @spider.pages
|
|
252
|
+
|
|
253
|
+
@page_queue << page
|
|
254
|
+
audit_queue if !@opts.spider_first
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
audit_queue
|
|
258
|
+
|
|
259
|
+
if( @opts.http_harvest_last )
|
|
260
|
+
harvest_http_responses( )
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def audit_queue
|
|
266
|
+
|
|
267
|
+
# this will run until no new elements appear for the given page
|
|
268
|
+
while( !@page_queue.empty? && page = @page_queue.pop )
|
|
269
|
+
|
|
270
|
+
# audit the page
|
|
271
|
+
exception_jail{ run_mods( page ) }
|
|
272
|
+
|
|
273
|
+
# run all the queued HTTP requests and harvest the responses
|
|
274
|
+
http.run
|
|
275
|
+
|
|
276
|
+
# check to see if the page was updated
|
|
277
|
+
page = http.trainer.page
|
|
278
|
+
# and push it in the queue to be audited as well
|
|
279
|
+
@page_queue << page if page
|
|
280
|
+
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
#
|
|
286
|
+
# Returns the results of the audit as an {AuditStore} instance
|
|
287
|
+
#
|
|
288
|
+
# @see AuditStore
|
|
289
|
+
#
|
|
290
|
+
# @return [AuditStore]
|
|
291
|
+
#
|
|
292
|
+
def audit_store( fresh = false )
|
|
293
|
+
|
|
294
|
+
# restore the original redundacy rules and their counters
|
|
295
|
+
@opts.redundant = @orig_redundant
|
|
296
|
+
opts = @opts.to_h
|
|
297
|
+
opts['mods'] = @modules.keys
|
|
298
|
+
|
|
299
|
+
if( !fresh && @store )
|
|
300
|
+
return @store
|
|
301
|
+
else
|
|
302
|
+
return @store = AuditStore.new( {
|
|
303
|
+
:version => version( ),
|
|
304
|
+
:revision => REVISION,
|
|
305
|
+
:options => opts,
|
|
306
|
+
:sitemap => @sitemap ? @sitemap.sort : ['N/A'],
|
|
307
|
+
:issues => @modules.results( ).deep_clone,
|
|
308
|
+
:plugins => @plugin_store
|
|
309
|
+
}, self )
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def plugin_store( plugin, obj )
|
|
314
|
+
name = ''
|
|
315
|
+
@plugins.each_pair {
|
|
316
|
+
|k, v|
|
|
317
|
+
|
|
318
|
+
if plugin.class.name == v.name
|
|
319
|
+
name = k
|
|
320
|
+
break
|
|
321
|
+
end
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
@plugin_store[name] = {
|
|
325
|
+
:results => obj
|
|
326
|
+
}.merge( plugin.class.info )
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
#
|
|
330
|
+
# Returns an array of hashes with information
|
|
331
|
+
# about all available modules
|
|
332
|
+
#
|
|
333
|
+
# @return [Array<Hash>]
|
|
334
|
+
#
|
|
335
|
+
def lsmod
|
|
336
|
+
|
|
337
|
+
mod_info = []
|
|
338
|
+
@modules.available( ).each {
|
|
339
|
+
|name|
|
|
340
|
+
|
|
341
|
+
path = @modules.name_to_path( name )
|
|
342
|
+
next if !lsmod_match?( path )
|
|
343
|
+
|
|
344
|
+
info = @modules[name].info( )
|
|
345
|
+
|
|
346
|
+
info[:mod_name] = name
|
|
347
|
+
info[:name] = info[:name].strip
|
|
348
|
+
info[:description] = info[:description].strip
|
|
349
|
+
|
|
350
|
+
if( !info[:dependencies] )
|
|
351
|
+
info[:dependencies] = []
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
info[:author] = info[:author].strip
|
|
355
|
+
info[:version] = info[:version].strip
|
|
356
|
+
info[:path] = path.strip
|
|
357
|
+
|
|
358
|
+
mod_info << info
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
# unload all modules
|
|
362
|
+
@modules.clear( )
|
|
363
|
+
|
|
364
|
+
return mod_info
|
|
365
|
+
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
#
|
|
369
|
+
# Returns an array of hashes with information
|
|
370
|
+
# about all available reports
|
|
371
|
+
#
|
|
372
|
+
# @return [Array<Hash>]
|
|
373
|
+
#
|
|
374
|
+
def lsrep
|
|
375
|
+
|
|
376
|
+
rep_info = []
|
|
377
|
+
@reports.available( ).each {
|
|
378
|
+
|report|
|
|
379
|
+
|
|
380
|
+
info = @reports[report].info
|
|
381
|
+
|
|
382
|
+
info[:rep_name] = report
|
|
383
|
+
info[:path] = @reports.name_to_path( report )
|
|
384
|
+
|
|
385
|
+
rep_info << info
|
|
386
|
+
}
|
|
387
|
+
@reports.clear( )
|
|
388
|
+
|
|
389
|
+
return rep_info
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
#
|
|
393
|
+
# Returns an array of hashes with information
|
|
394
|
+
# about all available reports
|
|
395
|
+
#
|
|
396
|
+
# @return [Array<Hash>]
|
|
397
|
+
#
|
|
398
|
+
def lsplug
|
|
399
|
+
|
|
400
|
+
plug_info = []
|
|
401
|
+
|
|
402
|
+
@plugins.available( ).each {
|
|
403
|
+
|plugin|
|
|
404
|
+
|
|
405
|
+
info = @plugins[plugin].info
|
|
406
|
+
|
|
407
|
+
info[:plug_name] = plugin
|
|
408
|
+
info[:path] = @plugins.name_to_path( plugin )
|
|
409
|
+
|
|
410
|
+
plug_info << info
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
@plugins.clear( )
|
|
414
|
+
|
|
415
|
+
return plug_info
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def running?
|
|
419
|
+
@running
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def paused?
|
|
423
|
+
!@paused.empty?
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def pause!
|
|
427
|
+
@paused << caller
|
|
428
|
+
return true
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def resume!
|
|
432
|
+
@paused.delete( caller )
|
|
433
|
+
return true
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
#
|
|
437
|
+
# Returns the version of the framework
|
|
438
|
+
#
|
|
439
|
+
# @return [String]
|
|
440
|
+
#
|
|
441
|
+
def version
|
|
442
|
+
Arachni::VERSION
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
#
|
|
446
|
+
# Returns the revision of the {Framework} (this) class
|
|
447
|
+
#
|
|
448
|
+
# @return [String]
|
|
449
|
+
#
|
|
450
|
+
def revision
|
|
451
|
+
REVISION
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
private
|
|
455
|
+
|
|
456
|
+
def clean_up!
|
|
457
|
+
@opts.finish_datetime = Time.now
|
|
458
|
+
@opts.delta_time = @opts.finish_datetime - @opts.start_datetime
|
|
459
|
+
|
|
460
|
+
# make sure this is disabled or it'll break report output
|
|
461
|
+
@@only_positives = false
|
|
462
|
+
|
|
463
|
+
@running = false
|
|
464
|
+
|
|
465
|
+
# wait for the plugins to finish
|
|
466
|
+
@plugins.block!
|
|
467
|
+
|
|
468
|
+
# a plug-in may have updated the page queue, rock it!
|
|
469
|
+
audit_queue
|
|
470
|
+
|
|
471
|
+
# refresh the audit store
|
|
472
|
+
audit_store( true )
|
|
473
|
+
|
|
474
|
+
return true
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def caller
|
|
478
|
+
if /^(.+?):(\d+)(?::in `(.*)')?/ =~ ::Kernel.caller[1]
|
|
479
|
+
return Regexp.last_match[1]
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def wait_if_paused
|
|
484
|
+
while( paused? )
|
|
485
|
+
::IO::select( nil, nil, nil, 1 )
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
#
|
|
491
|
+
# Prepares the user agent to be used throughout the system.
|
|
492
|
+
#
|
|
493
|
+
def prepare_user_agent
|
|
494
|
+
if( !@opts.user_agent )
|
|
495
|
+
@opts.user_agent = 'Arachni/' + version( )
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
if( @opts.authed_by )
|
|
499
|
+
authed_by = " (Scan authorized by: #{@opts.authed_by})"
|
|
500
|
+
@opts.user_agent += authed_by
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def prepare_cookie_jar( )
|
|
506
|
+
return if !@opts.cookie_jar || !@opts.cookie_jar.is_a?( String )
|
|
507
|
+
|
|
508
|
+
# make sure that the provided cookie-jar file exists
|
|
509
|
+
if !File.exist?( @opts.cookie_jar )
|
|
510
|
+
raise( Arachni::Exceptions::NoCookieJar,
|
|
511
|
+
'Cookie-jar \'' + @opts.cookie_jar + '\' doesn\'t exist.' )
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
#
|
|
518
|
+
# Takes care of page audit and module execution
|
|
519
|
+
#
|
|
520
|
+
# It will audit one page at a time as discovered by the spider <br/>
|
|
521
|
+
# and recursively check for new elements that may have <br/>
|
|
522
|
+
# appeared during the audit.
|
|
523
|
+
#
|
|
524
|
+
# When no new elements appear the recursion will stop and a new page<br/>
|
|
525
|
+
# will be accepted.
|
|
526
|
+
#
|
|
527
|
+
# @see Page
|
|
528
|
+
#
|
|
529
|
+
# @param [Page] page
|
|
530
|
+
#
|
|
531
|
+
def run_mods( page )
|
|
532
|
+
return if !page
|
|
533
|
+
|
|
534
|
+
@current_url = page.url.to_s
|
|
535
|
+
|
|
536
|
+
@modules.each_pair {
|
|
537
|
+
|name, mod|
|
|
538
|
+
|
|
539
|
+
wait_if_paused
|
|
540
|
+
run_mod( mod, page.deep_clone )
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
@auditmap << page.url
|
|
544
|
+
@auditmap.uniq!
|
|
545
|
+
@sitemap |= @auditmap
|
|
546
|
+
@sitemap.uniq!
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
if( !@opts.http_harvest_last )
|
|
550
|
+
harvest_http_responses( )
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def harvest_http_responses
|
|
556
|
+
|
|
557
|
+
print_status( 'Harvesting HTTP responses...' )
|
|
558
|
+
print_info( 'Depending on server responsiveness and network' +
|
|
559
|
+
' conditions this may take a while.' )
|
|
560
|
+
|
|
561
|
+
# run all the queued HTTP requests and harvest the responses
|
|
562
|
+
http.run
|
|
563
|
+
|
|
564
|
+
# try to get an updated page from the Trainer
|
|
565
|
+
page = http.trainer.page
|
|
566
|
+
|
|
567
|
+
# if there was an updated page push it in the queue
|
|
568
|
+
@page_queue << page if page
|
|
569
|
+
audit_queue
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
#
|
|
573
|
+
# Passes a page to the module and runs it.<br/>
|
|
574
|
+
# It also handles any exceptions thrown by the module at runtime.
|
|
575
|
+
#
|
|
576
|
+
# @see Page
|
|
577
|
+
#
|
|
578
|
+
# @param [Class] mod the module to run
|
|
579
|
+
# @param [Page] page
|
|
580
|
+
#
|
|
581
|
+
def run_mod( mod, page )
|
|
582
|
+
return if !run_mod?( mod, page )
|
|
583
|
+
|
|
584
|
+
begin
|
|
585
|
+
|
|
586
|
+
# instantiate the module
|
|
587
|
+
mod_new = mod.new( page )
|
|
588
|
+
|
|
589
|
+
mod_new.prepare
|
|
590
|
+
mod_new.run
|
|
591
|
+
mod_new.clean_up
|
|
592
|
+
rescue Exception => e
|
|
593
|
+
print_error( 'Error in ' + mod.to_s + ': ' + e.to_s )
|
|
594
|
+
print_debug_backtrace( e )
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
#
|
|
599
|
+
# Determines whether or not to run the module against the given page
|
|
600
|
+
# depending on which elements exist in the page, which elements the module
|
|
601
|
+
# is configured to audit and user options.
|
|
602
|
+
#
|
|
603
|
+
# @param [Class] mod the module to run
|
|
604
|
+
# @param [Page] page
|
|
605
|
+
#
|
|
606
|
+
# @return [Bool]
|
|
607
|
+
#
|
|
608
|
+
def run_mod?( mod, page )
|
|
609
|
+
return true if( !mod.info[:elements] || mod.info[:elements].empty? )
|
|
610
|
+
|
|
611
|
+
elems = {
|
|
612
|
+
Issue::Element::LINK => page.links && page.links.size > 0 && @opts.audit_links,
|
|
613
|
+
Issue::Element::FORM => page.forms && page.forms.size > 0 && @opts.audit_forms,
|
|
614
|
+
Issue::Element::COOKIE => page.cookies && page.cookies.size > 0 && @opts.audit_cookies,
|
|
615
|
+
Issue::Element::HEADER => page.headers && page.headers.size > 0 && @opts.audit_headers,
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
elems.each_pair {
|
|
619
|
+
|elem, expr|
|
|
620
|
+
return true if mod.info[:elements].include?( elem ) && expr
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return false
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def lsmod_match?( path )
|
|
627
|
+
cnt = 0
|
|
628
|
+
@opts.lsmod.each {
|
|
629
|
+
|filter|
|
|
630
|
+
cnt += 1 if path =~ filter
|
|
631
|
+
}
|
|
632
|
+
return true if cnt == @opts.lsmod.size
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
end
|