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,302 @@
1
+ =begin
2
+ $Id$
3
+
4
+ Arachni
5
+ Copyright (c) 2010-2011 Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
6
+
7
+ This is free software; you can copy and distribute and modify
8
+ this program under the term of the GPL v2.0 License
9
+ (See LICENSE file for details)
10
+
11
+ =end
12
+
13
+
14
+ #
15
+ # Vulnerability class.
16
+ #
17
+ # It represents a detected vulnerability.
18
+ #
19
+ #
20
+ # @author: Tasos "Zapotek" Laskos
21
+ # <tasos.laskos@gmail.com>
22
+ # <zapotek@segfault.gr>
23
+ # @version: 0.1
24
+ #
25
+ module Arachni
26
+
27
+ class Issue
28
+
29
+ #
30
+ # Holds constants to describe the {Issue#severity} of a
31
+ # vulnerability.
32
+ #
33
+ module Severity
34
+ HIGH = 'High'
35
+ MEDIUM = 'Medium'
36
+ LOW = 'Low'
37
+ INFORMATIONAL = 'Informational'
38
+ end
39
+
40
+ #
41
+ # Holds constants to describe the {Issue#elem} of a
42
+ # vulnerability.
43
+ #
44
+ module Element
45
+ LINK = 'link'
46
+ FORM = 'form'
47
+ COOKIE = 'cookie'
48
+ HEADER = 'header'
49
+ BODY = 'body'
50
+ PATH = 'path'
51
+ SERVER = 'server'
52
+ end
53
+
54
+ #
55
+ # The name of the issue
56
+ #
57
+ # @return [String]
58
+ #
59
+ attr_accessor :name
60
+
61
+ #
62
+ # The module that detected the issue
63
+ #
64
+ # @return [String] the name of the module
65
+ #
66
+ attr_accessor :mod_name
67
+
68
+ #
69
+ # The vulnerable HTTP variable
70
+ #
71
+ # @return [String] the name of the http variable
72
+ #
73
+ attr_accessor :var
74
+
75
+ #
76
+ # The vulnerable URL
77
+ #
78
+ # @return [String]
79
+ #
80
+ attr_accessor :url
81
+
82
+ #
83
+ # The headers exchanhed during the attack
84
+ #
85
+ # @return [Hash<String, Hash>] request and reply headers
86
+ #
87
+ attr_accessor :headers
88
+
89
+ #
90
+ # The HTML response of the attack
91
+ #
92
+ # @return [String] the html response of the attack
93
+ #
94
+ attr_accessor :response
95
+
96
+ #
97
+ # The injected data that revealed the issue
98
+ #
99
+ # @return [String]
100
+ #
101
+ attr_accessor :injected
102
+
103
+ #
104
+ # The string that identified the issue
105
+ #
106
+ # @return [String]
107
+ #
108
+ attr_accessor :id
109
+
110
+ #
111
+ # The regexp that identified the issue
112
+ #
113
+ # @return [String]
114
+ #
115
+ attr_accessor :regexp
116
+
117
+ #
118
+ # The data that was matched by the regexp
119
+ #
120
+ # @return [String]
121
+ #
122
+ attr_accessor :regexp_match
123
+
124
+ #
125
+ # The vulnerable element, link, form or cookie
126
+ #
127
+ # @return [String]
128
+ #
129
+ attr_accessor :elem
130
+
131
+ #
132
+ # HTTP method
133
+ #
134
+ # @return [String]
135
+ #
136
+ attr_accessor :method
137
+
138
+ #
139
+ # The description of the issue
140
+ #
141
+ # @return [String]
142
+ #
143
+ attr_accessor :description
144
+
145
+ #
146
+ # References related to the issue
147
+ #
148
+ # @return [Hash]
149
+ #
150
+ attr_accessor :references
151
+
152
+ #
153
+ # The CWE ID number of the issue
154
+ #
155
+ # @return [String]
156
+ #
157
+ attr_accessor :cwe
158
+
159
+ #
160
+ # The CWE URL of the issue
161
+ #
162
+ # @return [String]
163
+ #
164
+ attr_accessor :cwe_url
165
+
166
+ #
167
+ # To be assigned a constant form {Severity}
168
+ #
169
+ # @see Severity
170
+ #
171
+ # @return [String]
172
+ #
173
+ attr_accessor :severity
174
+
175
+ #
176
+ # The CVSS v2 score
177
+ #
178
+ # @return [String]
179
+ #
180
+ attr_accessor :cvssv2
181
+
182
+ #
183
+ # A brief text informing the user how to remedy the situation
184
+ #
185
+ # @return [String]
186
+ #
187
+ attr_accessor :remedy_guidance
188
+
189
+ #
190
+ # A code snipet showing the user how to remedy the situation
191
+ #
192
+ # @return [String]
193
+ #
194
+ attr_accessor :remedy_code
195
+
196
+ #
197
+ # Placeholder variable to be populated by {AuditStore#prepare_variations}
198
+ #
199
+ # @see AuditStore#prepare_variations
200
+ #
201
+ attr_accessor :variations
202
+
203
+ #
204
+ # Is manual verification required?
205
+ #
206
+ # @return [Bool]
207
+ #
208
+ attr_accessor :verification
209
+
210
+ #
211
+ # The Metasploit module that can exploit the vulnerability.
212
+ #
213
+ # ex. exploit/unix/webapp/php_include
214
+ #
215
+ # @return [String]
216
+ #
217
+ attr_accessor :metasploitable
218
+
219
+ attr_accessor :opts
220
+
221
+ attr_accessor :internal_modname
222
+ attr_accessor :tags
223
+ attr_accessor :_hash
224
+
225
+ #
226
+ # Sets up the instanse attributes
227
+ #
228
+ # @param Hash cofiguration hash
229
+ # Usually the returned data of a module's
230
+ # info() method for the references
231
+ # merged with a name=>value pair hash holding
232
+ # class attributes
233
+ #
234
+ def initialize( opts = {} )
235
+
236
+ @verification = false
237
+
238
+ opts.each {
239
+ |k, v|
240
+ begin
241
+ send( "#{k.to_s.downcase}=", v )
242
+ rescue Exception => e
243
+ end
244
+ }
245
+
246
+ opts[:issue].each {
247
+ |k, v|
248
+ begin
249
+ send( "#{k.to_s.downcase}=", v )
250
+ rescue Exception => e
251
+ end
252
+ }
253
+
254
+ if( @cwe )
255
+ @cwe_url = "http://cwe.mitre.org/data/definitions/" + @cwe + ".html"
256
+ end
257
+
258
+ @mod_name = opts[:name]
259
+ @references = opts[:references] || {}
260
+
261
+ end
262
+
263
+ def regexp=( regexp )
264
+ return if !regexp
265
+ @regexp = regexp.to_s
266
+ end
267
+
268
+ def opts=( hash )
269
+ return if !hash
270
+ hash[:regexp] = hash[:regexp].to_s
271
+ hash[:match] ||= false
272
+ @opts = hash.dup
273
+ end
274
+
275
+ def each
276
+ self.instance_variables.each {
277
+ |var|
278
+ yield( { normalize_name( var ) => instance_variable_get( var ) } )
279
+ }
280
+ end
281
+
282
+ def each_pair
283
+ self.instance_variables.each {
284
+ |var|
285
+ yield normalize_name( var ), instance_variable_get( var )
286
+ }
287
+ end
288
+
289
+ def remove_instance_var( var )
290
+ remove_instance_variable( var )
291
+ end
292
+
293
+ private
294
+
295
+ def normalize_name( name )
296
+ name.to_s.gsub( /@/, '' )
297
+ end
298
+
299
+
300
+ end
301
+
302
+ end
@@ -0,0 +1,4 @@
1
+ opts = Arachni::Options.instance
2
+ require opts.dir['lib'] + 'component_manager'
3
+ require opts.dir['lib'] + 'module/base'
4
+ require opts.dir['lib'] + 'module/manager'
@@ -0,0 +1,455 @@
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
+ # Auditor module
16
+ #
17
+ # Included by {Module::Base}.<br/>
18
+ # Includes audit methods.
19
+ #
20
+ # @author: Tasos "Zapotek" Laskos
21
+ # <tasos.laskos@gmail.com>
22
+ # <zapotek@segfault.gr>
23
+ # @version: 0.2.2
24
+ #
25
+ module Auditor
26
+
27
+ #
28
+ # Holds constant bitfields that describe the preferred formatting
29
+ # of injection strings.
30
+ #
31
+ module Format
32
+
33
+ #
34
+ # Leaves the injection string as is.
35
+ #
36
+ STRAIGHT = 1 << 0
37
+
38
+ #
39
+ # Apends the injection string to the default value of the input vector.<br/>
40
+ # (If no default value exists Arachni will choose one.)
41
+ #
42
+ APPEND = 1 << 1
43
+
44
+ #
45
+ # Terminates the injection string with a null character.
46
+ #
47
+ NULL = 1 << 2
48
+
49
+ #
50
+ # Prefix the string with a ';', useful for command injection modules
51
+ #
52
+ SEMICOLON = 1 << 3
53
+ end
54
+
55
+ #
56
+ # Holds constants that describe the HTML elements to be audited.
57
+ #
58
+ module Element
59
+ LINK = Issue::Element::LINK
60
+ FORM = Issue::Element::FORM
61
+ COOKIE = Issue::Element::COOKIE
62
+ HEADER = Issue::Element::HEADER
63
+ BODY = Issue::Element::BODY
64
+ PATH = Issue::Element::PATH
65
+ SERVER = Issue::Element::SERVER
66
+ end
67
+
68
+ #
69
+ # Default audit options.
70
+ #
71
+ OPTIONS = {
72
+
73
+ #
74
+ # Elements to audit.
75
+ #
76
+ # Only required when calling {#audit}.<br/>
77
+ # If no elements have been passed to audit it will
78
+ # use the elements in {#self.info}.
79
+ #
80
+ :elements => [ Element::LINK, Element::FORM,
81
+ Element::COOKIE, Element::HEADER,
82
+ Issue::Element::BODY ],
83
+
84
+ #
85
+ # The regular expression to match against the response body.
86
+ #
87
+ :regexp => nil,
88
+
89
+ #
90
+ # Verify the matched string with this value.
91
+ #
92
+ :match => nil,
93
+
94
+ #
95
+ # Formatting of the injection strings.
96
+ #
97
+ # A new set of audit inputs will be generated
98
+ # for each value in the array.
99
+ #
100
+ # Values can be OR'ed bitfields of all available constants
101
+ # of {Auditor::Format}.
102
+ #
103
+ # @see Auditor::Format
104
+ #
105
+ :format => [ Format::STRAIGHT, Format::APPEND,
106
+ Format::NULL, Format::APPEND | Format::NULL ],
107
+
108
+ #
109
+ # If 'train' is set to true the HTTP response will be
110
+ # analyzed for new elements. <br/>
111
+ # Be carefull when enabling it, there'll be a performance penalty.
112
+ #
113
+ # When the Auditor submits a form with original or sample values
114
+ # this option will be overriden to true.
115
+ #
116
+ :train => false,
117
+
118
+ #
119
+ # Enable skipping of already audited inputs
120
+ #
121
+ :redundant => false,
122
+
123
+ #
124
+ # Make requests asynchronously
125
+ #
126
+ :async => true
127
+ }
128
+
129
+ #
130
+ # Matches the HTML in @page.html to an array of regular expressions
131
+ # and logs the results.
132
+ #
133
+ # @param [Array<Regexp>] regexps
134
+ # @param [String] string
135
+ # @param [Block] block block to verify matches before logging
136
+ # must return true/false
137
+ #
138
+ def match_and_log( regexps, string = @page.html, &block )
139
+
140
+ # make sure that we're working with an array
141
+ regexps = [regexps].flatten
142
+
143
+ elems = self.class.info[:elements]
144
+ elems = OPTIONS[:elements] if !elems || elems.empty?
145
+
146
+ regexps.each {
147
+ |regexp|
148
+
149
+ string.scan( regexp ).flatten.uniq.each {
150
+ |match|
151
+
152
+ next if !match
153
+ next if block && !block.call( match )
154
+
155
+ log(
156
+ :regexp => regexp,
157
+ :match => match,
158
+ :element => Issue::Element::BODY
159
+ )
160
+ } if elems.include? Issue::Element::BODY
161
+
162
+ next if string == @page.html
163
+
164
+ @page.response_headers.each {
165
+ |k,v|
166
+ next if !v
167
+
168
+ v.to_s.scan( regexp ).flatten.uniq.each {
169
+ |match|
170
+
171
+ next if !match
172
+ next if block && !block.call( match )
173
+
174
+ log(
175
+ :var => k,
176
+ :regexp => regexp,
177
+ :match => match,
178
+ :element => Issue::Element::HEADER
179
+ )
180
+ }
181
+ } if elems.include? Issue::Element::HEADER
182
+
183
+ }
184
+ end
185
+
186
+ #
187
+ # Logs a vulnerability based on a regular expression and it's matched string
188
+ #
189
+ # @param [Regexp] regexp
190
+ # @param [String] match
191
+ #
192
+ def log( opts, res = nil )
193
+
194
+ method = nil
195
+
196
+ request_headers = nil
197
+ response_headers = @page.response_headers
198
+ response = @page.html
199
+ url = @page.url
200
+ method = @page.method.to_s.upcase if @page.method
201
+
202
+ if( res )
203
+ request_headers = res.request.headers
204
+ response_headers = res.headers
205
+ response = res.body
206
+ url = res.effective_url
207
+ method = res.request.method.to_s.upcase
208
+ end
209
+
210
+ if response_headers['content-type'] &&
211
+ !response_headers['content-type'].substring?( 'text' )
212
+ response = nil
213
+ end
214
+
215
+ begin
216
+ print_ok( "In #{opts[:element]} var '#{opts[:altered]}' ( #{url} )" )
217
+ rescue
218
+ end
219
+
220
+ print_verbose( "Injected string:\t" + opts[:injected] ) if opts[:injected]
221
+ print_verbose( "Verified string:\t" + opts[:match].to_s ) if opts[:match]
222
+ print_verbose( "Matched regular expression: " + opts[:regexp].to_s )
223
+ print_debug( 'Request ID: ' + res.request.id.to_s ) if res
224
+ print_verbose( '---------' ) if only_positives?
225
+
226
+ # Instantiate a new Vulnerability class and
227
+ # append it to the results array
228
+ vuln = Issue.new( {
229
+ :var => opts[:altered],
230
+ :url => url,
231
+ :injected => opts[:injected],
232
+ :id => opts[:id],
233
+ :regexp => opts[:regexp],
234
+ :regexp_match => opts[:match],
235
+ :elem => opts[:element],
236
+ :verification => opts[:verification] || false,
237
+ :method => method,
238
+ :response => response,
239
+ :opts => opts,
240
+ :headers => {
241
+ :request => request_headers,
242
+ :response => response_headers,
243
+ }
244
+ }.merge( self.class.info ) )
245
+ register_results( [vuln] )
246
+ end
247
+
248
+ #
249
+ # Provides easy access to element auditing.
250
+ #
251
+ # If no elements have been specified in 'opts' it will
252
+ # use the elements from the module's "self.info()" hash. <br/>
253
+ # If no elements have been specified in 'opts' or "self.info()" it will
254
+ # use the elements in {OPTIONS}. <br/>
255
+ #
256
+ #
257
+ # @param [String] injection_str the string to be injected
258
+ # @param [Hash] opts options as described in {OPTIONS}
259
+ # @param [Block] &block block to be passed the:
260
+ # * HTTP response
261
+ # * name of the input vector
262
+ # * updated opts
263
+ # The block will be called as soon as the
264
+ # HTTP response is received.
265
+ #
266
+ def audit( injection_str, opts = { }, &block )
267
+
268
+ if( !opts.include?( :elements) || !opts[:elements] || opts[:elements].empty? )
269
+ opts[:elements] = self.class.info[:elements]
270
+ end
271
+
272
+ if( !opts.include?( :elements) || !opts[:elements] || opts[:elements].empty? )
273
+ opts[:elements] = OPTIONS[:elements]
274
+ end
275
+
276
+ opts = OPTIONS.merge( opts )
277
+
278
+ opts[:elements].each {
279
+ |elem|
280
+
281
+ case elem
282
+
283
+ when Element::LINK
284
+ audit_links( injection_str, opts, &block )
285
+
286
+ when Element::FORM
287
+ audit_forms( injection_str, opts, &block )
288
+
289
+ when Element::COOKIE
290
+ audit_cookies( injection_str, opts, &block )
291
+
292
+ when Element::HEADER
293
+ audit_headers( injection_str, opts, &block )
294
+ else
295
+ raise( 'Unknown element to audit: ' + elem.to_s )
296
+
297
+ end
298
+
299
+ }
300
+ end
301
+
302
+
303
+ #
304
+ # Audits elements using a 2 phase timing attack and logs results.
305
+ #
306
+ # 'opts' needs to contain a :timeout value in milliseconds.</br>
307
+ # Optionally, you can add a :timeout_divider.
308
+ #
309
+ # Phase 1 uses the timeout value passed in opts, phase 2 uses (timeout * 2). </br>
310
+ # If phase 1 fails, phase 2 is aborted. </br>
311
+ # If we have a result in phase 1, phase 2 verifies that result with the higher timeout.
312
+ #
313
+ # @param [Array] strings injection strings
314
+ # '__TIME__' will be substituded with (timeout / timeout_divider)
315
+ # @param [Hash] opts options as described in {OPTIONS}
316
+ #
317
+ def audit_timeout( strings, opts )
318
+ logged = Set.new
319
+
320
+ delay = opts[:timeout]
321
+
322
+ audit_timeout_debug_msg( 1, delay )
323
+ timing_attack( strings, opts ) {
324
+ |res, opts, elem|
325
+
326
+ if !logged.include?( opts[:altered] )
327
+ logged << opts[:altered]
328
+ audit_timeout_phase_2( elem )
329
+ end
330
+ }
331
+ end
332
+
333
+ #
334
+ # Runs phase 2 of the timing attack auditng an individual element
335
+ # (which passed phase 1) with a higher delay and timeout
336
+ #
337
+ def audit_timeout_phase_2( elem )
338
+
339
+ opts = elem.opts
340
+ opts[:timeout] *= 2
341
+
342
+ audit_timeout_debug_msg( 2, opts[:timeout] )
343
+
344
+ str = opts[:timing_string].gsub( '__TIME__',
345
+ ( (opts[:timeout] + 3000) / opts[:timeout_divider] ).to_s )
346
+
347
+ elem.auditor( self )
348
+ elem.audit( str, opts ) {
349
+ |res, opts|
350
+
351
+ if res.timed_out?
352
+
353
+ # all issues logged by timing attacks need manual verification.
354
+ # end of story.
355
+ opts[:verification] = true
356
+ log( opts, res)
357
+ end
358
+ }
359
+ end
360
+
361
+ def audit_timeout_debug_msg( phase, delay )
362
+ print_debug( '---------------------------------------------' )
363
+ print_debug( "Running phase #{phase.to_s} of timing attack." )
364
+ print_debug( "Delay set to: #{delay.to_s} milliseconds" )
365
+ print_debug( '---------------------------------------------' )
366
+ end
367
+
368
+ #
369
+ # Audits elements using a timing attack.
370
+ #
371
+ # 'opts' needs to contain a :timeout value in milliseconds.</br>
372
+ # Optionally, you can add a :timeout_divider.
373
+ #
374
+ # @param [Array] strings injection strings
375
+ # '__TIME__' will be substituded with (timeout / timeout_divider)
376
+ # @param [Hash] opts options as described in {OPTIONS}
377
+ # @param [Block] &block block to call if a timeout occurs,
378
+ # it will be passed the response and opts
379
+ #
380
+ def timing_attack( strings, opts, &block )
381
+
382
+ opts[:timeout_divider] ||= 1
383
+ [strings].flatten.each {
384
+ |str|
385
+
386
+ opts[:timing_string] = str
387
+ str = str.gsub( '__TIME__', ( (opts[:timeout] + 3000) / opts[:timeout_divider] ).to_s )
388
+ audit( str, opts ) {
389
+ |res, opts, elem|
390
+ block.call( res, opts, elem ) if block && res.timed_out?
391
+ }
392
+ }
393
+ end
394
+
395
+ #
396
+ # Provides the following methods:
397
+ # * audit_links()
398
+ # * audit_forms()
399
+ # * audit_cookies()
400
+ # * audit_headers()
401
+ #
402
+ # Metaprogrammed to avoid redundant code while maintaining compatibility
403
+ # and method shortcuts.
404
+ #
405
+ # @see #audit_elems
406
+ #
407
+ def method_missing( sym, *args, &block )
408
+
409
+ elem = sym.to_s.gsub!( 'audit_', '@' )
410
+ raise NoMethodError.new( "Undefined method '#{sym.to_s}'.", sym, args ) if !elem
411
+
412
+ elems = @page.instance_variable_get( elem )
413
+
414
+ if( elems && elem )
415
+ raise ArgumentError.new( "Missing required argument 'injection_str'" +
416
+ " for audit_#{elem.gsub( '@', '' )}()." ) if( !args[0] )
417
+ audit_elems( elems, args[0], args[1] ? args[1]: {}, &block )
418
+ else
419
+ raise NoMethodError.new( "Undefined method '#{sym.to_s}'.", sym, args )
420
+ end
421
+ end
422
+
423
+ #
424
+ # Audits Auditalble HTML/HTTP elements
425
+ #
426
+ # @param [Array<Arachni::Element::Auditable>] elements auditable elements to audit
427
+ # @param [String] injection_str the string to be injected
428
+ # @param [Hash] opts options as described in {OPTIONS}
429
+ # @param [Block] &block block to be passed the:
430
+ # * HTTP response
431
+ # * name of the input vector
432
+ # * updated opts
433
+ # The block will be called as soon as the
434
+ # HTTP response is received.
435
+ #
436
+ # @see #method_missing
437
+ #
438
+ def audit_elems( elements, injection_str, opts = { }, &block )
439
+
440
+ opts = OPTIONS.merge( opts )
441
+ url = @page.url
442
+
443
+ opts[:injected_orig] = injection_str
444
+
445
+ elements.each{
446
+ |elem|
447
+ elem.auditor( self )
448
+ elem.audit( injection_str, opts, &block )
449
+ }
450
+ end
451
+
452
+ end
453
+
454
+ end
455
+ end