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
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
# Overloads the {XMLRPC::BasicServer} class with a clear_handlers() method.
|
|
13
|
+
#
|
|
14
|
+
# @author: Tasos "Zapotek" Laskos
|
|
15
|
+
# <tasos.laskos@gmail.com>
|
|
16
|
+
# <zapotek@segfault.gr>
|
|
17
|
+
# @version: 0.1
|
|
18
|
+
#
|
|
19
|
+
module XMLRPC
|
|
20
|
+
class BasicServer
|
|
21
|
+
|
|
22
|
+
def clear_handlers
|
|
23
|
+
@handler.clear
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/spider.rb
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
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'] + 'anemone'
|
|
12
|
+
require Arachni::Options.instance.dir['lib'] + 'module/utilities'
|
|
13
|
+
|
|
14
|
+
module Arachni
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# Spider class
|
|
18
|
+
#
|
|
19
|
+
# Crawls the URL in opts[:url] and grabs the HTML code and headers.
|
|
20
|
+
#
|
|
21
|
+
# @author: Tasos "Zapotek" Laskos
|
|
22
|
+
# <tasos.laskos@gmail.com>
|
|
23
|
+
# <zapotek@segfault.gr>
|
|
24
|
+
# @version: 0.1
|
|
25
|
+
#
|
|
26
|
+
class Spider
|
|
27
|
+
|
|
28
|
+
include Arachni::UI::Output
|
|
29
|
+
include Arachni::Module::Utilities
|
|
30
|
+
|
|
31
|
+
#
|
|
32
|
+
#
|
|
33
|
+
# @return [Options]
|
|
34
|
+
#
|
|
35
|
+
attr_reader :opts
|
|
36
|
+
|
|
37
|
+
attr_reader :pages
|
|
38
|
+
|
|
39
|
+
#
|
|
40
|
+
# Sitemap, array of links
|
|
41
|
+
#
|
|
42
|
+
# @return [Array]
|
|
43
|
+
#
|
|
44
|
+
attr_reader :sitemap
|
|
45
|
+
|
|
46
|
+
#
|
|
47
|
+
# Code block to be executed on each page
|
|
48
|
+
#
|
|
49
|
+
# @return [Proc]
|
|
50
|
+
#
|
|
51
|
+
attr_reader :on_every_page_blocks
|
|
52
|
+
|
|
53
|
+
#
|
|
54
|
+
# Constructor <br/>
|
|
55
|
+
# Instantiates Spider class with user options.
|
|
56
|
+
#
|
|
57
|
+
# @param [Options] opts
|
|
58
|
+
#
|
|
59
|
+
def initialize( opts )
|
|
60
|
+
@opts = opts
|
|
61
|
+
|
|
62
|
+
@anemone_opts = {
|
|
63
|
+
:threads => 1,
|
|
64
|
+
:discard_page_bodies => false,
|
|
65
|
+
:delay => 0,
|
|
66
|
+
:obey_robots_txt => false,
|
|
67
|
+
:depth_limit => false,
|
|
68
|
+
:link_count_limit => false,
|
|
69
|
+
:redirect_limit => false,
|
|
70
|
+
:storage => nil,
|
|
71
|
+
:cookies => nil,
|
|
72
|
+
:accept_cookies => true,
|
|
73
|
+
:proxy_addr => nil,
|
|
74
|
+
:proxy_port => nil,
|
|
75
|
+
:proxy_user => nil,
|
|
76
|
+
:proxy_pass => nil
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
hash_opts = @opts.to_h
|
|
80
|
+
@anemone_opts.each_pair {
|
|
81
|
+
|k, v|
|
|
82
|
+
@anemone_opts[k] = hash_opts[k.to_s] if hash_opts[k.to_s]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@anemone_opts = @anemone_opts.merge( hash_opts )
|
|
86
|
+
|
|
87
|
+
@sitemap = []
|
|
88
|
+
@on_every_page_blocks = []
|
|
89
|
+
|
|
90
|
+
# if we have no 'include' patterns create one that will match
|
|
91
|
+
# everything, like '.*'
|
|
92
|
+
@opts.include =[ Regexp.new( '.*' ) ] if @opts.include.empty?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
#
|
|
96
|
+
# Runs the Spider and passes parsed page to the block
|
|
97
|
+
#
|
|
98
|
+
# @param [Block] block
|
|
99
|
+
#
|
|
100
|
+
# @return [Arachni::Parser::Page]
|
|
101
|
+
#
|
|
102
|
+
def run( &block )
|
|
103
|
+
return if @opts.link_count_limit == 0
|
|
104
|
+
|
|
105
|
+
i = 1
|
|
106
|
+
# start the crawl
|
|
107
|
+
Anemone.crawl( @opts.url, @anemone_opts ) {
|
|
108
|
+
|anemone|
|
|
109
|
+
|
|
110
|
+
# apply 'exclude' patterns
|
|
111
|
+
anemone.skip_links_like( @opts.exclude ) if @opts.exclude
|
|
112
|
+
|
|
113
|
+
# apply 'include' patterns and grab matching pages
|
|
114
|
+
# as they are discovered
|
|
115
|
+
anemone.on_pages_like( @opts.include ) {
|
|
116
|
+
|page|
|
|
117
|
+
|
|
118
|
+
@pages = anemone.pages.keys || []
|
|
119
|
+
|
|
120
|
+
url = url_sanitize( page.url.to_s )
|
|
121
|
+
|
|
122
|
+
# something went kaboom, tell the user and skip the page
|
|
123
|
+
if page.error
|
|
124
|
+
print_error( "[Error: " + (page.error.to_s) + "] " + url )
|
|
125
|
+
print_debug_backtrace( page.error )
|
|
126
|
+
next
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# push the url in the sitemap
|
|
130
|
+
@sitemap.push( url )
|
|
131
|
+
|
|
132
|
+
print_line
|
|
133
|
+
print_status( "[HTTP: #{page.code}] " + url )
|
|
134
|
+
|
|
135
|
+
# call the block...if we have one
|
|
136
|
+
if block
|
|
137
|
+
exception_jail{
|
|
138
|
+
new_page = Arachni::Parser.new( @opts,
|
|
139
|
+
Typhoeus::Response.new(
|
|
140
|
+
:effective_url => url,
|
|
141
|
+
:body => page.body,
|
|
142
|
+
:headers_hash => page.headers
|
|
143
|
+
)
|
|
144
|
+
).run
|
|
145
|
+
new_page.code = page.code
|
|
146
|
+
new_page.method = 'GET'
|
|
147
|
+
block.call( new_page.clone )
|
|
148
|
+
}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# run blocks specified later
|
|
152
|
+
@on_every_page_blocks.each {
|
|
153
|
+
|block|
|
|
154
|
+
block.call( page )
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# we don't need the HTML doc anymore
|
|
158
|
+
page.discard_doc!( )
|
|
159
|
+
|
|
160
|
+
# make sure we obey the link count limit and
|
|
161
|
+
# return if we have exceeded it.
|
|
162
|
+
if( @opts.link_count_limit &&
|
|
163
|
+
@opts.link_count_limit <= i )
|
|
164
|
+
return @sitemap.uniq
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
i+=1
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return @sitemap.uniq
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
#
|
|
175
|
+
# Decodes URLs to reverse multiple encodes and removes NULL characters
|
|
176
|
+
#
|
|
177
|
+
def url_sanitize( url )
|
|
178
|
+
|
|
179
|
+
while( url =~ /%/ )
|
|
180
|
+
url = ( URI.decode( url ).to_s.unpack( 'A*' )[0] )
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
return url
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
#
|
|
188
|
+
# Hook for further analysis of pages, statistics etc.
|
|
189
|
+
#
|
|
190
|
+
# @param [Proc] block code to be executed for every page
|
|
191
|
+
#
|
|
192
|
+
# @return [self]
|
|
193
|
+
#
|
|
194
|
+
def on_every_page( &block )
|
|
195
|
+
@on_every_page_blocks.push( block )
|
|
196
|
+
self
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
end
|
|
200
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
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
|
+
# Override the on_complete methods of Typhoeus adding support
|
|
13
|
+
# for multiple on_complete blocks.
|
|
14
|
+
#
|
|
15
|
+
# Also added support for on demand training of the response and
|
|
16
|
+
# incremental request id numbers.
|
|
17
|
+
#
|
|
18
|
+
module Typhoeus
|
|
19
|
+
|
|
20
|
+
class Request
|
|
21
|
+
|
|
22
|
+
attr_accessor :id, :proxy, :proxy_username, :proxy_password,
|
|
23
|
+
:proxy_username, :proxy_password, :proxy_type
|
|
24
|
+
|
|
25
|
+
alias :old_initialize :initialize
|
|
26
|
+
|
|
27
|
+
def initialize( url, options = {} )
|
|
28
|
+
|
|
29
|
+
old_initialize( url, options )
|
|
30
|
+
|
|
31
|
+
@proxy_type = options[:proxy_type]
|
|
32
|
+
@proxy_username = options[:proxy_username]
|
|
33
|
+
@proxy_password = options[:proxy_password]
|
|
34
|
+
@proxy_auth_method = options[:proxy_auth_method]
|
|
35
|
+
|
|
36
|
+
@on_complete = []
|
|
37
|
+
@handled_response = []
|
|
38
|
+
@multiple_callbacks = false
|
|
39
|
+
@train = false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def on_complete( multi = false, &block )
|
|
43
|
+
|
|
44
|
+
# remember user preference for subsequent calls
|
|
45
|
+
if( multi || @multiple_callbacks )
|
|
46
|
+
@multiple_callbacks = true
|
|
47
|
+
@on_complete << block
|
|
48
|
+
else
|
|
49
|
+
@on_complete = block
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def on_complete=( multi = false, proc )
|
|
55
|
+
|
|
56
|
+
# remember user preference for subsequent calls
|
|
57
|
+
if( multi || @multiple_callbacks )
|
|
58
|
+
@multiple_callbacks = true
|
|
59
|
+
@on_complete << proc
|
|
60
|
+
else
|
|
61
|
+
@on_complete = proc
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def call_handlers
|
|
67
|
+
|
|
68
|
+
if @on_complete.is_a? Array
|
|
69
|
+
|
|
70
|
+
@on_complete.each do |callback|
|
|
71
|
+
@handled_response << callback.call(response)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
else
|
|
75
|
+
@handled_response << @on_complete.call(response)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
call_after_complete
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def train?
|
|
82
|
+
@train
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def train!
|
|
86
|
+
@train = true
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
module Typhoeus
|
|
3
|
+
class Response
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# Converts obj to hash
|
|
7
|
+
#
|
|
8
|
+
# @param [Object] obj instance of an object
|
|
9
|
+
#
|
|
10
|
+
# @return [Hash]
|
|
11
|
+
#
|
|
12
|
+
def to_hash
|
|
13
|
+
hash = Hash.new
|
|
14
|
+
instance_variables.each {
|
|
15
|
+
|var|
|
|
16
|
+
key = var.to_s.gsub( /@/, '' )
|
|
17
|
+
hash[key] = instance_variable_get( var )
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
hash['headers_hash'] = {}
|
|
22
|
+
headers_hash.each_pair {
|
|
23
|
+
|k, v|
|
|
24
|
+
hash['headers_hash'][k] = v
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
hash.delete( 'request' )
|
|
28
|
+
|
|
29
|
+
return hash
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/ui/cli/cli.rb
ADDED
|
@@ -0,0 +1,744 @@
|
|
|
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
|
+
module Arachni
|
|
13
|
+
|
|
14
|
+
require Options.instance.dir['lib'] + 'ui/cli/output'
|
|
15
|
+
require Options.instance.dir['lib'] + 'framework'
|
|
16
|
+
|
|
17
|
+
module UI
|
|
18
|
+
|
|
19
|
+
#
|
|
20
|
+
# Arachni::UI:CLI class
|
|
21
|
+
#
|
|
22
|
+
# Provides a command line interface for the Arachni Framework.<br/>
|
|
23
|
+
# Most of the logic is in the Framework class however profiles can only<br/>
|
|
24
|
+
# be loaded and saved at this level.
|
|
25
|
+
#
|
|
26
|
+
# @author: Tasos "Zapotek" Laskos
|
|
27
|
+
# <tasos.laskos@gmail.com>
|
|
28
|
+
# <zapotek@segfault.gr>
|
|
29
|
+
# @version: 0.1.6
|
|
30
|
+
# @see Arachni::Framework
|
|
31
|
+
#
|
|
32
|
+
class CLI
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# Instance options
|
|
36
|
+
#
|
|
37
|
+
# @return [Options]
|
|
38
|
+
#
|
|
39
|
+
attr_reader :opts
|
|
40
|
+
|
|
41
|
+
# the output interface for CLI
|
|
42
|
+
include Arachni::UI::Output
|
|
43
|
+
include Arachni::Module::Utilities
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
# Initializes the command line interface and the framework
|
|
47
|
+
#
|
|
48
|
+
# @param [Options] opts
|
|
49
|
+
#
|
|
50
|
+
def initialize( opts )
|
|
51
|
+
|
|
52
|
+
@opts = opts
|
|
53
|
+
|
|
54
|
+
# if we have a load profile load it and merge it with the
|
|
55
|
+
# user supplied options
|
|
56
|
+
if( @opts.load_profile )
|
|
57
|
+
load_profile( @opts.load_profile )
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
#
|
|
61
|
+
# the stdout report is the default one for the CLI,
|
|
62
|
+
# each UI should have it's own default
|
|
63
|
+
#
|
|
64
|
+
# always load the stdout report unless the user requested
|
|
65
|
+
# to see a list of the available reports
|
|
66
|
+
#
|
|
67
|
+
# *do not* forget this check, otherwise the reports registry
|
|
68
|
+
# will desync
|
|
69
|
+
#
|
|
70
|
+
if( @opts.reports.empty? && @opts.lsrep.empty? )
|
|
71
|
+
@opts.reports['stdout'] = {}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# instantiate the big-boy!
|
|
75
|
+
@arachni = Arachni::Framework.new( @opts )
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# echo the banner
|
|
79
|
+
banner( )
|
|
80
|
+
|
|
81
|
+
# work on the user supplied arguments
|
|
82
|
+
parse_opts( )
|
|
83
|
+
|
|
84
|
+
# trap Ctrl+C interrupts
|
|
85
|
+
trap( 'INT' ) { handle_interrupt( ) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
#
|
|
89
|
+
# Runs Arachni
|
|
90
|
+
#
|
|
91
|
+
def run( )
|
|
92
|
+
|
|
93
|
+
print_status( 'Initing...' )
|
|
94
|
+
|
|
95
|
+
begin
|
|
96
|
+
# start the show!
|
|
97
|
+
@arachni.run( ){
|
|
98
|
+
@interrupt_handler.join if @interrupt_handler
|
|
99
|
+
}
|
|
100
|
+
print_stats
|
|
101
|
+
rescue Arachni::Exceptions::NoMods => e
|
|
102
|
+
print_error( e.to_s )
|
|
103
|
+
print_info( "Run arachni with the '-h' parameter for help or " )
|
|
104
|
+
print_info( "with the '--lsmod' parameter to see all available modules." )
|
|
105
|
+
print_line
|
|
106
|
+
exit 0
|
|
107
|
+
rescue Arachni::Exceptions => e
|
|
108
|
+
print_error( e.to_s )
|
|
109
|
+
print_info( "Run arachni with the '-h' parameter for help." )
|
|
110
|
+
print_line
|
|
111
|
+
exit 0
|
|
112
|
+
rescue Exception => e
|
|
113
|
+
exception_jail{ raise e }
|
|
114
|
+
exit 0
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def print_stats( refresh_time = false )
|
|
121
|
+
|
|
122
|
+
stats = @arachni.stats( refresh_time )
|
|
123
|
+
|
|
124
|
+
audited = stats[:auditmap_size]
|
|
125
|
+
mapped = stats[:sitemap_size]
|
|
126
|
+
progress = ( Float( audited ) / mapped ) * 100
|
|
127
|
+
|
|
128
|
+
print_line
|
|
129
|
+
print_info( "Audit progress: #{progress.to_s[0...5]}% ( #{audited}/#{mapped} pages )" )
|
|
130
|
+
print_line
|
|
131
|
+
print_info( "Sent #{stats[:requests]} requests." )
|
|
132
|
+
print_info( "Received and analyzed #{stats[:responses]} responses." )
|
|
133
|
+
print_info( 'In ' + stats[:time] )
|
|
134
|
+
|
|
135
|
+
avg = 'Average: ' + stats[:avg] + ' requests/second.'
|
|
136
|
+
print_info( avg )
|
|
137
|
+
|
|
138
|
+
print_line
|
|
139
|
+
print_info( "Currently auditing #{stats[:current_page]}" )
|
|
140
|
+
print_info( "Burst response time total #{stats[:curr_res_time]}" )
|
|
141
|
+
print_info( "Burst response count total #{stats[:curr_res_cnt]} " )
|
|
142
|
+
print_info( "Burst average response time #{stats[:average_res_time]}" )
|
|
143
|
+
print_info( "Burst average #{stats[:curr_avg]} requests/second" )
|
|
144
|
+
print_info( "Original max concurrency #{@opts.http_req_limit}" )
|
|
145
|
+
print_info( "Throttled max concurrency #{stats[:max_concurrency]}" )
|
|
146
|
+
|
|
147
|
+
print_line
|
|
148
|
+
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
#
|
|
153
|
+
# Handles Ctrl+C interrupts
|
|
154
|
+
#
|
|
155
|
+
# Once an interrupt has been trapped the system pauses and waits
|
|
156
|
+
# for user input. <br/>
|
|
157
|
+
# The user can either continue or exit.
|
|
158
|
+
#
|
|
159
|
+
# The interrupt will be handled after a module has finished.
|
|
160
|
+
#
|
|
161
|
+
def handle_interrupt( )
|
|
162
|
+
return if @interrupt_handler && @interrupt_handler.alive?
|
|
163
|
+
|
|
164
|
+
only_positives_opt = only_positives?
|
|
165
|
+
@@only_positives = false
|
|
166
|
+
@interrupt_handler = Thread.new {
|
|
167
|
+
|
|
168
|
+
Thread.new {
|
|
169
|
+
if gets[0] == 'e'
|
|
170
|
+
@@only_positives = false
|
|
171
|
+
unmute!
|
|
172
|
+
@interrupt_handler.kill
|
|
173
|
+
|
|
174
|
+
print_info( 'Exiting...' )
|
|
175
|
+
exit 0
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
@@only_positives = only_positives_opt
|
|
179
|
+
unmute!
|
|
180
|
+
@interrupt_handler.kill
|
|
181
|
+
Thread.kill
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
while( 1 )
|
|
185
|
+
|
|
186
|
+
unmute!
|
|
187
|
+
print_line
|
|
188
|
+
clear_screen
|
|
189
|
+
print_info( 'Results thus far:' )
|
|
190
|
+
|
|
191
|
+
begin
|
|
192
|
+
print_issues( @arachni.audit_store( true ) )
|
|
193
|
+
print_stats( true )
|
|
194
|
+
rescue Exception => e
|
|
195
|
+
exception_jail{ raise e }
|
|
196
|
+
exit 0
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
print_info( 'Continue? (hit \'enter\' to continue, \'e\' to exit)' )
|
|
200
|
+
mute!
|
|
201
|
+
|
|
202
|
+
::IO::select( nil, nil, nil, 1 )
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
unmute!
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def clear_screen
|
|
211
|
+
puts "\e[H\e[2J"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def print_issues( audit_store )
|
|
215
|
+
|
|
216
|
+
print_line( )
|
|
217
|
+
print_info( audit_store.issues.size.to_s +
|
|
218
|
+
' issues have been detected.' )
|
|
219
|
+
|
|
220
|
+
print_line( )
|
|
221
|
+
audit_store.issues.each {
|
|
222
|
+
|issue|
|
|
223
|
+
|
|
224
|
+
print_ok( "#{issue.name} (In #{issue.elem} variable '#{issue.var}'" +
|
|
225
|
+
" - Severity: #{issue.severity} - Variations: #{issue.variations.size.to_s})" )
|
|
226
|
+
|
|
227
|
+
print_info( issue.variations[0]['url'] )
|
|
228
|
+
|
|
229
|
+
print_line( )
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
print_line( )
|
|
233
|
+
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
#
|
|
237
|
+
# It parses and processes the user options.
|
|
238
|
+
#
|
|
239
|
+
# Loads modules, reports, saves/loads profiles etc.<br/>
|
|
240
|
+
# It basically prepares the framework before calling {Arachni::Framework#run}.
|
|
241
|
+
#
|
|
242
|
+
def parse_opts( )
|
|
243
|
+
|
|
244
|
+
if !@opts.repload
|
|
245
|
+
|
|
246
|
+
if( !@opts.mods || @opts.mods.empty? )
|
|
247
|
+
print_info( "No modules were specified." )
|
|
248
|
+
print_info( " -> Will run all mods." )
|
|
249
|
+
|
|
250
|
+
@opts.mods = ['*']
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
if( !@opts.audit_links &&
|
|
254
|
+
!@opts.audit_forms &&
|
|
255
|
+
!@opts.audit_cookies &&
|
|
256
|
+
!@opts.audit_headers
|
|
257
|
+
)
|
|
258
|
+
print_info( "No audit options were specified." )
|
|
259
|
+
print_info( " -> Will audit links, forms and cookies." )
|
|
260
|
+
|
|
261
|
+
@opts.audit_links = true
|
|
262
|
+
@opts.audit_forms = true
|
|
263
|
+
@opts.audit_cookies = true
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
@arachni.plugins.load_defaults!
|
|
269
|
+
@opts.to_h.each {
|
|
270
|
+
|opt, arg|
|
|
271
|
+
|
|
272
|
+
case opt.to_s
|
|
273
|
+
|
|
274
|
+
when 'help'
|
|
275
|
+
usage
|
|
276
|
+
exit 0
|
|
277
|
+
|
|
278
|
+
when 'arachni_verbose'
|
|
279
|
+
verbose!
|
|
280
|
+
|
|
281
|
+
when 'debug'
|
|
282
|
+
debug!
|
|
283
|
+
|
|
284
|
+
when 'only_positives'
|
|
285
|
+
only_positives!
|
|
286
|
+
|
|
287
|
+
when 'lsmod'
|
|
288
|
+
next if arg.empty?
|
|
289
|
+
lsmod
|
|
290
|
+
exit 0
|
|
291
|
+
|
|
292
|
+
when 'lsplug'
|
|
293
|
+
next if arg.empty?
|
|
294
|
+
lsplug
|
|
295
|
+
exit 0
|
|
296
|
+
|
|
297
|
+
when 'lsrep'
|
|
298
|
+
next if arg.empty?
|
|
299
|
+
lsrep
|
|
300
|
+
exit 0
|
|
301
|
+
|
|
302
|
+
when 'show_profile'
|
|
303
|
+
print_profile( )
|
|
304
|
+
exit 0
|
|
305
|
+
|
|
306
|
+
when 'save_profile'
|
|
307
|
+
exception_jail{ save_profile( arg ) }
|
|
308
|
+
exit 0
|
|
309
|
+
|
|
310
|
+
when 'mods'
|
|
311
|
+
begin
|
|
312
|
+
exception_jail{
|
|
313
|
+
@opts.mods = @arachni.modules.load( arg )
|
|
314
|
+
}
|
|
315
|
+
rescue
|
|
316
|
+
exit 0
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
when 'reports'
|
|
320
|
+
begin
|
|
321
|
+
exception_jail{ @arachni.reports.load( arg.keys ) }
|
|
322
|
+
rescue
|
|
323
|
+
exit 0
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
when 'plugins'
|
|
327
|
+
begin
|
|
328
|
+
exception_jail{ @arachni.plugins.load( arg.keys ) }
|
|
329
|
+
rescue
|
|
330
|
+
exit 0
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
when 'repload'
|
|
334
|
+
exception_jail{ @arachni.reports.run( AuditStore.load( arg ), false ) }
|
|
335
|
+
exit 0
|
|
336
|
+
|
|
337
|
+
end
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
# Check for missing url
|
|
341
|
+
if( !@opts.url && !@opts.repload )
|
|
342
|
+
print_error( "Missing url argument." )
|
|
343
|
+
exit 0
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
#
|
|
349
|
+
# Outputs all available modules and their info.
|
|
350
|
+
#
|
|
351
|
+
def lsmod
|
|
352
|
+
print_line
|
|
353
|
+
print_line
|
|
354
|
+
print_info( 'Available modules:' )
|
|
355
|
+
print_line
|
|
356
|
+
|
|
357
|
+
mods = @arachni.lsmod( )
|
|
358
|
+
|
|
359
|
+
i = 0
|
|
360
|
+
mods.each {
|
|
361
|
+
|info|
|
|
362
|
+
|
|
363
|
+
print_status( "#{info[:mod_name]}:" )
|
|
364
|
+
print_line( "--------------------" )
|
|
365
|
+
|
|
366
|
+
print_line( "Name:\t\t" + info[:name] )
|
|
367
|
+
print_line( "Description:\t" + info[:description] )
|
|
368
|
+
|
|
369
|
+
if( info[:elements] && info[:elements].size > 0 )
|
|
370
|
+
print_line( "Elements:\t" +
|
|
371
|
+
info[:elements].join( ', ' ).downcase )
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
print_line( "Author:\t\t" + info[:author] )
|
|
375
|
+
print_line( "Version:\t" + info[:version] )
|
|
376
|
+
|
|
377
|
+
if( info[:references] )
|
|
378
|
+
print_line( "References:" )
|
|
379
|
+
info[:references].keys.each {
|
|
380
|
+
|key|
|
|
381
|
+
print_info( key + "\t\t" + info[:references][key] )
|
|
382
|
+
}
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
print_line( "Targets:" )
|
|
386
|
+
info[:targets].keys.each {
|
|
387
|
+
|key|
|
|
388
|
+
print_info( key + "\t\t" + info[:targets][key] )
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if( info[:issue] &&
|
|
392
|
+
( sploit = info[:issue][:metasploitable] ) )
|
|
393
|
+
print_line( "Metasploitable:\t" + sploit )
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
print_line( "Path:\t" + info[:path] )
|
|
397
|
+
|
|
398
|
+
i+=1
|
|
399
|
+
|
|
400
|
+
# pause every 3 modules to give the user time to read
|
|
401
|
+
# (cheers to aungkhant@yehg.net for suggesting it)
|
|
402
|
+
if( i % 3 == 0 && i != mods.size )
|
|
403
|
+
print_line
|
|
404
|
+
print_line( 'Hit <space> <enter> to continue, any other key to exit. ' )
|
|
405
|
+
|
|
406
|
+
if gets[0] != " "
|
|
407
|
+
print_line
|
|
408
|
+
return
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
print_line
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
#
|
|
419
|
+
# Outputs all available reports and their info.
|
|
420
|
+
#
|
|
421
|
+
def lsrep
|
|
422
|
+
print_line
|
|
423
|
+
print_line
|
|
424
|
+
print_info( 'Available reports:' )
|
|
425
|
+
print_line
|
|
426
|
+
|
|
427
|
+
@arachni.lsrep().each {
|
|
428
|
+
|info|
|
|
429
|
+
|
|
430
|
+
print_status( "#{info[:rep_name]}:" )
|
|
431
|
+
print_line( "--------------------" )
|
|
432
|
+
|
|
433
|
+
print_line( "Name:\t\t" + info[:name] )
|
|
434
|
+
print_line( "Description:\t" + info[:description] )
|
|
435
|
+
|
|
436
|
+
if( info[:options] && !info[:options].empty? )
|
|
437
|
+
print_line( "Options:\t" )
|
|
438
|
+
|
|
439
|
+
info[:options].each {
|
|
440
|
+
|option|
|
|
441
|
+
print_info( "\t#{option.name} - #{option.desc}" )
|
|
442
|
+
print_info( "\tType: #{option.type}" )
|
|
443
|
+
print_info( "\tDefault: #{option.default}" )
|
|
444
|
+
print_info( "\tRequired?: #{option.required?}" )
|
|
445
|
+
|
|
446
|
+
print_line( )
|
|
447
|
+
}
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
print_line( "Author:\t\t" + info[:author] )
|
|
451
|
+
print_line( "Version:\t" + info[:version] )
|
|
452
|
+
print_line( "Path:\t" + info[:path] )
|
|
453
|
+
|
|
454
|
+
print_line
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
#
|
|
460
|
+
# Outputs all available reports and their info.
|
|
461
|
+
#
|
|
462
|
+
def lsplug
|
|
463
|
+
print_line
|
|
464
|
+
print_line
|
|
465
|
+
print_info( 'Available plugins:' )
|
|
466
|
+
print_line
|
|
467
|
+
|
|
468
|
+
@arachni.lsplug().each {
|
|
469
|
+
|info|
|
|
470
|
+
|
|
471
|
+
print_status( "#{info[:plug_name]}:" )
|
|
472
|
+
print_line( "--------------------" )
|
|
473
|
+
|
|
474
|
+
print_line( "Name:\t\t" + info[:name] )
|
|
475
|
+
print_line( "Description:\t" + info[:description] )
|
|
476
|
+
|
|
477
|
+
if( info[:options] && !info[:options].empty? )
|
|
478
|
+
print_line( "Options:\t" )
|
|
479
|
+
|
|
480
|
+
info[:options].each {
|
|
481
|
+
|option|
|
|
482
|
+
print_info( "\t#{option.name} - #{option.desc}" )
|
|
483
|
+
print_info( "\tType: #{option.type}" )
|
|
484
|
+
print_info( "\tDefault: #{option.default}" )
|
|
485
|
+
print_info( "\tRequired?: #{option.required?}" )
|
|
486
|
+
|
|
487
|
+
print_line( )
|
|
488
|
+
}
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
print_line( "Author:\t\t" + info[:author] )
|
|
492
|
+
print_line( "Version:\t" + info[:version] )
|
|
493
|
+
print_line( "Path:\t" + info[:path] )
|
|
494
|
+
|
|
495
|
+
print_line
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
#
|
|
502
|
+
# Loads an Arachni Framework Profile file and merges it with the
|
|
503
|
+
# user supplied options.
|
|
504
|
+
#
|
|
505
|
+
# @param [String] filename the file to load
|
|
506
|
+
#
|
|
507
|
+
def load_profile( profiles )
|
|
508
|
+
exception_jail{
|
|
509
|
+
@opts.load_profile = nil
|
|
510
|
+
profiles.each {
|
|
511
|
+
|filename|
|
|
512
|
+
@opts.merge!( YAML::load( IO.read( filename ) ) )
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
#
|
|
518
|
+
# Saves options to an Arachni Framework Profile file.<br/>
|
|
519
|
+
# The file will be appended with the {PROFILE_EXT} extension.
|
|
520
|
+
#
|
|
521
|
+
# @param [String] filename
|
|
522
|
+
#
|
|
523
|
+
def save_profile( filename )
|
|
524
|
+
|
|
525
|
+
if filename = @opts.save( filename )
|
|
526
|
+
print_status( "Saved profile in '#{filename}'." )
|
|
527
|
+
print_line( )
|
|
528
|
+
else
|
|
529
|
+
banner( )
|
|
530
|
+
print_error( 'Could not save profile.' )
|
|
531
|
+
exit 0
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
def print_profile( )
|
|
536
|
+
print_info( 'Running profile:' )
|
|
537
|
+
print_info( @opts.to_args )
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
#
|
|
541
|
+
# Outputs Arachni banner.<br/>
|
|
542
|
+
# Displays version number, revision number, author details etc.
|
|
543
|
+
#
|
|
544
|
+
# @see VERSION
|
|
545
|
+
# @see REVISION
|
|
546
|
+
#
|
|
547
|
+
# @return [void]
|
|
548
|
+
#
|
|
549
|
+
def banner
|
|
550
|
+
|
|
551
|
+
print_line 'Arachni - Web Application Security Scanner Framework v' +
|
|
552
|
+
@arachni.version + ' [' + @arachni.revision + ']
|
|
553
|
+
Author: Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
|
554
|
+
<zapotek@segfault.gr>
|
|
555
|
+
(With the support of the community and the Arachni Team.)
|
|
556
|
+
|
|
557
|
+
Website: http://github.com/Zapotek/arachni
|
|
558
|
+
Documentation: http://github.com/Zapotek/arachni/wiki'
|
|
559
|
+
print_line
|
|
560
|
+
print_line
|
|
561
|
+
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
#
|
|
565
|
+
# Outputs help/usage information.<br/>
|
|
566
|
+
# Displays supported options and parameters.
|
|
567
|
+
#
|
|
568
|
+
# @return [void]
|
|
569
|
+
#
|
|
570
|
+
def usage
|
|
571
|
+
print_line <<USAGE
|
|
572
|
+
Usage: arachni \[options\] url
|
|
573
|
+
|
|
574
|
+
Supported options:
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
General ----------------------
|
|
578
|
+
|
|
579
|
+
-h
|
|
580
|
+
--help output this
|
|
581
|
+
|
|
582
|
+
-v be verbose
|
|
583
|
+
|
|
584
|
+
--debug show what is happening internally
|
|
585
|
+
(You should give it a shot sometime ;) )
|
|
586
|
+
|
|
587
|
+
--only-positives echo positive results *only*
|
|
588
|
+
|
|
589
|
+
--http-req-limit concurent HTTP requests limit
|
|
590
|
+
(Be carefull not to kill your server.)
|
|
591
|
+
(Default: 60)
|
|
592
|
+
(*NOTE*: If your scan seems unresponsive try lowering the limit.)
|
|
593
|
+
|
|
594
|
+
--http-harvest-last build up the HTTP request queue of the audit for the whole site
|
|
595
|
+
and harvest the HTTP responses at the end of the crawl.
|
|
596
|
+
(In some test cases this option has split the scan time in half.)
|
|
597
|
+
(Default: responses will be harvested for each page)
|
|
598
|
+
(*NOTE*: If you are scanning a high-end server and
|
|
599
|
+
you are using a powerful machine with enough bandwidth
|
|
600
|
+
*and* you feel dangerous you can use
|
|
601
|
+
this flag with an increased '--http-req-limit'
|
|
602
|
+
to get maximum performance out of your scan.)
|
|
603
|
+
(*WARNING*: When scanning large websites with hundreads
|
|
604
|
+
of pages this could eat up all your memory pretty quickly.)
|
|
605
|
+
|
|
606
|
+
--cookie-jar=<cookiejar> netscape HTTP cookie file, use curl to create it
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
--user-agent=<user agent> specify user agent
|
|
610
|
+
|
|
611
|
+
--authed-by=<who> who authorized the scan, include name and e-mail address
|
|
612
|
+
(It'll make it easier on the sys-admins during log reviews.)
|
|
613
|
+
(Will be appended to the user-agent string.)
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
Profiles -----------------------
|
|
617
|
+
|
|
618
|
+
--save-profile=<file> save the current run profile/options to <file>
|
|
619
|
+
|
|
620
|
+
--load-profile=<file> load a run profile from <file>
|
|
621
|
+
(Can be used multiple times.)
|
|
622
|
+
(You can complement it with more options, except for:
|
|
623
|
+
* --mods
|
|
624
|
+
* --redundant)
|
|
625
|
+
|
|
626
|
+
--show-profile will output the running profile as CLI arguments
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
Crawler -----------------------
|
|
630
|
+
|
|
631
|
+
-e <regex>
|
|
632
|
+
--exclude=<regex> exclude urls matching regex
|
|
633
|
+
(Can be used multiple times.)
|
|
634
|
+
|
|
635
|
+
-i <regex>
|
|
636
|
+
--include=<regex> include urls matching this regex only
|
|
637
|
+
(Can be used multiple times.)
|
|
638
|
+
|
|
639
|
+
--redundant=<regex>:<count> limit crawl on redundant pages like galleries or catalogs
|
|
640
|
+
(URLs matching <regex> will be crawled <count> links deep.)
|
|
641
|
+
(Can be used multiple times.)
|
|
642
|
+
|
|
643
|
+
-f
|
|
644
|
+
--follow-subdomains follow links to subdomains (default: off)
|
|
645
|
+
|
|
646
|
+
--obey-robots-txt obey robots.txt file (default: off)
|
|
647
|
+
|
|
648
|
+
--depth=<number> depth limit (default: inf)
|
|
649
|
+
(How deep Arachni should go into the site structure.)
|
|
650
|
+
|
|
651
|
+
--link-count=<number> how many links to follow (default: inf)
|
|
652
|
+
|
|
653
|
+
--redirect-limit=<number> how many redirects to follow (default: inf)
|
|
654
|
+
|
|
655
|
+
--spider-first spider first, audit later
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
Auditor ------------------------
|
|
659
|
+
|
|
660
|
+
-g
|
|
661
|
+
--audit-links audit link variables (GET)
|
|
662
|
+
|
|
663
|
+
-p
|
|
664
|
+
--audit-forms audit form variables
|
|
665
|
+
(usually POST, can also be GET)
|
|
666
|
+
|
|
667
|
+
-c
|
|
668
|
+
--audit-cookies audit cookies (COOKIE)
|
|
669
|
+
|
|
670
|
+
--exclude-cookie=<name> cookies not to audit
|
|
671
|
+
(You should exclude session cookies.)
|
|
672
|
+
(Can be used multiple times.)
|
|
673
|
+
|
|
674
|
+
--audit-headers audit HTTP headers
|
|
675
|
+
(*NOTE*: Header audits use brute force.
|
|
676
|
+
Almost all valid HTTP request headers will be audited
|
|
677
|
+
even if there's no indication that the web app uses them.)
|
|
678
|
+
(*WARNING*: Enabling this option will result in increased requests,
|
|
679
|
+
maybe by an order of magnitude.)
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
Modules ------------------------
|
|
683
|
+
|
|
684
|
+
--lsmod=<regexp> list available modules based on the provided regular expression
|
|
685
|
+
(If no regexp is provided all modules will be listed.)
|
|
686
|
+
(Can be used multiple times.)
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
-m <modname,modname..>
|
|
690
|
+
--mods=<modname,modname..> comma separated list of modules to deploy
|
|
691
|
+
(Use '*' as a module name to deploy all modules or inside module names like so:
|
|
692
|
+
xss_* to load all xss modules
|
|
693
|
+
sqli_* to load all sql injection modules
|
|
694
|
+
etc.
|
|
695
|
+
|
|
696
|
+
You can exclude modules by prefixing their name with a dash:
|
|
697
|
+
--mods=*,-backup_files,-xss
|
|
698
|
+
The above will load all modules except for the 'backup_files' and 'xss' modules.
|
|
699
|
+
|
|
700
|
+
Or mix and match:
|
|
701
|
+
-xss_* to unload all xss modules. )
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
Reports ------------------------
|
|
705
|
+
|
|
706
|
+
--lsrep list available reports
|
|
707
|
+
|
|
708
|
+
--repload=<file> load audit results from an .afr file
|
|
709
|
+
(Allows you to create new reports from finished scans.)
|
|
710
|
+
|
|
711
|
+
--report='<report>:<optname>=<val>,<optname2>=<val2>,...'
|
|
712
|
+
|
|
713
|
+
<report>: the name of the report as displayed by '--lsrep'
|
|
714
|
+
(Default: stdout)
|
|
715
|
+
(Can be used multiple times.)
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
Plugins ------------------------
|
|
719
|
+
|
|
720
|
+
--lsplug list available plugins
|
|
721
|
+
|
|
722
|
+
--plugin='<plugin>:<optname>=<val>,<optname2>=<val2>,...'
|
|
723
|
+
|
|
724
|
+
<plugin>: the name of the plugin as displayed by '--lsplug'
|
|
725
|
+
(Can be used multiple times.)
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
Proxy --------------------------
|
|
729
|
+
|
|
730
|
+
--proxy=<server:port> specify proxy
|
|
731
|
+
|
|
732
|
+
--proxy-auth=<user:passwd> specify proxy auth credentials
|
|
733
|
+
|
|
734
|
+
--proxy-type=<type> proxy type can be http, http_1_0, socks4, socks5, socks4a
|
|
735
|
+
(Default: http)
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
USAGE
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
end
|
|
744
|
+
end
|