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.
Files changed (262) hide show
  1. data/ACKNOWLEDGMENTS.md +14 -0
  2. data/AUTHORS.md +6 -0
  3. data/CHANGELOG.md +162 -0
  4. data/CONTRIBUTORS.md +10 -0
  5. data/EXPLOITATION.md +429 -0
  6. data/HACKING.md +101 -0
  7. data/LICENSE.md +341 -0
  8. data/README.md +350 -0
  9. data/Rakefile +86 -0
  10. data/bin/arachni +22 -0
  11. data/bin/arachni_web +77 -0
  12. data/bin/arachni_xmlrpc +21 -0
  13. data/bin/arachni_xmlrpcd +82 -0
  14. data/bin/arachni_xmlrpcd_monitor +74 -0
  15. data/conf/README.webui.yaml.txt +44 -0
  16. data/conf/webui.yaml +11 -0
  17. data/external/metasploit/LICENSE +24 -0
  18. data/external/metasploit/modules/exploits/unix/webapp/arachni_exec.rb +142 -0
  19. data/external/metasploit/modules/exploits/unix/webapp/arachni_path_traversal.rb +113 -0
  20. data/external/metasploit/modules/exploits/unix/webapp/arachni_php_eval.rb +150 -0
  21. data/external/metasploit/modules/exploits/unix/webapp/arachni_php_include.rb +141 -0
  22. data/external/metasploit/modules/exploits/unix/webapp/arachni_sqlmap.rb +92 -0
  23. data/external/metasploit/plugins/arachni.rb +536 -0
  24. data/getoptslong.rb +241 -0
  25. data/lib/anemone.rb +2 -0
  26. data/lib/anemone/cookie_store.rb +35 -0
  27. data/lib/anemone/core.rb +371 -0
  28. data/lib/anemone/exceptions.rb +5 -0
  29. data/lib/anemone/http.rb +144 -0
  30. data/lib/anemone/page.rb +337 -0
  31. data/lib/anemone/page_store.rb +160 -0
  32. data/lib/anemone/storage.rb +34 -0
  33. data/lib/anemone/storage/base.rb +75 -0
  34. data/lib/anemone/storage/exceptions.rb +15 -0
  35. data/lib/anemone/storage/mongodb.rb +89 -0
  36. data/lib/anemone/storage/pstore.rb +50 -0
  37. data/lib/anemone/storage/redis.rb +90 -0
  38. data/lib/anemone/storage/tokyo_cabinet.rb +57 -0
  39. data/lib/anemone/tentacle.rb +40 -0
  40. data/lib/arachni.rb +16 -0
  41. data/lib/audit_store.rb +346 -0
  42. data/lib/component_manager.rb +293 -0
  43. data/lib/component_options.rb +395 -0
  44. data/lib/exceptions.rb +76 -0
  45. data/lib/framework.rb +637 -0
  46. data/lib/http.rb +809 -0
  47. data/lib/issue.rb +302 -0
  48. data/lib/module.rb +4 -0
  49. data/lib/module/auditor.rb +455 -0
  50. data/lib/module/base.rb +188 -0
  51. data/lib/module/element_db.rb +158 -0
  52. data/lib/module/key_filler.rb +87 -0
  53. data/lib/module/manager.rb +87 -0
  54. data/lib/module/output.rb +68 -0
  55. data/lib/module/trainer.rb +240 -0
  56. data/lib/module/utilities.rb +110 -0
  57. data/lib/options.rb +547 -0
  58. data/lib/parser.rb +2 -0
  59. data/lib/parser/auditable.rb +522 -0
  60. data/lib/parser/elements.rb +296 -0
  61. data/lib/parser/page.rb +149 -0
  62. data/lib/parser/parser.rb +717 -0
  63. data/lib/plugin.rb +4 -0
  64. data/lib/plugin/base.rb +110 -0
  65. data/lib/plugin/manager.rb +162 -0
  66. data/lib/report.rb +4 -0
  67. data/lib/report/base.rb +119 -0
  68. data/lib/report/manager.rb +92 -0
  69. data/lib/rpc/xml/client/base.rb +71 -0
  70. data/lib/rpc/xml/client/dispatcher.rb +49 -0
  71. data/lib/rpc/xml/client/instance.rb +88 -0
  72. data/lib/rpc/xml/server/base.rb +90 -0
  73. data/lib/rpc/xml/server/dispatcher.rb +357 -0
  74. data/lib/rpc/xml/server/framework.rb +206 -0
  75. data/lib/rpc/xml/server/instance.rb +191 -0
  76. data/lib/rpc/xml/server/module/manager.rb +46 -0
  77. data/lib/rpc/xml/server/options.rb +124 -0
  78. data/lib/rpc/xml/server/output.rb +299 -0
  79. data/lib/rpc/xml/server/plugin/manager.rb +58 -0
  80. data/lib/ruby.rb +5 -0
  81. data/lib/ruby/object.rb +32 -0
  82. data/lib/ruby/string.rb +74 -0
  83. data/lib/ruby/xmlrpc/server.rb +27 -0
  84. data/lib/spider.rb +200 -0
  85. data/lib/typhoeus/request.rb +91 -0
  86. data/lib/typhoeus/response.rb +34 -0
  87. data/lib/ui/cli/cli.rb +744 -0
  88. data/lib/ui/cli/output.rb +279 -0
  89. data/lib/ui/web/log.rb +82 -0
  90. data/lib/ui/web/output_stream.rb +94 -0
  91. data/lib/ui/web/report_manager.rb +222 -0
  92. data/lib/ui/web/server.rb +903 -0
  93. data/lib/ui/web/server/db/placeholder +0 -0
  94. data/lib/ui/web/server/public/banner.png +0 -0
  95. data/lib/ui/web/server/public/bodybg-small.png +0 -0
  96. data/lib/ui/web/server/public/bodybg.png +0 -0
  97. data/lib/ui/web/server/public/css/smoothness/images/pbar-ani.gif +0 -0
  98. data/lib/ui/web/server/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  99. data/lib/ui/web/server/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  100. data/lib/ui/web/server/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  101. data/lib/ui/web/server/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  102. data/lib/ui/web/server/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  103. data/lib/ui/web/server/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  104. data/lib/ui/web/server/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  105. data/lib/ui/web/server/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  106. data/lib/ui/web/server/public/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
  107. data/lib/ui/web/server/public/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  108. data/lib/ui/web/server/public/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
  109. data/lib/ui/web/server/public/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
  110. data/lib/ui/web/server/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  111. data/lib/ui/web/server/public/css/smoothness/jquery-ui-1.8.9.custom.css +573 -0
  112. data/lib/ui/web/server/public/favicon.ico +0 -0
  113. data/lib/ui/web/server/public/footer.jpg +0 -0
  114. data/lib/ui/web/server/public/icons/error.png +0 -0
  115. data/lib/ui/web/server/public/icons/info.png +0 -0
  116. data/lib/ui/web/server/public/icons/ok.png +0 -0
  117. data/lib/ui/web/server/public/icons/status.png +0 -0
  118. data/lib/ui/web/server/public/js/jquery-1.4.4.min.js +167 -0
  119. data/lib/ui/web/server/public/js/jquery-ui-1.8.9.custom.min.js +781 -0
  120. data/lib/ui/web/server/public/logo.png +0 -0
  121. data/lib/ui/web/server/public/nav-left.jpg +0 -0
  122. data/lib/ui/web/server/public/nav-right.jpg +0 -0
  123. data/lib/ui/web/server/public/nav-selected-left.jpg +0 -0
  124. data/lib/ui/web/server/public/nav-selected-right.jpg +0 -0
  125. data/lib/ui/web/server/public/reports/placeholder +1 -0
  126. data/lib/ui/web/server/public/sidebar-bottom.jpg +0 -0
  127. data/lib/ui/web/server/public/sidebar-h4.jpg +0 -0
  128. data/lib/ui/web/server/public/sidebar-top.jpg +0 -0
  129. data/lib/ui/web/server/public/spider.png +0 -0
  130. data/lib/ui/web/server/public/style.css +604 -0
  131. data/lib/ui/web/server/tmp/placeholder +0 -0
  132. data/lib/ui/web/server/views/dispatcher.erb +85 -0
  133. data/lib/ui/web/server/views/dispatcher_error.erb +14 -0
  134. data/lib/ui/web/server/views/error.erb +1 -0
  135. data/lib/ui/web/server/views/flash.erb +18 -0
  136. data/lib/ui/web/server/views/home.erb +14 -0
  137. data/lib/ui/web/server/views/instance.erb +213 -0
  138. data/lib/ui/web/server/views/layout.erb +95 -0
  139. data/lib/ui/web/server/views/log.erb +40 -0
  140. data/lib/ui/web/server/views/modules.erb +71 -0
  141. data/lib/ui/web/server/views/options.erb +23 -0
  142. data/lib/ui/web/server/views/output_results.erb +51 -0
  143. data/lib/ui/web/server/views/plugins.erb +42 -0
  144. data/lib/ui/web/server/views/report_formats.erb +30 -0
  145. data/lib/ui/web/server/views/reports.erb +55 -0
  146. data/lib/ui/web/server/views/settings.erb +120 -0
  147. data/lib/ui/web/server/views/welcome.erb +38 -0
  148. data/lib/ui/xmlrpc/dispatcher_monitor.rb +204 -0
  149. data/lib/ui/xmlrpc/xmlrpc.rb +843 -0
  150. data/logs/placeholder +0 -0
  151. data/metamodules/autothrottle.rb +74 -0
  152. data/metamodules/timeout_notice.rb +118 -0
  153. data/metamodules/uniformity.rb +98 -0
  154. data/modules/audit/code_injection.rb +136 -0
  155. data/modules/audit/code_injection_timing.rb +115 -0
  156. data/modules/audit/code_injection_timing/payloads.txt +4 -0
  157. data/modules/audit/csrf.rb +301 -0
  158. data/modules/audit/ldapi.rb +103 -0
  159. data/modules/audit/ldapi/errors.txt +26 -0
  160. data/modules/audit/os_cmd_injection.rb +103 -0
  161. data/modules/audit/os_cmd_injection/payloads.txt +2 -0
  162. data/modules/audit/os_cmd_injection_timing.rb +104 -0
  163. data/modules/audit/os_cmd_injection_timing/payloads.txt +3 -0
  164. data/modules/audit/path_traversal.rb +141 -0
  165. data/modules/audit/response_splitting.rb +105 -0
  166. data/modules/audit/rfi.rb +193 -0
  167. data/modules/audit/sqli.rb +120 -0
  168. data/modules/audit/sqli/regexp_ids.txt +90 -0
  169. data/modules/audit/sqli_blind_rdiff.rb +321 -0
  170. data/modules/audit/sqli_blind_timing.rb +103 -0
  171. data/modules/audit/sqli_blind_timing/payloads.txt +51 -0
  172. data/modules/audit/trainer.rb +89 -0
  173. data/modules/audit/unvalidated_redirect.rb +90 -0
  174. data/modules/audit/xpath.rb +104 -0
  175. data/modules/audit/xpath/errors.txt +26 -0
  176. data/modules/audit/xss.rb +99 -0
  177. data/modules/audit/xss_event.rb +134 -0
  178. data/modules/audit/xss_path.rb +125 -0
  179. data/modules/audit/xss_script_tag.rb +112 -0
  180. data/modules/audit/xss_tag.rb +112 -0
  181. data/modules/audit/xss_uri.rb +125 -0
  182. data/modules/recon/allowed_methods.rb +104 -0
  183. data/modules/recon/backdoors.rb +131 -0
  184. data/modules/recon/backdoors/filenames.txt +16 -0
  185. data/modules/recon/backup_files.rb +177 -0
  186. data/modules/recon/backup_files/extensions.txt +28 -0
  187. data/modules/recon/common_directories.rb +138 -0
  188. data/modules/recon/common_directories/directories.txt +265 -0
  189. data/modules/recon/common_files.rb +138 -0
  190. data/modules/recon/common_files/filenames.txt +17 -0
  191. data/modules/recon/directory_listing.rb +171 -0
  192. data/modules/recon/grep/captcha.rb +62 -0
  193. data/modules/recon/grep/credit_card.rb +85 -0
  194. data/modules/recon/grep/cvs_svn_users.rb +73 -0
  195. data/modules/recon/grep/emails.rb +59 -0
  196. data/modules/recon/grep/html_objects.rb +53 -0
  197. data/modules/recon/grep/private_ip.rb +54 -0
  198. data/modules/recon/grep/ssn.rb +53 -0
  199. data/modules/recon/htaccess_limit.rb +82 -0
  200. data/modules/recon/http_put.rb +95 -0
  201. data/modules/recon/interesting_responses.rb +118 -0
  202. data/modules/recon/unencrypted_password_forms.rb +119 -0
  203. data/modules/recon/webdav.rb +126 -0
  204. data/modules/recon/xst.rb +107 -0
  205. data/path_extractors/anchors.rb +35 -0
  206. data/path_extractors/forms.rb +35 -0
  207. data/path_extractors/frames.rb +38 -0
  208. data/path_extractors/generic.rb +39 -0
  209. data/path_extractors/links.rb +35 -0
  210. data/path_extractors/meta_refresh.rb +39 -0
  211. data/path_extractors/scripts.rb +37 -0
  212. data/path_extractors/sitemap.rb +31 -0
  213. data/plugins/autologin.rb +137 -0
  214. data/plugins/content_types.rb +90 -0
  215. data/plugins/cookie_collector.rb +99 -0
  216. data/plugins/form_dicattack.rb +185 -0
  217. data/plugins/healthmap.rb +94 -0
  218. data/plugins/http_dicattack.rb +133 -0
  219. data/plugins/metamodules.rb +118 -0
  220. data/plugins/proxy.rb +248 -0
  221. data/plugins/proxy/server.rb +66 -0
  222. data/plugins/waf_detector.rb +184 -0
  223. data/profiles/comprehensive.afp +74 -0
  224. data/profiles/full.afp +75 -0
  225. data/reports/afr.rb +59 -0
  226. data/reports/ap.rb +55 -0
  227. data/reports/html.rb +179 -0
  228. data/reports/html/default.erb +967 -0
  229. data/reports/metareport.rb +139 -0
  230. data/reports/metareport/arachni_metareport.rb +174 -0
  231. data/reports/plugin_formatters/html/content_types.rb +82 -0
  232. data/reports/plugin_formatters/html/cookie_collector.rb +66 -0
  233. data/reports/plugin_formatters/html/form_dicattack.rb +54 -0
  234. data/reports/plugin_formatters/html/healthmap.rb +76 -0
  235. data/reports/plugin_formatters/html/http_dicattack.rb +54 -0
  236. data/reports/plugin_formatters/html/metaformatters/timeout_notice.rb +65 -0
  237. data/reports/plugin_formatters/html/metaformatters/uniformity.rb +71 -0
  238. data/reports/plugin_formatters/html/metamodules.rb +93 -0
  239. data/reports/plugin_formatters/html/waf_detector.rb +54 -0
  240. data/reports/plugin_formatters/stdout/content_types.rb +73 -0
  241. data/reports/plugin_formatters/stdout/cookie_collector.rb +61 -0
  242. data/reports/plugin_formatters/stdout/form_dicattack.rb +52 -0
  243. data/reports/plugin_formatters/stdout/healthmap.rb +72 -0
  244. data/reports/plugin_formatters/stdout/http_dicattack.rb +53 -0
  245. data/reports/plugin_formatters/stdout/metaformatters/timeout_notice.rb +55 -0
  246. data/reports/plugin_formatters/stdout/metaformatters/uniformity.rb +68 -0
  247. data/reports/plugin_formatters/stdout/metamodules.rb +89 -0
  248. data/reports/plugin_formatters/stdout/waf_detector.rb +48 -0
  249. data/reports/plugin_formatters/xml/content_types.rb +91 -0
  250. data/reports/plugin_formatters/xml/cookie_collector.rb +70 -0
  251. data/reports/plugin_formatters/xml/form_dicattack.rb +57 -0
  252. data/reports/plugin_formatters/xml/healthmap.rb +82 -0
  253. data/reports/plugin_formatters/xml/http_dicattack.rb +57 -0
  254. data/reports/plugin_formatters/xml/metaformatters/timeout_notice.rb +67 -0
  255. data/reports/plugin_formatters/xml/metaformatters/uniformity.rb +82 -0
  256. data/reports/plugin_formatters/xml/metamodules.rb +91 -0
  257. data/reports/plugin_formatters/xml/waf_detector.rb +58 -0
  258. data/reports/stdout.rb +182 -0
  259. data/reports/txt.rb +77 -0
  260. data/reports/xml.rb +231 -0
  261. data/reports/xml/buffer.rb +98 -0
  262. 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
@@ -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