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,68 @@
|
|
|
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
|
+
module Module
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# Provides output functionality to the modules via the {Arachni::UI::Output}<br/>
|
|
18
|
+
# prepending the module name to the output message.
|
|
19
|
+
#
|
|
20
|
+
# @author: Tasos "Zapotek" Laskos
|
|
21
|
+
# <tasos.laskos@gmail.com>
|
|
22
|
+
# <zapotek@segfault.gr>
|
|
23
|
+
# @version: 0.1
|
|
24
|
+
#
|
|
25
|
+
module Output
|
|
26
|
+
|
|
27
|
+
include Arachni::UI::Output
|
|
28
|
+
|
|
29
|
+
alias :o_print_error :print_error
|
|
30
|
+
alias :o_print_status :print_status
|
|
31
|
+
alias :o_print_info :print_info
|
|
32
|
+
alias :o_print_ok :print_ok
|
|
33
|
+
alias :o_print_debug :print_debug
|
|
34
|
+
alias :o_print_verbose :print_verbose
|
|
35
|
+
alias :o_print_line :print_line
|
|
36
|
+
|
|
37
|
+
def print_error( str = '' )
|
|
38
|
+
o_print_error( self.class.info[:name] + ": " + str )
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def print_status( str = '' )
|
|
42
|
+
o_print_status( self.class.info[:name] + ": " + str )
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def print_info( str = '' )
|
|
46
|
+
o_print_info( self.class.info[:name] + ": " + str )
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def print_ok( str = '' )
|
|
50
|
+
o_print_ok( self.class.info[:name] + ": " + str )
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def print_debug( str = '' )
|
|
54
|
+
o_print_debug( self.class.info[:name] + ": " + str )
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def print_verbose( str = '' )
|
|
58
|
+
o_print_verbose( self.class.info[:name] + ": " + str )
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def print_line( str = '' )
|
|
62
|
+
o_print_line( self.class.info[:name] + ": " + str )
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,240 @@
|
|
|
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'] + 'module/element_db'
|
|
12
|
+
require Arachni::Options.instance.dir['lib'] + 'module/output'
|
|
13
|
+
|
|
14
|
+
module Arachni
|
|
15
|
+
module Module
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# Trainer class
|
|
19
|
+
#
|
|
20
|
+
# Analyzes all HTTP responses looking for new auditable elements.
|
|
21
|
+
#
|
|
22
|
+
# <tasos.laskos@gmail.com>
|
|
23
|
+
# <zapotek@segfault.gr>
|
|
24
|
+
# @version: 0.2.1
|
|
25
|
+
#
|
|
26
|
+
class Trainer
|
|
27
|
+
|
|
28
|
+
include Output
|
|
29
|
+
include ElementDB
|
|
30
|
+
|
|
31
|
+
attr_writer :page
|
|
32
|
+
attr_accessor :http
|
|
33
|
+
attr_accessor :parser
|
|
34
|
+
|
|
35
|
+
def initialize
|
|
36
|
+
@opts = Options.instance
|
|
37
|
+
@updated = false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
#
|
|
41
|
+
# Passes the reponse to {#analyze} for analysis
|
|
42
|
+
#
|
|
43
|
+
# @param [Typhoeus::Response] res
|
|
44
|
+
# @param [Bool] redir was the response forcing a redirection?
|
|
45
|
+
#
|
|
46
|
+
def add_response( res, redir = false )
|
|
47
|
+
|
|
48
|
+
# non text files won't contain any auditable elements
|
|
49
|
+
type = @http.class.content_type( res.headers_hash )
|
|
50
|
+
if type.is_a?( String) && !type.substring?( 'text' )
|
|
51
|
+
return false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
@parser = Parser.new( Options.instance, res )
|
|
55
|
+
@parser.url = @page.url
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
url = res.effective_url
|
|
59
|
+
url = URI( to_absolute( url ) )
|
|
60
|
+
|
|
61
|
+
return if !follow?( url )
|
|
62
|
+
return if ( redir && !follow?( url ) )
|
|
63
|
+
|
|
64
|
+
analyze( [ res, redir ] )
|
|
65
|
+
|
|
66
|
+
rescue Exception => e
|
|
67
|
+
print_error( "Invalid URL, probably broken redirection. Ignoring..." )
|
|
68
|
+
raise e
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
# Decodes URLs to reverse multiple encodes and removes NULL characters
|
|
75
|
+
#
|
|
76
|
+
def url_sanitize( url )
|
|
77
|
+
|
|
78
|
+
while( url =~ /%/ )
|
|
79
|
+
url = ( URI.decode( url ).to_s.unpack( 'A*' )[0] )
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
return URI.encode( url )
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def follow?( url )
|
|
86
|
+
@parser.url = @page.url
|
|
87
|
+
|
|
88
|
+
return false if !@parser.in_domain?( url )
|
|
89
|
+
return false if @parser.exclude?( url )
|
|
90
|
+
return false if !@parser.include?( url )
|
|
91
|
+
return true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
#
|
|
95
|
+
# Returns an updated {Arachni::Parser::Page} object or nil if there waere no updates
|
|
96
|
+
#
|
|
97
|
+
# @return [Page]
|
|
98
|
+
#
|
|
99
|
+
def page
|
|
100
|
+
if( @updated )
|
|
101
|
+
@updated = false
|
|
102
|
+
return @page
|
|
103
|
+
else
|
|
104
|
+
return nil
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
#
|
|
110
|
+
# Analyzes a response looking for new links, forms and cookies.
|
|
111
|
+
#
|
|
112
|
+
# @param [Typhoeus::Response, Bool] res
|
|
113
|
+
#
|
|
114
|
+
def analyze( res )
|
|
115
|
+
|
|
116
|
+
print_debug( 'Started for response with request ID: #' +
|
|
117
|
+
res[0].request.id.to_s )
|
|
118
|
+
|
|
119
|
+
@parser.url = res[0].effective_url.clone
|
|
120
|
+
|
|
121
|
+
train_cookies( res[0] )
|
|
122
|
+
|
|
123
|
+
# if the response body is the same as the page body and
|
|
124
|
+
# no new cookies have appeared there's no reason to analyze the page
|
|
125
|
+
if( res[0].body == @page.html && !@updated )
|
|
126
|
+
print_debug( 'Page hasn\'t changed, skipping...' )
|
|
127
|
+
return
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
train_forms( res[0] )
|
|
131
|
+
train_links( res[0], res[1] )
|
|
132
|
+
|
|
133
|
+
if( @updated )
|
|
134
|
+
@page.html = res[0].body.dup
|
|
135
|
+
|
|
136
|
+
begin
|
|
137
|
+
url = res[0].request.url
|
|
138
|
+
# prepare the page url
|
|
139
|
+
@parser.url = to_absolute( url )
|
|
140
|
+
rescue Exception => e
|
|
141
|
+
print_error( "Invalid URL, probably broken redirection. Ignoring..." )
|
|
142
|
+
# raise e
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
@page.response_headers = res[0].headers_hash
|
|
146
|
+
@page.query_vars = @parser.link_vars( @parser.url ).dup
|
|
147
|
+
@page.url = @parser.url.dup
|
|
148
|
+
@page.code = res[0].code
|
|
149
|
+
@page.method = res[0].request.method.to_s.upcase
|
|
150
|
+
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
print_debug( 'Training complete.' )
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
private
|
|
157
|
+
|
|
158
|
+
def to_absolute( url )
|
|
159
|
+
effective_url = url_sanitize( url )
|
|
160
|
+
@page.url = url_sanitize( @page.url )
|
|
161
|
+
|
|
162
|
+
begin
|
|
163
|
+
# prepare the page url
|
|
164
|
+
return (URI.parse( @page.url ).merge( URI( effective_url ) )).to_s.dup
|
|
165
|
+
rescue
|
|
166
|
+
# worst case scenario
|
|
167
|
+
return @page.url
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def train_forms( res )
|
|
172
|
+
return [], 0 if !@opts.audit_forms
|
|
173
|
+
|
|
174
|
+
# @parser.url = res.effective_url.clone
|
|
175
|
+
forms = @parser.forms( ).clone
|
|
176
|
+
cforms, form_cnt = update_forms( forms )
|
|
177
|
+
|
|
178
|
+
if ( form_cnt > 0 )
|
|
179
|
+
@page.forms = cforms.flatten
|
|
180
|
+
@updated = true
|
|
181
|
+
|
|
182
|
+
print_debug( 'Found ' + form_cnt.to_s + ' new forms.' )
|
|
183
|
+
else
|
|
184
|
+
@page.forms = forms
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def train_links( res, redir = false )
|
|
190
|
+
return [], 0 if !@opts.audit_links
|
|
191
|
+
|
|
192
|
+
# @parser.url = res.effective_url.clone
|
|
193
|
+
|
|
194
|
+
links = @parser.links.clone
|
|
195
|
+
|
|
196
|
+
if( redir )
|
|
197
|
+
url = to_absolute( res.effective_url )
|
|
198
|
+
links << Arachni::Parser::Element::Link.new( url, {
|
|
199
|
+
'href' => url,
|
|
200
|
+
'vars' => @parser.link_vars( url )
|
|
201
|
+
} )
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
clinks, link_cnt = update_links( links )
|
|
205
|
+
|
|
206
|
+
if ( link_cnt > 0 )
|
|
207
|
+
@page.links = clinks.flatten
|
|
208
|
+
@updated = true
|
|
209
|
+
|
|
210
|
+
print_debug( 'Found ' + link_cnt.to_s + ' new links.' )
|
|
211
|
+
else
|
|
212
|
+
@page.links = links
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def train_cookies( res )
|
|
218
|
+
|
|
219
|
+
cookies = @parser.cookies.clone
|
|
220
|
+
ccookies, cookie_cnt = update_cookies( cookies )
|
|
221
|
+
|
|
222
|
+
if ( cookie_cnt > 0 )
|
|
223
|
+
@page.cookies = ccookies.flatten
|
|
224
|
+
@updated = true
|
|
225
|
+
|
|
226
|
+
print_debug( 'Found ' + cookie_cnt.to_s + ' new cookies.' )
|
|
227
|
+
else
|
|
228
|
+
@page.cookies = cookies
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def self.info
|
|
235
|
+
{ :name => 'Trainer' }
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Arachni
|
|
3
|
+
Copyright (c) 2010-2011 Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
|
4
|
+
|
|
5
|
+
This is free software; you can copy and distribute and modify
|
|
6
|
+
this program under the term of the GPL v2.0 License
|
|
7
|
+
(See LICENSE file for details)
|
|
8
|
+
|
|
9
|
+
=end
|
|
10
|
+
|
|
11
|
+
module Arachni
|
|
12
|
+
module Module
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
#
|
|
16
|
+
# Utilities class
|
|
17
|
+
#
|
|
18
|
+
# Includes some useful methods for the system, the modules etc...
|
|
19
|
+
#
|
|
20
|
+
# @author: Tasos "Zapotek" Laskos
|
|
21
|
+
# <tasos.laskos@gmail.com>
|
|
22
|
+
# <zapotek@segfault.gr>
|
|
23
|
+
# @version: 0.1.1
|
|
24
|
+
#
|
|
25
|
+
module Utilities
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# Gets path from URL
|
|
29
|
+
#
|
|
30
|
+
# @param [String] url
|
|
31
|
+
#
|
|
32
|
+
# @return [String] path
|
|
33
|
+
#
|
|
34
|
+
def get_path( url )
|
|
35
|
+
|
|
36
|
+
uri = URI( URI.escape( url ) )
|
|
37
|
+
path = uri.path
|
|
38
|
+
|
|
39
|
+
if !File.extname( path ).empty?
|
|
40
|
+
path = File.dirname( path )
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
path << '/' if path[-1] != '/'
|
|
44
|
+
return uri.scheme + "://" + uri.host + path
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def seed
|
|
48
|
+
@@seed ||= Digest::SHA2.hexdigest( srand( 1000 ).to_s )
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def normalize_url( url )
|
|
52
|
+
begin
|
|
53
|
+
return URI.encode( URI.decode( url.to_s ) ).to_s.gsub( '[', '%5B' ).gsub( ']', '%5D' )
|
|
54
|
+
rescue
|
|
55
|
+
begin
|
|
56
|
+
return URI.encode( URI.decode( url.to_s ) ).to_s
|
|
57
|
+
rescue
|
|
58
|
+
return url
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
#
|
|
64
|
+
# Gets module data files from 'modules/[modtype]/[modname]/[filename]'
|
|
65
|
+
#
|
|
66
|
+
# @param [String] filename filename, without the path
|
|
67
|
+
# @param [Block] the block to be passed each line as it's read
|
|
68
|
+
#
|
|
69
|
+
def read_file( filename, &block )
|
|
70
|
+
|
|
71
|
+
# the path of the module that called us
|
|
72
|
+
mod_path = block.source_location[0]
|
|
73
|
+
|
|
74
|
+
# the name of the module that called us
|
|
75
|
+
mod_name = File.basename( mod_path, ".rb")
|
|
76
|
+
|
|
77
|
+
# the path to the module's data file directory
|
|
78
|
+
path = File.expand_path( File.dirname( mod_path ) ) +
|
|
79
|
+
'/' + mod_name + '/'
|
|
80
|
+
|
|
81
|
+
file = File.open( path + '/' + filename ).each {
|
|
82
|
+
|line|
|
|
83
|
+
yield line.strip
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
file.close
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
#
|
|
91
|
+
# Wraps the "block" in exception handling code and runs it.
|
|
92
|
+
#
|
|
93
|
+
# @param [Block]
|
|
94
|
+
#
|
|
95
|
+
def exception_jail( &block )
|
|
96
|
+
begin
|
|
97
|
+
block.call
|
|
98
|
+
rescue Exception => e
|
|
99
|
+
print_error( e.to_s )
|
|
100
|
+
print_debug_backtrace( e )
|
|
101
|
+
raise e
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
extend self
|
|
106
|
+
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
110
|
+
end
|
data/lib/options.rb
ADDED
|
@@ -0,0 +1,547 @@
|
|
|
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 'singleton'
|
|
12
|
+
|
|
13
|
+
module Arachni
|
|
14
|
+
|
|
15
|
+
#
|
|
16
|
+
# Options class.
|
|
17
|
+
#
|
|
18
|
+
# Implements the Singleton pattern and formaly defines
|
|
19
|
+
# all of Arachni's runtime options.
|
|
20
|
+
#
|
|
21
|
+
# @author: Tasos "Zapotek" Laskos
|
|
22
|
+
# <tasos.laskos@gmail.com>
|
|
23
|
+
# <zapotek@segfault.gr>
|
|
24
|
+
# @version: 0.1.1
|
|
25
|
+
#
|
|
26
|
+
class Options
|
|
27
|
+
|
|
28
|
+
include Singleton
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# The extension of the profile files.
|
|
32
|
+
#
|
|
33
|
+
# @return [String]
|
|
34
|
+
#
|
|
35
|
+
PROFILE_EXT = '.afp'
|
|
36
|
+
|
|
37
|
+
#
|
|
38
|
+
# Holds absolute paths for the directory structure of the framework
|
|
39
|
+
#
|
|
40
|
+
# @return [Hash]
|
|
41
|
+
#
|
|
42
|
+
attr_accessor :dir
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# The URL to audit
|
|
46
|
+
#
|
|
47
|
+
# @return [String,URI]
|
|
48
|
+
#
|
|
49
|
+
attr_accessor :url
|
|
50
|
+
|
|
51
|
+
#
|
|
52
|
+
# Show help?
|
|
53
|
+
#
|
|
54
|
+
# @return [Bool]
|
|
55
|
+
#
|
|
56
|
+
attr_accessor :help
|
|
57
|
+
|
|
58
|
+
#
|
|
59
|
+
# Output only positive results during the audit?
|
|
60
|
+
#
|
|
61
|
+
# @return [Bool]
|
|
62
|
+
#
|
|
63
|
+
attr_accessor :only_positives
|
|
64
|
+
|
|
65
|
+
#
|
|
66
|
+
# Be verbose?
|
|
67
|
+
#
|
|
68
|
+
# @return [Bool]
|
|
69
|
+
#
|
|
70
|
+
attr_accessor :arachni_verbose
|
|
71
|
+
|
|
72
|
+
#
|
|
73
|
+
# Output debugging messages?
|
|
74
|
+
#
|
|
75
|
+
# @return [Bool]
|
|
76
|
+
#
|
|
77
|
+
attr_accessor :debug
|
|
78
|
+
|
|
79
|
+
#
|
|
80
|
+
# Filters for redundant links
|
|
81
|
+
#
|
|
82
|
+
# @return [Array]
|
|
83
|
+
#
|
|
84
|
+
attr_accessor :redundant
|
|
85
|
+
|
|
86
|
+
#
|
|
87
|
+
# Should the crawler obery robots.txt files?
|
|
88
|
+
#
|
|
89
|
+
# @return [Bool]
|
|
90
|
+
#
|
|
91
|
+
attr_accessor :obey_robots_txt
|
|
92
|
+
|
|
93
|
+
#
|
|
94
|
+
# How deep to go in the site structure?<br/>
|
|
95
|
+
# If nil, depth_limit = inf
|
|
96
|
+
#
|
|
97
|
+
# @return [Integer]
|
|
98
|
+
#
|
|
99
|
+
attr_accessor :depth_limit
|
|
100
|
+
|
|
101
|
+
#
|
|
102
|
+
# How many links to follow?
|
|
103
|
+
# If nil, link_count_limit = inf
|
|
104
|
+
#
|
|
105
|
+
# @return [Integer]
|
|
106
|
+
#
|
|
107
|
+
attr_accessor :link_count_limit
|
|
108
|
+
|
|
109
|
+
#
|
|
110
|
+
# How many redirects to follow?
|
|
111
|
+
# If nil, redirect_limit = inf
|
|
112
|
+
#
|
|
113
|
+
# @return [Integer]
|
|
114
|
+
#
|
|
115
|
+
attr_accessor :redirect_limit
|
|
116
|
+
|
|
117
|
+
#
|
|
118
|
+
# List modules, based on regexps, and exit?
|
|
119
|
+
#
|
|
120
|
+
# @return [Array<Regexp>]
|
|
121
|
+
#
|
|
122
|
+
attr_accessor :lsmod
|
|
123
|
+
|
|
124
|
+
#
|
|
125
|
+
# List reports and exit?
|
|
126
|
+
#
|
|
127
|
+
# @return [Bool]
|
|
128
|
+
#
|
|
129
|
+
attr_accessor :lsrep
|
|
130
|
+
|
|
131
|
+
#
|
|
132
|
+
# How many concurrent HTTP requests?
|
|
133
|
+
#
|
|
134
|
+
# @return [Integer]
|
|
135
|
+
#
|
|
136
|
+
attr_accessor :http_req_limit
|
|
137
|
+
|
|
138
|
+
#
|
|
139
|
+
# Should Arachni audit links?
|
|
140
|
+
#
|
|
141
|
+
# @return [Bool]
|
|
142
|
+
#
|
|
143
|
+
attr_accessor :audit_links
|
|
144
|
+
|
|
145
|
+
#
|
|
146
|
+
# Should Arachni audit forms?
|
|
147
|
+
#
|
|
148
|
+
# @return [Bool]
|
|
149
|
+
#
|
|
150
|
+
attr_accessor :audit_forms
|
|
151
|
+
|
|
152
|
+
#
|
|
153
|
+
# Should Arachni audit cookies?
|
|
154
|
+
#
|
|
155
|
+
# @return [Bool]
|
|
156
|
+
#
|
|
157
|
+
attr_accessor :audit_cookies
|
|
158
|
+
|
|
159
|
+
#
|
|
160
|
+
# Should Arachni audit HTTP headers?
|
|
161
|
+
#
|
|
162
|
+
# @return [Bool]
|
|
163
|
+
#
|
|
164
|
+
attr_accessor :audit_headers
|
|
165
|
+
|
|
166
|
+
#
|
|
167
|
+
# Array of modules to load
|
|
168
|
+
#
|
|
169
|
+
# @return [Array]
|
|
170
|
+
#
|
|
171
|
+
attr_accessor :mods
|
|
172
|
+
|
|
173
|
+
#
|
|
174
|
+
# Array of reports to load
|
|
175
|
+
#
|
|
176
|
+
# @return [Array]
|
|
177
|
+
#
|
|
178
|
+
attr_accessor :reports
|
|
179
|
+
|
|
180
|
+
#
|
|
181
|
+
# Location of an Arachni Framework Report (.afr) file to load
|
|
182
|
+
#
|
|
183
|
+
# @return [String]
|
|
184
|
+
#
|
|
185
|
+
attr_accessor :repload
|
|
186
|
+
|
|
187
|
+
#
|
|
188
|
+
# Where to save the Arachni Framework Profile (.afp) file
|
|
189
|
+
#
|
|
190
|
+
# @return [String]
|
|
191
|
+
#
|
|
192
|
+
attr_accessor :save_profile
|
|
193
|
+
|
|
194
|
+
#
|
|
195
|
+
# Location of Arachni Framework Profile (.afp) files to load
|
|
196
|
+
#
|
|
197
|
+
# @return [Array]
|
|
198
|
+
#
|
|
199
|
+
attr_accessor :load_profile
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
attr_accessor :show_profile
|
|
203
|
+
|
|
204
|
+
#
|
|
205
|
+
# The person that authorized the scan<br/>
|
|
206
|
+
# It will be added to the HTTP "user-agent" and "from" headers.
|
|
207
|
+
#
|
|
208
|
+
# @return [String]
|
|
209
|
+
#
|
|
210
|
+
attr_accessor :authed_by
|
|
211
|
+
|
|
212
|
+
#
|
|
213
|
+
# The address of the proxy server
|
|
214
|
+
#
|
|
215
|
+
# @return [String]
|
|
216
|
+
#
|
|
217
|
+
attr_accessor :proxy_addr
|
|
218
|
+
|
|
219
|
+
#
|
|
220
|
+
# The port to connect on the proxy server
|
|
221
|
+
#
|
|
222
|
+
# @return [String]
|
|
223
|
+
#
|
|
224
|
+
attr_accessor :proxy_port
|
|
225
|
+
|
|
226
|
+
#
|
|
227
|
+
# The proxy password
|
|
228
|
+
#
|
|
229
|
+
# @return [String]
|
|
230
|
+
#
|
|
231
|
+
attr_accessor :proxy_pass
|
|
232
|
+
|
|
233
|
+
#
|
|
234
|
+
# The proxy user
|
|
235
|
+
#
|
|
236
|
+
# @return [String]
|
|
237
|
+
#
|
|
238
|
+
attr_accessor :proxy_user
|
|
239
|
+
|
|
240
|
+
#
|
|
241
|
+
# The proxy type
|
|
242
|
+
#
|
|
243
|
+
# @return [String] [http, socks]
|
|
244
|
+
#
|
|
245
|
+
attr_accessor :proxy_type
|
|
246
|
+
|
|
247
|
+
#
|
|
248
|
+
# To be populated by the framework
|
|
249
|
+
#
|
|
250
|
+
# Parsed cookiejar cookies
|
|
251
|
+
#
|
|
252
|
+
# @return [Hash] name=>value pairs
|
|
253
|
+
#
|
|
254
|
+
attr_accessor :cookies
|
|
255
|
+
|
|
256
|
+
#
|
|
257
|
+
# Location of the cookiejar
|
|
258
|
+
#
|
|
259
|
+
# @return [String]
|
|
260
|
+
#
|
|
261
|
+
attr_accessor :cookie_jar
|
|
262
|
+
|
|
263
|
+
#
|
|
264
|
+
# The HTTP user-agent to use
|
|
265
|
+
#
|
|
266
|
+
# @return [String]
|
|
267
|
+
#
|
|
268
|
+
attr_accessor :user_agent
|
|
269
|
+
|
|
270
|
+
#
|
|
271
|
+
# Exclude filters <br/>
|
|
272
|
+
# URL matching any of these patterns won't be followed
|
|
273
|
+
#
|
|
274
|
+
# @return [Array]
|
|
275
|
+
#
|
|
276
|
+
attr_accessor :exclude
|
|
277
|
+
|
|
278
|
+
#
|
|
279
|
+
# Cookies to exclude from audit<br/>
|
|
280
|
+
#
|
|
281
|
+
# @return [Array]
|
|
282
|
+
#
|
|
283
|
+
attr_accessor :exclude_cookies
|
|
284
|
+
|
|
285
|
+
#
|
|
286
|
+
# Include filters <br/>
|
|
287
|
+
# Only URLs that match any of these patterns will be followed
|
|
288
|
+
#
|
|
289
|
+
# @return [Array]
|
|
290
|
+
#
|
|
291
|
+
attr_accessor :include
|
|
292
|
+
|
|
293
|
+
#
|
|
294
|
+
# Should the crawler follow subdomains?
|
|
295
|
+
#
|
|
296
|
+
# @return [Bool]
|
|
297
|
+
#
|
|
298
|
+
attr_accessor :follow_subdomains
|
|
299
|
+
|
|
300
|
+
#
|
|
301
|
+
# Harvest the HTTP responses for the whole site at the end or
|
|
302
|
+
# for each page?
|
|
303
|
+
#
|
|
304
|
+
# @return [Bool]
|
|
305
|
+
#
|
|
306
|
+
attr_accessor :http_harvest_last
|
|
307
|
+
|
|
308
|
+
# to be populated by the framework
|
|
309
|
+
attr_accessor :start_datetime
|
|
310
|
+
# to be populated by the framework
|
|
311
|
+
attr_accessor :finish_datetime
|
|
312
|
+
# to be populated by the framework
|
|
313
|
+
attr_accessor :delta_time
|
|
314
|
+
|
|
315
|
+
attr_accessor :lsplug
|
|
316
|
+
attr_accessor :plugins
|
|
317
|
+
|
|
318
|
+
attr_accessor :spider_first
|
|
319
|
+
|
|
320
|
+
attr_accessor :rpc_port
|
|
321
|
+
attr_accessor :ssl
|
|
322
|
+
attr_accessor :ssl_pkey
|
|
323
|
+
attr_accessor :ssl_cert
|
|
324
|
+
attr_accessor :ssl_ca
|
|
325
|
+
|
|
326
|
+
attr_accessor :server
|
|
327
|
+
|
|
328
|
+
attr_accessor :reroute_to_logfile
|
|
329
|
+
attr_accessor :pool_size
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def initialize( )
|
|
333
|
+
|
|
334
|
+
# nil everything out
|
|
335
|
+
self.instance_variables.each {
|
|
336
|
+
|var|
|
|
337
|
+
instance_variable_set( var.to_s, nil )
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@exclude = []
|
|
341
|
+
@include = []
|
|
342
|
+
@redundant = []
|
|
343
|
+
|
|
344
|
+
@reports = {}
|
|
345
|
+
@lsrep = []
|
|
346
|
+
|
|
347
|
+
@lsmod = []
|
|
348
|
+
@dir = Hash.new
|
|
349
|
+
@exclude_cookies = []
|
|
350
|
+
@load_profile = []
|
|
351
|
+
|
|
352
|
+
@plugins = {}
|
|
353
|
+
@lsplug = []
|
|
354
|
+
|
|
355
|
+
# set some defaults
|
|
356
|
+
@redirect_limit = 20
|
|
357
|
+
|
|
358
|
+
# relatively low but will give good performance without bottleneck
|
|
359
|
+
# on low bandwidth conections
|
|
360
|
+
@http_req_limit = 20
|
|
361
|
+
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
#
|
|
365
|
+
# Saves 'self' to file
|
|
366
|
+
#
|
|
367
|
+
# @param [String] file
|
|
368
|
+
#
|
|
369
|
+
def save( file )
|
|
370
|
+
|
|
371
|
+
dir = @dir.clone
|
|
372
|
+
load_profile = @load_profile.clone if @load_profile
|
|
373
|
+
save_profile = @save_profile.clone if @save_profile
|
|
374
|
+
authed_by = @authed_by.clone if @authed_by
|
|
375
|
+
|
|
376
|
+
@dir = nil
|
|
377
|
+
@load_profile = nil
|
|
378
|
+
@save_profile = nil
|
|
379
|
+
@authed_by = nil
|
|
380
|
+
|
|
381
|
+
begin
|
|
382
|
+
f = File.open( file + PROFILE_EXT, 'w' )
|
|
383
|
+
YAML.dump( self, f )
|
|
384
|
+
rescue
|
|
385
|
+
return
|
|
386
|
+
ensure
|
|
387
|
+
f.close
|
|
388
|
+
|
|
389
|
+
@dir = dir
|
|
390
|
+
@load_profile = load_profile
|
|
391
|
+
@save_profile = save_profile
|
|
392
|
+
@authed_by = authed_by
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
return f.path
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def url=( str )
|
|
399
|
+
return if !str
|
|
400
|
+
|
|
401
|
+
require 'uri'
|
|
402
|
+
require self.dir['lib'] + 'exceptions'
|
|
403
|
+
require self.dir['lib'] + 'module/utilities'
|
|
404
|
+
|
|
405
|
+
begin
|
|
406
|
+
@url = URI( Arachni::Module::Utilities.normalize_url( str.to_s ) )
|
|
407
|
+
rescue
|
|
408
|
+
raise( Arachni::Exceptions::InvalidURL, "Invalid URL argument." )
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
return str
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
#
|
|
415
|
+
# Converts the Options object to hash
|
|
416
|
+
#
|
|
417
|
+
# @return [Hash]
|
|
418
|
+
#
|
|
419
|
+
def to_h
|
|
420
|
+
hash = Hash.new
|
|
421
|
+
self.instance_variables.each {
|
|
422
|
+
|var|
|
|
423
|
+
hash[normalize_name( var )] = self.instance_variable_get( var )
|
|
424
|
+
}
|
|
425
|
+
hash
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
#
|
|
429
|
+
# Merges self with the object in 'options'
|
|
430
|
+
#
|
|
431
|
+
# @param [Options]
|
|
432
|
+
#
|
|
433
|
+
def merge!( options )
|
|
434
|
+
options.to_h.each_pair {
|
|
435
|
+
|k, v|
|
|
436
|
+
|
|
437
|
+
next if ( v.is_a?( Array ) || v.is_a?( Hash ) ) && v.empty?
|
|
438
|
+
send( "#{k}=", v ) if v
|
|
439
|
+
}
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def to_args
|
|
443
|
+
|
|
444
|
+
cli_args = ''
|
|
445
|
+
|
|
446
|
+
self.to_h.keys.each {
|
|
447
|
+
|key|
|
|
448
|
+
|
|
449
|
+
arg = self.to_arg( key )
|
|
450
|
+
|
|
451
|
+
cli_args += " #{arg.to_s}" if arg
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return cli_args += " #{self.url}"
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def to_arg( key )
|
|
458
|
+
|
|
459
|
+
var = self.instance_variable_get( "@#{key}" )
|
|
460
|
+
|
|
461
|
+
return if !var
|
|
462
|
+
return if ( var.is_a?( Array ) || var.is_a?( Hash ) ) && var.empty?
|
|
463
|
+
return if key == 'show_profile'
|
|
464
|
+
return if key == 'url'
|
|
465
|
+
return if key == 'dir'
|
|
466
|
+
return if key == 'include' && var == [/.*/]
|
|
467
|
+
return if key == 'reports' && var == ['stdout']
|
|
468
|
+
|
|
469
|
+
key = 'exclude_cookie' if key == 'exclude_cookies'
|
|
470
|
+
key = 'report' if key == 'reports'
|
|
471
|
+
|
|
472
|
+
key = key.gsub( '_', '-' )
|
|
473
|
+
|
|
474
|
+
arg = ''
|
|
475
|
+
|
|
476
|
+
case key
|
|
477
|
+
|
|
478
|
+
when 'mods'
|
|
479
|
+
var = var.join( ',' )
|
|
480
|
+
|
|
481
|
+
when 'arachni-verbose'
|
|
482
|
+
key = 'verbosity'
|
|
483
|
+
|
|
484
|
+
when 'redundant'
|
|
485
|
+
var.each {
|
|
486
|
+
|rule|
|
|
487
|
+
arg += " --#{key}=#{rule['regexp'].source}:#{rule['count']}"
|
|
488
|
+
}
|
|
489
|
+
return arg
|
|
490
|
+
|
|
491
|
+
when 'plugins','report'
|
|
492
|
+
arg = ''
|
|
493
|
+
var.each {
|
|
494
|
+
|opt, val|
|
|
495
|
+
arg += " --#{key.chomp( 's' )}=#{opt}"
|
|
496
|
+
arg += ':' if !val.empty?
|
|
497
|
+
|
|
498
|
+
val.each {
|
|
499
|
+
|k, v|
|
|
500
|
+
arg += "#{k}=#{v},"
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
arg.chomp!( ',' )
|
|
504
|
+
}
|
|
505
|
+
return arg
|
|
506
|
+
|
|
507
|
+
when 'proxy-port'
|
|
508
|
+
return
|
|
509
|
+
|
|
510
|
+
when 'proxy-addr'
|
|
511
|
+
return "--proxy=#{self.proxy_addr}:#{self.proxy_port}"
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
if( var.is_a?( TrueClass ) )
|
|
517
|
+
arg = "--#{key}"
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
if( var.is_a?( String ) || var.is_a?( Fixnum ) )
|
|
521
|
+
arg = "--#{key}=#{var.to_s}"
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
if( var.is_a?( Array ) )
|
|
525
|
+
|
|
526
|
+
var.each {
|
|
527
|
+
|i|
|
|
528
|
+
|
|
529
|
+
i = i.source if i.is_a?( Regexp )
|
|
530
|
+
|
|
531
|
+
arg += " --#{key}=#{i}"
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
return arg
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
private
|
|
540
|
+
|
|
541
|
+
def normalize_name( name )
|
|
542
|
+
name.to_s.gsub( '@', '' )
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
end
|
|
547
|
+
end
|