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,118 @@
1
+ =begin
2
+ Arachni
3
+ Copyright (c) 2010-2011 Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
4
+
5
+ This is free software; you can copy and distribute and modify
6
+ this program under the term of the GPL v2.0 License
7
+ (See LICENSE file for details)
8
+
9
+ =end
10
+
11
+ module Arachni
12
+
13
+ #
14
+ # Namespace under which all meta modules will reside
15
+ #
16
+ module MetaModules
17
+ class Base
18
+ include Arachni::Module::Output
19
+
20
+ def initialize( framework )
21
+ end
22
+
23
+ def prepare
24
+ end
25
+
26
+ def run
27
+ end
28
+
29
+ def clean_up
30
+ end
31
+
32
+ def self.info
33
+ {
34
+ :name => '[Meta] ' + self.name.to_s.gsub( 'Arachni::MetaModules::', '' ),
35
+ :description => %q{Performs high-level meta-analysis on the results of the scan
36
+ using abstract meta components.},
37
+ :author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>',
38
+ :version => '0.1',
39
+ }
40
+ end
41
+
42
+ end
43
+ end
44
+
45
+
46
+ module Plugins
47
+
48
+ #
49
+ # Performs high-level meta-analysis on the results of the scan
50
+ # using abstract meta-modules.
51
+ #
52
+ # @author: Tasos "Zapotek" Laskos
53
+ # <tasos.laskos@gmail.com>
54
+ # <zapotek@segfault.gr>
55
+ # @version: 0.1
56
+ #
57
+ class MetaModules < Arachni::Plugin::Base
58
+
59
+ def initialize( framework, options )
60
+ @framework = framework
61
+
62
+ @metamanager = Arachni::ComponentManager.new( @framework.opts.dir['root'] + 'metamodules/', Arachni::MetaModules )
63
+
64
+ # load all meta-components
65
+ @metamanager.load( ['*'] )
66
+ @inited = {}
67
+ @metamanager.each {
68
+ |name, klass|
69
+ @inited[name] = klass.new( @framework )
70
+ }
71
+ end
72
+
73
+ def prepare
74
+ # prepare all meta modules here to give them a chance to set up their hooks
75
+ # and callbacks to other framework interfaces.
76
+ @inited.values.each { |meta| meta.prepare }
77
+
78
+ # we need to wait until the framework has finished running
79
+ # in order to work with the full report
80
+ while( @framework.running? )
81
+ ::IO.select( nil, nil, nil, 1 )
82
+ end
83
+
84
+ end
85
+
86
+ def run
87
+ results = { }
88
+ # run all meta-modules
89
+ @inited.each_pair {
90
+ |name, meta|
91
+ if (metaresult = meta.run) && !metaresult.empty?
92
+ results[name] = { :results => metaresult }.merge( meta.class.info )
93
+ end
94
+ }
95
+
96
+ register_results( results )
97
+ end
98
+
99
+ def clean_up
100
+ # let the meta-modules clean up after themselves
101
+ @inited.values.each { |meta| meta.clean_up }
102
+ end
103
+
104
+ def self.info
105
+ {
106
+ :name => 'Metamodules',
107
+ :description => %q{Performs high-level meta-analysis on the results of the scan using abstract meta-modules.
108
+ Before reviewing the scan results you are strongly encouraged to take full advantage of the data gathered via meta-analysis.
109
+ They will help you shed light into the inner workings of the web application and even caution you about possible false positives and/or inconclusive test results.},
110
+ :author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>',
111
+ :version => '0.1'
112
+ }
113
+ end
114
+
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,248 @@
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 Plugins
13
+
14
+ #
15
+ # Passive proxy.
16
+ #
17
+ # Will gather data based on user actions and exchanged HTTP traffic and push that
18
+ # data to the {Framework#page_queue} to be audited.
19
+ #
20
+ # @author: Tasos "Zapotek" Laskos
21
+ # <tasos.laskos@gmail.com>
22
+ # <zapotek@segfault.gr>
23
+ # @version: 0.1
24
+ #
25
+ class Proxy < Arachni::Plugin::Base
26
+
27
+ SHUTDOWN_URL = 'http://arachni.proxy.shutdown/'
28
+
29
+ MSG_SHUTDOWN = 'Shutting down the Arachni proxy plug-in...'
30
+
31
+ MSG_DISALOWED = "You can't access this resource via the Arachni " +
32
+ "proxy plug-in for the following reasons:"
33
+
34
+ MSG_NOT_IN_DOMAIN = 'This resource is on a domain or subdomain' +
35
+ ' outside the scope of the audit.'
36
+
37
+ MSG_EXCLUDED = 'This resource is matched by an exclude rule.'
38
+
39
+ MSG_NOT_INCLUDED = 'This resource is disallowed based on an include rule.'
40
+
41
+ #
42
+ # @param [Arachni::Framework] framework
43
+ # @param [Hash] options options passed to the plugin
44
+ #
45
+ def initialize( framework, options )
46
+ @framework = framework
47
+ @options = options
48
+
49
+ # don't let the framework run just yet
50
+ @framework.pause!
51
+ print_info( "System paused." )
52
+ end
53
+
54
+ def prepare
55
+ require @framework.opts.dir['plugins'] + '/proxy/server.rb'
56
+
57
+ # foo initialization, we just need it to verify URLs
58
+ @parser = Arachni::Parser.new( @framework.opts,
59
+ Typhoeus::Response.new(
60
+ :effective_url => @framework.opts.url.to_s,
61
+ :body => '',
62
+ :headers_hash => {}
63
+ )
64
+ )
65
+
66
+ @server = Server.new(
67
+ :BindAddress => @options['bind_address'],
68
+ :Port => @options['port'],
69
+ :ProxyVia => false,
70
+ :ProxyContentHandler => method( :handler ),
71
+ :ProxyURITest => method( :allowed? ),
72
+ :AccessLog => [],
73
+ :Logger => WEBrick::Log::new( "/dev/null", 7 )
74
+ )
75
+ end
76
+
77
+ def run( )
78
+ print_status( "Listening on: " +
79
+ "http://#{@server[:BindAddress]}:#{@server[:Port]}" )
80
+
81
+ print_status( "Shutdown URL: #{SHUTDOWN_URL}" )
82
+ print_info( "The scan will resume once you visit the shutdown URL." )
83
+ @server.start
84
+ end
85
+
86
+ #
87
+ # Called by the proxy to process each page
88
+ #
89
+ def handler( req, res )
90
+
91
+ if( res.header['content-encoding'] == 'gzip' )
92
+ res.header.delete( 'content-encoding' )
93
+ res.body = Zlib::GzipReader.new( StringIO.new( res.body ) ).read
94
+ end
95
+
96
+ headers = {}
97
+ headers.merge( res.header.dup ) if res.header
98
+ headers['set-cookie'] = res.cookies if !res.cookies.empty?
99
+
100
+ # proper initialization in order to parse the response into a page
101
+ @parser = Arachni::Parser.new( @framework.opts,
102
+ Typhoeus::Response.new(
103
+ :effective_url => req.unparsed_uri,
104
+ :body => res.body,
105
+ :headers_hash => headers
106
+ )
107
+ )
108
+
109
+ page = @parser.run
110
+ page = update_forms( page, req ) if req.body
111
+ page.method = res.request_method
112
+ page.code = res.status
113
+
114
+ print_info " * #{page.forms.size} forms"
115
+ print_info " * #{page.links.size} links"
116
+ print_info " * #{page.cookies.size} cookies"
117
+
118
+ update_framework_cookies( page, req )
119
+ @framework.page_queue << page.dup
120
+
121
+ return res
122
+ end
123
+
124
+ def update_framework_cookies( page, req )
125
+
126
+ print_debug( 'Updating framework cookies...' )
127
+
128
+ cookies = {}
129
+
130
+ if req['Cookie']
131
+ req['Cookie'].split( ';' ).each{
132
+ |cookie|
133
+ k, v = cookie.split( '=', 2 )
134
+ cookies[k.strip] = v.strip
135
+ }
136
+ end
137
+
138
+ page.cookies.each {
139
+ |cookie|
140
+ cookies.merge!( cookie.simple )
141
+ }
142
+
143
+ if cookies.empty?
144
+ print_debug( 'Could not extract cookies...' )
145
+ return
146
+ else
147
+ print_debug( 'Extracted cookies:' )
148
+ cookies.each{
149
+ |k, v|
150
+ print_debug( " * #{k} => #{v}" )
151
+ }
152
+ end
153
+
154
+ @framework.http.update_cookies( cookies )
155
+
156
+ end
157
+
158
+ def update_forms( page, req )
159
+ params = {}
160
+
161
+ URI.decode( req.body ).split( '&' ).each {
162
+ |param|
163
+ k,v = param.split( '=', 2 )
164
+ params[k] = v
165
+ }
166
+
167
+ raw = {
168
+ 'attrs' => {
169
+ 'action' => req.unparsed_uri,
170
+ 'method' => req.request_method,
171
+ }
172
+ }
173
+
174
+ form = ::Arachni::Parser::Element::Form.new( req.unparsed_uri, raw )
175
+ form.auditable = params
176
+
177
+ page.forms << form
178
+
179
+ return page
180
+ end
181
+
182
+ #
183
+ # Checks if the URL is allowed.
184
+ #
185
+ # URLs outside the scope of the scan are not allowed.
186
+ #
187
+ def allowed?( uri )
188
+
189
+ url = URI( uri )
190
+
191
+ print_status( 'Requesting: ' + url.to_s )
192
+
193
+ # if !(url.to_s =~ /http(s):\/\//)
194
+ # url = URI( @framework.opts.url.scheme + '://' + url.to_s )
195
+ # end
196
+
197
+ reasons = []
198
+
199
+ if shutdown?( url )
200
+ print_status( 'Shutting down...' )
201
+ @server.shutdown
202
+ reasons << MSG_SHUTDOWN
203
+ return reasons
204
+ end
205
+
206
+ @parser.url = @framework.opts.url
207
+
208
+ reasons << MSG_NOT_IN_DOMAIN if !@parser.in_domain?( url )
209
+ reasons << MSG_EXCLUDED if @parser.exclude?( url )
210
+ reasons << MSG_NOT_INCLUDED if !@parser.include?( url )
211
+
212
+ if !reasons.empty?
213
+ print_info( "#{MSG_DISALOWED}" )
214
+ reasons.each{ |msg| print_info " * #{msg}" }
215
+ reasons << MSG_DISALOWED
216
+ end
217
+
218
+ return reasons
219
+ end
220
+
221
+ def shutdown?( url )
222
+ return url.to_s == SHUTDOWN_URL
223
+ end
224
+
225
+ def clean_up
226
+ @framework.resume!
227
+ end
228
+
229
+ def self.info
230
+ {
231
+ :name => 'Proxy',
232
+ :description => %q{Gathers data based on user actions and exchanged HTTP
233
+ traffic and pushes that data to the framework's page-queue to be audited.
234
+ It also updates the framework cookies with the cookies of the HTTP requests and
235
+ responses, thus it can also be used to login to a web application.},
236
+ :author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>',
237
+ :version => '0.1',
238
+ :options => [
239
+ Arachni::OptPort.new( 'port', [ false, 'Port to bind to.', 8282 ] ),
240
+ Arachni::OptAddress.new( 'bind_address', [ false, 'IP address to bind to.', '0.0.0.0' ] )
241
+ ]
242
+ }
243
+ end
244
+
245
+ end
246
+
247
+ end
248
+ end
@@ -0,0 +1,66 @@
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 'webrick/httpproxy'
12
+ require 'stringio'
13
+ require 'zlib'
14
+ require 'open-uri'
15
+
16
+ module Arachni
17
+ module Plugins
18
+
19
+ class Proxy
20
+ #
21
+ # We add our own type of WEBrick::HTTPProxyServer class that supports
22
+ # notifications when the user tries to access a resource irrelevant
23
+ # to the scan and does not restrict header exchange.
24
+ #
25
+ # @author: Tasos "Zapotek" Laskos
26
+ # <tasos.laskos@gmail.com>
27
+ # <zapotek@segfault.gr>
28
+ # @version: 0.1
29
+ #
30
+ class Server < WEBrick::HTTPProxyServer
31
+
32
+ def choose_header(src, dst)
33
+ connections = split_field(src['connection'])
34
+ src.each{|key, value|
35
+ key = key.downcase
36
+ if HopByHop.member?(key) || # RFC2616: 13.5.1
37
+ connections.member?(key) || # RFC2616: 14.10
38
+ # ShouldNotTransfer.member?(key) # pragmatics
39
+ @logger.debug("choose_header: `#{key}: #{value}'")
40
+ next
41
+ end
42
+ dst[key] = value
43
+ }
44
+ end
45
+
46
+ def service( req, res )
47
+ exclude_reasons = @config[:ProxyURITest].call( req.unparsed_uri )
48
+
49
+ if( exclude_reasons.empty? )
50
+ super( req, res )
51
+ else
52
+ notify( exclude_reasons, req, res )
53
+ end
54
+ end
55
+
56
+ def notify( reasons, req, res )
57
+ res.header['content-type'] = 'text/plain'
58
+ res.header.delete( 'content-encoding' )
59
+
60
+ res.body << reasons.map{ |msg| " * #{msg}" }.join( "\n" )
61
+ end
62
+ end
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,184 @@
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 Plugins
13
+
14
+ #
15
+ # Web Application Firewall detection plugin.
16
+ #
17
+ # This is a 4 stage process:
18
+ # 1. Grab the original page as is
19
+ # 2. Send a lot of innocent strings in non-existent inputs so as to profile normal behavior
20
+ # 3. Send a lot of suspicious strings in non-existent inputs and check if behavior changes
21
+ # 4. Make heads or tails of the gathered responses
22
+ #
23
+ # Steps 1 to 3 will be repeated _precision_ times and the responses will be averaged using rDiff analysis.
24
+ #
25
+ #
26
+ # @author: Tasos "Zapotek" Laskos
27
+ # <tasos.laskos@gmail.com>
28
+ # <zapotek@segfault.gr>
29
+ # @version: 0.1
30
+ #
31
+ class WAFDetector < Arachni::Plugin::Base
32
+
33
+ MSG_INCONCLUSIVE = %q{Could not establish a baseline behavior for the website. Due to that fact analysis has been aborted.}
34
+
35
+ MSG_FOUND = %q{Request parameters are being filtered, this is usually a sign of a WAF.}
36
+
37
+ MSG_NOT_FOUND = %q{Could not detect any sign of filtering, a WAF doesn't seem to be present.}
38
+
39
+ #
40
+ # @param [Arachni::Framework] framework
41
+ # @param [Hash] options options passed to the plugin
42
+ #
43
+ def initialize( framework, options )
44
+ @framework = framework
45
+ @options = options
46
+ end
47
+
48
+ def prepare
49
+
50
+ @precision = @options['precision']
51
+
52
+ bad = [
53
+ '../../../../',
54
+ '<script>foo</script>',
55
+ '\'--;`',
56
+ ]
57
+
58
+ names = []
59
+ bad.size.times {
60
+ |i|
61
+ names << i.to_s + '_' + Digest::SHA2.hexdigest( rand( i*1000 ).to_s )
62
+ }
63
+
64
+ @safe = { }
65
+ @unsafe = { }
66
+ names.each_with_index {
67
+ |name, i|
68
+ @safe[name] = 'value_' + name
69
+ @unsafe[name] = i.to_s + '_' + bad.join( '_' )
70
+ }
71
+
72
+ @url = @framework.opts.url
73
+
74
+ @responses = {
75
+ :original => nil,
76
+ :vanilla => nil,
77
+ :spicy => nil
78
+ }
79
+
80
+ end
81
+
82
+ def run( )
83
+
84
+ print_status( "Starting detection with a precision of #{@precision}." )
85
+
86
+ print_status( "Stage #1: Requesting original page." )
87
+ queue_original( )
88
+
89
+ print_status( "Stage #2: Requesting with vanilla inputs." )
90
+ queue_vanilla( )
91
+
92
+ print_status( "Stage #3: Requesting with spicy inputs." )
93
+ queue_spicy( )
94
+
95
+ print_status( "Stage #4: Analyzing gathered responses." )
96
+
97
+ @framework.http.after_run {
98
+
99
+ if @responses[:original] == @responses[:vanilla]
100
+ if @responses[:vanilla] == @responses[:spicy]
101
+ not_found!
102
+ else
103
+ found!
104
+ end
105
+ else
106
+ inconclusive!
107
+ end
108
+ }
109
+
110
+ end
111
+
112
+ def found!
113
+ print_ok( MSG_FOUND )
114
+ register_results( { :code => 1, :msg => MSG_FOUND } )
115
+ end
116
+
117
+ def not_found!
118
+ print_ok( MSG_NOT_FOUND )
119
+ register_results( { :code => 0, :msg => MSG_NOT_FOUND } )
120
+ end
121
+
122
+ def inconclusive!
123
+ print_ok( MSG_INCONCLUSIVE )
124
+ register_results( { :code => -1, :msg => MSG_INCONCLUSIVE } )
125
+ end
126
+
127
+ def queue_original
128
+ @precision.times {
129
+ # grab the page containing the login form
130
+ @framework.http.get( @url.to_s ).on_complete {
131
+ |res|
132
+ @responses[:original] ||= res.body
133
+ @responses[:original] = @responses[:original].rdiff( res.body )
134
+ }
135
+ }
136
+ end
137
+
138
+ def queue_vanilla( )
139
+ @precision.times {
140
+ # grab the page containing the login form
141
+ @framework.http.get( @url.to_s, :params => @safe ).on_complete {
142
+ |res|
143
+ @responses[:vanilla] ||= res.body
144
+ @responses[:vanilla] = @responses[:vanilla].rdiff( res.body )
145
+ }
146
+ }
147
+ end
148
+
149
+ def queue_spicy( )
150
+ @precision.times {
151
+ # grab the page containing the login form
152
+ @framework.http.get( @url.to_s, :params => @unsafe ).on_complete {
153
+ |res|
154
+ @responses[:spicy] ||= res.body
155
+ @responses[:spicy] = @responses[:spicy].rdiff( res.body )
156
+ }
157
+ }
158
+ end
159
+
160
+ def self.info
161
+ {
162
+ :name => 'WAF Detector',
163
+ :description => %q{Performs basic profiling on the web application
164
+ in order to assess the existence of a Web Application Firewall.
165
+
166
+ This is a 4 stage process:
167
+ 1. Grab the original page as is
168
+ 2. Send a lot of innocent (vanilla) strings in non-existent inputs so as to profile normal behavior
169
+ 3. Send a lot of suspicious (spicy) strings in non-existent inputs and check if behavior changes
170
+ 4. Make heads or tails of the gathered responses
171
+
172
+ Steps 1 to 3 will be repeated _precision_ times (default: 5) and the responses will be averaged using rDiff analysis.},
173
+ :author => 'Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>',
174
+ :version => '0.1',
175
+ :options => [
176
+ Arachni::OptInt.new( 'precision', [ false, 'Stage precision (how many times to perform each detection stage).', 5 ] )
177
+ ]
178
+ }
179
+ end
180
+
181
+ end
182
+
183
+ end
184
+ end