watobo 0.9.19 → 0.9.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (266) hide show
  1. data/CHANGELOG.md +104 -0
  2. data/bin/nfq_server.rb +8 -20
  3. data/bin/watobo_gui.rb +8 -20
  4. data/config/forwarding_proxy.yml +2 -2
  5. data/lib/watobo.rb +12 -22
  6. data/lib/watobo/adapters.rb +12 -24
  7. data/lib/watobo/adapters/data_store.rb +76 -66
  8. data/lib/watobo/adapters/file/file_store.rb +295 -307
  9. data/lib/watobo/adapters/session_store.rb +13 -25
  10. data/lib/watobo/ca.rb +9 -21
  11. data/lib/watobo/config.rb +205 -217
  12. data/lib/watobo/constants.rb +8 -20
  13. data/lib/watobo/core.rb +11 -23
  14. data/lib/watobo/core/active_check.rb +11 -21
  15. data/lib/watobo/core/active_checks.rb +57 -69
  16. data/lib/watobo/core/ca.rb +388 -398
  17. data/lib/watobo/core/cert_store.rb +42 -54
  18. data/lib/watobo/core/chat.rb +100 -112
  19. data/lib/watobo/core/chats.rb +271 -275
  20. data/lib/watobo/core/client_cert_store.rb +33 -45
  21. data/lib/watobo/core/conversation.rb +56 -68
  22. data/lib/watobo/core/cookie.rb +31 -43
  23. data/lib/watobo/core/finding.rb +74 -86
  24. data/lib/watobo/core/findings.rb +113 -125
  25. data/lib/watobo/core/forwarding_proxy.rb +44 -35
  26. data/lib/watobo/core/fuzz_gen.rb +8 -20
  27. data/lib/watobo/core/intercept_carver.rb +176 -188
  28. data/lib/watobo/core/intercept_filter.rb +243 -255
  29. data/lib/watobo/core/interceptor.rb +106 -118
  30. data/lib/watobo/core/min_class.rb +12 -24
  31. data/lib/watobo/core/netfilter_queue.rb +178 -190
  32. data/lib/watobo/core/ott_cache.rb +152 -148
  33. data/lib/watobo/core/parameter.rb +53 -58
  34. data/lib/watobo/core/passive_check.rb +8 -20
  35. data/lib/watobo/core/passive_checks.rb +56 -68
  36. data/lib/watobo/core/passive_scanner.rb +54 -66
  37. data/lib/watobo/core/plugin.rb +19 -31
  38. data/lib/watobo/core/project.rb +8 -20
  39. data/lib/watobo/core/proxy.rb +51 -63
  40. data/lib/watobo/core/request.rb +128 -120
  41. data/lib/watobo/core/response.rb +59 -61
  42. data/lib/watobo/core/scanner.rb +8 -20
  43. data/lib/watobo/core/scanner3.rb +413 -425
  44. data/lib/watobo/core/scope.rb +91 -103
  45. data/lib/watobo/core/session.rb +109 -87
  46. data/lib/watobo/core/sid_cache.rb +106 -118
  47. data/lib/watobo/core/subscriber.rb +33 -45
  48. data/lib/watobo/defaults.rb +29 -41
  49. data/lib/watobo/external/diff/lcs.rb +8 -20
  50. data/lib/watobo/external/diff/lcs/array.rb +8 -20
  51. data/lib/watobo/external/diff/lcs/block.rb +8 -20
  52. data/lib/watobo/external/diff/lcs/callbacks.rb +8 -20
  53. data/lib/watobo/external/diff/lcs/change.rb +8 -20
  54. data/lib/watobo/external/diff/lcs/hunk.rb +8 -20
  55. data/lib/watobo/external/diff/lcs/ldiff.rb +8 -20
  56. data/lib/watobo/external/diff/lcs/string.rb +8 -20
  57. data/lib/watobo/externals.rb +14 -26
  58. data/lib/watobo/framework.rb +12 -24
  59. data/lib/watobo/framework/create_project.rb +68 -80
  60. data/lib/watobo/framework/init.rb +8 -20
  61. data/lib/watobo/framework/init_modules.rb +8 -20
  62. data/lib/watobo/framework/license_text.rb +36 -48
  63. data/lib/watobo/framework/load_chat.rb +21 -33
  64. data/lib/watobo/gui.rb +121 -133
  65. data/lib/watobo/gui/about_watobo.rb +8 -20
  66. data/lib/watobo/gui/browser_preview.rb +8 -20
  67. data/lib/watobo/gui/certificate_dialog.rb +8 -20
  68. data/lib/watobo/gui/chat_diff.rb +11 -21
  69. data/lib/watobo/gui/chatviewer_frame.rb +10 -22
  70. data/lib/watobo/gui/checkboxtree.rb +8 -20
  71. data/lib/watobo/gui/checks_policy_frame.rb +8 -20
  72. data/lib/watobo/gui/client_cert_dialog.rb +10 -21
  73. data/lib/watobo/gui/confirm_scan_dialog.rb +8 -20
  74. data/lib/watobo/gui/conversation_table.rb +54 -44
  75. data/lib/watobo/gui/conversation_table_ctrl.rb +215 -227
  76. data/lib/watobo/gui/conversation_table_ctrl2.rb +385 -393
  77. data/lib/watobo/gui/csrf_token_dialog.rb +11 -25
  78. data/lib/watobo/gui/custom_viewer.rb +357 -369
  79. data/lib/watobo/gui/dashboard.rb +8 -20
  80. data/lib/watobo/gui/define_scope_frame.rb +8 -20
  81. data/lib/watobo/gui/differ_frame.rb +223 -235
  82. data/lib/watobo/gui/edit_comment.rb +8 -20
  83. data/lib/watobo/gui/edit_scope_dialog.rb +8 -20
  84. data/lib/watobo/gui/export_dialog.rb +114 -0
  85. data/lib/watobo/gui/finding_info.rb +9 -21
  86. data/lib/watobo/gui/findings_tree.rb +8 -20
  87. data/lib/watobo/gui/full_scan_dialog.rb +8 -20
  88. data/lib/watobo/gui/fuzzer_gui.rb +8 -20
  89. data/lib/watobo/gui/goto_url_dialog.rb +78 -90
  90. data/lib/watobo/gui/hex_viewer.rb +25 -27
  91. data/lib/watobo/gui/html_viewer.rb +295 -307
  92. data/lib/watobo/gui/intercept_filter_dialog.rb +196 -208
  93. data/lib/watobo/gui/interceptor_gui.rb +1046 -1041
  94. data/lib/watobo/gui/interceptor_settings_dialog.rb +8 -20
  95. data/lib/watobo/gui/list_box.rb +109 -121
  96. data/lib/watobo/gui/log_file_viewer.rb +40 -52
  97. data/lib/watobo/gui/log_viewer.rb +87 -99
  98. data/lib/watobo/gui/login_wizzard.rb +8 -20
  99. data/lib/watobo/gui/main_window.rb +34 -33
  100. data/lib/watobo/gui/manual_request_editor.rb +25 -35
  101. data/lib/watobo/gui/master_pw_dialog.rb +8 -20
  102. data/lib/watobo/gui/mixins/gui_settings.rb +37 -49
  103. data/lib/watobo/gui/page_tree.rb +225 -237
  104. data/lib/watobo/gui/password_policy_dialog.rb +8 -20
  105. data/lib/watobo/gui/plugin_board.rb +8 -20
  106. data/lib/watobo/gui/preferences_dialog.rb +8 -20
  107. data/lib/watobo/gui/progress_window.rb +8 -20
  108. data/lib/watobo/gui/project_wizzard.rb +8 -20
  109. data/lib/watobo/gui/proxy_dialog.rb +117 -85
  110. data/lib/watobo/gui/quick_scan_dialog.rb +8 -20
  111. data/lib/watobo/gui/request_builder_frame.rb +125 -122
  112. data/lib/watobo/gui/request_editor.rb +53 -28
  113. data/lib/watobo/gui/rewrite_filters_dialog.rb +402 -414
  114. data/lib/watobo/gui/rewrite_rules_dialog.rb +380 -392
  115. data/lib/watobo/gui/save_chat_dialog.rb +148 -160
  116. data/lib/watobo/gui/scanner_settings_dialog.rb +8 -20
  117. data/lib/watobo/gui/select_chat_dialog.rb +8 -20
  118. data/lib/watobo/gui/session_management_dialog.rb +8 -20
  119. data/lib/watobo/gui/sites_tree.rb +118 -22
  120. data/lib/watobo/gui/status_bar.rb +8 -20
  121. data/lib/watobo/gui/table_editor.rb +76 -53
  122. data/lib/watobo/gui/tagless_viewer.rb +10 -21
  123. data/lib/watobo/gui/templates/plugin.rb +8 -20
  124. data/lib/watobo/gui/templates/plugin2.rb +99 -111
  125. data/lib/watobo/gui/templates/plugin_base.rb +152 -164
  126. data/lib/watobo/gui/text_viewer.rb +8 -20
  127. data/lib/watobo/gui/transcoder_window.rb +15 -22
  128. data/lib/watobo/gui/utils/gui_utils.rb +8 -20
  129. data/lib/watobo/gui/utils/init_icons.rb +94 -106
  130. data/lib/watobo/gui/utils/load_icons.rb +41 -53
  131. data/lib/watobo/gui/utils/load_plugins.rb +118 -130
  132. data/lib/watobo/gui/utils/master_password.rb +76 -88
  133. data/lib/watobo/gui/utils/save_default_settings.rb +121 -133
  134. data/lib/watobo/gui/utils/save_project_settings.rb +8 -20
  135. data/lib/watobo/gui/utils/save_proxy_settings.rb +53 -21
  136. data/lib/watobo/gui/utils/save_scanner_settings.rb +26 -38
  137. data/lib/watobo/gui/utils/session_history.rb +120 -132
  138. data/lib/watobo/gui/workspace_dialog.rb +8 -20
  139. data/lib/watobo/gui/www_auth_dialog.rb +8 -20
  140. data/lib/watobo/gui/xml_viewer_frame.rb +8 -20
  141. data/lib/watobo/http.rb +12 -23
  142. data/lib/watobo/http/cookies/cookies.rb +63 -70
  143. data/lib/watobo/http/data/data.rb +56 -64
  144. data/lib/watobo/http/data/json.rb +51 -0
  145. data/lib/watobo/http/url/url.rb +46 -58
  146. data/lib/watobo/http/xml/xml.rb +129 -141
  147. data/lib/watobo/interceptor.rb +11 -23
  148. data/lib/watobo/interceptor/proxy.rb +624 -625
  149. data/lib/watobo/interceptor/transparent.rb +22 -34
  150. data/lib/watobo/mixins.rb +18 -30
  151. data/lib/watobo/mixins/check_info.rb +35 -47
  152. data/lib/watobo/mixins/httpparser.rb +42 -35
  153. data/lib/watobo/mixins/request_parser.rb +8 -20
  154. data/lib/watobo/mixins/shapers.rb +484 -477
  155. data/lib/watobo/mixins/transcoders.rb +8 -20
  156. data/lib/watobo/parser.rb +9 -21
  157. data/lib/watobo/parser/html.rb +91 -103
  158. data/lib/watobo/sockets.rb +11 -23
  159. data/lib/watobo/sockets/agent.rb +836 -848
  160. data/lib/watobo/sockets/client_socket.rb +283 -277
  161. data/lib/watobo/sockets/connection.rb +409 -421
  162. data/lib/watobo/sockets/http_socket.rb +16 -23
  163. data/lib/watobo/sockets/ntlm_auth.rb +137 -149
  164. data/lib/watobo/utils.rb +18 -30
  165. data/lib/watobo/utils/check_regex.rb +8 -20
  166. data/lib/watobo/utils/copy_object.rb +8 -20
  167. data/lib/watobo/utils/crypto.rb +8 -20
  168. data/lib/watobo/utils/expand_range.rb +31 -43
  169. data/lib/watobo/utils/export_xml.rb +108 -0
  170. data/lib/watobo/utils/file_management.rb +8 -20
  171. data/lib/watobo/utils/hexprint.rb +17 -29
  172. data/lib/watobo/utils/load_chat.rb +8 -20
  173. data/lib/watobo/utils/load_icon.rb +8 -20
  174. data/lib/watobo/{external/ntlm → utils}/ntlm.rb +874 -796
  175. data/lib/watobo/utils/print_debug.rb +20 -32
  176. data/lib/watobo/utils/response_builder.rb +98 -110
  177. data/lib/watobo/utils/response_hash.rb +9 -20
  178. data/lib/watobo/utils/secure_eval.rb +10 -22
  179. data/lib/watobo/utils/strings.rb +18 -30
  180. data/lib/watobo/utils/text2request.rb +12 -20
  181. data/lib/watobo/utils/url.rb +31 -43
  182. data/lib/watobo/utils/utf16.rb +22 -0
  183. data/modules/active/Apache/mod_status.rb +9 -0
  184. data/modules/active/Apache/multiview.rb +161 -0
  185. data/modules/active/Flash/crossdomain.rb +9 -0
  186. data/modules/active/directories/dirwalker.rb +8 -20
  187. data/modules/active/discovery/fileextensions.rb +10 -22
  188. data/modules/active/discovery/http_methods.rb +8 -20
  189. data/modules/active/domino/domino_db.rb +8 -20
  190. data/modules/active/dotNET/custom_errors.rb +110 -122
  191. data/modules/active/dotNET/dotnet_files.rb +98 -110
  192. data/modules/active/fileinclusion/lfi_simple.rb +8 -20
  193. data/modules/active/jboss/jboss_basic.rb +8 -20
  194. data/modules/active/sap/business_objects.rb +63 -0
  195. data/modules/active/sap/its_commands.rb +8 -20
  196. data/modules/active/sap/its_service_parameter.rb +8 -20
  197. data/modules/active/sap/its_services.rb +8 -20
  198. data/modules/active/sap/its_xss.rb +8 -20
  199. data/modules/active/shell_shock/shell_shock.rb +149 -0
  200. data/modules/active/siebel/siebel_apps.rb +168 -180
  201. data/modules/active/sqlinjection/sql_boolean.rb +9 -21
  202. data/modules/active/sqlinjection/sqli_error.rb +10 -22
  203. data/modules/active/sqlinjection/sqli_timing.rb +228 -240
  204. data/modules/active/struts2/default_handler_ognl.rb +114 -126
  205. data/modules/active/struts2/include_params_ognl.rb +113 -125
  206. data/modules/active/xml/xml_xxe.rb +122 -127
  207. data/modules/active/xss/xss_ng.rb +223 -234
  208. data/modules/active/xss/xss_simple.rb +8 -20
  209. data/modules/passive/ajax.rb +76 -84
  210. data/modules/passive/autocomplete.rb +64 -76
  211. data/modules/passive/cookie_options.rb +8 -20
  212. data/modules/passive/cookie_xss.rb +9 -21
  213. data/modules/passive/detect_code.rb +9 -21
  214. data/modules/passive/detect_fileupload.rb +11 -22
  215. data/modules/passive/detect_infrastructure.rb +23 -35
  216. data/modules/passive/detect_one_time_tokens.rb +8 -20
  217. data/modules/passive/dirindexing.rb +9 -21
  218. data/modules/passive/disclosure_domino.rb +66 -79
  219. data/modules/passive/disclosure_emails.rb +9 -21
  220. data/modules/passive/disclosure_ipaddr.rb +15 -23
  221. data/modules/passive/filename_as_parameter.rb +8 -20
  222. data/modules/passive/form_spotter.rb +15 -21
  223. data/modules/passive/hidden_fields.rb +64 -70
  224. data/modules/passive/hotspots.rb +13 -22
  225. data/modules/passive/in_script_parameter.rb +15 -24
  226. data/modules/passive/multiple_server_headers.rb +8 -20
  227. data/modules/passive/possible_login.rb +12 -23
  228. data/modules/passive/redirect_url.rb +10 -22
  229. data/modules/passive/redirectionz.rb +9 -21
  230. data/modules/passive/sap-headers.rb +64 -76
  231. data/modules/passive/xss_dom.rb +10 -21
  232. data/plugins/catalog/catalog.rb +17 -23
  233. data/plugins/crawler/crawler.rb +12 -24
  234. data/plugins/crawler/gui.rb +13 -25
  235. data/plugins/crawler/gui/auth_frame.rb +278 -290
  236. data/plugins/crawler/gui/crawler_gui.rb +302 -320
  237. data/plugins/crawler/gui/general_settings_frame.rb +104 -116
  238. data/plugins/crawler/gui/hooks_frame.rb +88 -100
  239. data/plugins/crawler/gui/scope_frame.rb +58 -70
  240. data/plugins/crawler/gui/settings_tabbook.rb +46 -58
  241. data/plugins/crawler/gui/status_frame.rb +67 -78
  242. data/plugins/crawler/lib/bags.rb +26 -38
  243. data/plugins/crawler/lib/constants.rb +19 -31
  244. data/plugins/crawler/lib/engine.rb +505 -508
  245. data/plugins/crawler/lib/grabber.rb +77 -87
  246. data/plugins/crawler/lib/status.rb +82 -0
  247. data/plugins/crawler/lib/uri_mp.rb +20 -32
  248. data/plugins/filefinder/dbs/siebel_paths.txt +1118 -0
  249. data/plugins/filefinder/dbs/subs-big.lst +31986 -0
  250. data/plugins/filefinder/filefinder.rb +13 -23
  251. data/plugins/sqlmap/bin/test.rb +86 -98
  252. data/plugins/sqlmap/gui.rb +12 -24
  253. data/plugins/sqlmap/gui/main.rb +226 -238
  254. data/plugins/sqlmap/gui/options_frame.rb +105 -117
  255. data/plugins/sqlmap/lib/sqlmap_ctrl.rb +103 -115
  256. data/plugins/sqlmap/sqlmap.rb +10 -22
  257. data/plugins/sslchecker/cli/sslchecker_cli.rb +8 -20
  258. data/plugins/sslchecker/gui/cipher_table.rb +252 -264
  259. data/plugins/sslchecker/gui/gui.rb +267 -276
  260. data/plugins/sslchecker/gui/sslchecker.rb +12 -24
  261. data/plugins/sslchecker/lib/check.rb +172 -80
  262. data/plugins/wshell/gui/main.rb +115 -127
  263. data/plugins/wshell/lib/core.rb +85 -97
  264. data/plugins/wshell/wshell.rb +19 -31
  265. metadata +14 -6
  266. data/.yardopts +0 -24
@@ -1,24 +1,12 @@
1
- # .
1
+ #.
2
2
  # crypto.rb
3
- #
4
- # Copyright 2013 by siberas, http://www.siberas.de
5
- #
6
- # This file is part of WATOBO (Web Application Tool Box)
7
- # http://watobo.sourceforge.com
8
- #
9
- # WATOBO is free software; you can redistribute it and/or modify
10
- # it under the terms of the GNU General Public License as published by
11
- # the Free Software Foundation version 2 of the License.
12
- #
13
- # WATOBO is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with WATOBO; if not, write to the Free Software
20
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
- # .
3
+ #.
4
+ # Copyright 2014 by siberas, http://www.siberas.de
5
+ # This file is part of WATOBO (Web Application Tool Box) http://watobo.sourceforge.com
6
+ # WATOBO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License.
7
+ # WATOBO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
+ # You should have received a copy of the GNU General Public License along with WATOBO; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
9
+
22
10
  require 'openssl'
23
11
  require 'digest/sha1'
24
12
  require 'base64'
@@ -1,46 +1,34 @@
1
- # .
1
+ #.
2
2
  # expand_range.rb
3
- #
4
- # Copyright 2013 by siberas, http://www.siberas.de
5
- #
6
- # This file is part of WATOBO (Web Application Tool Box)
7
- # http://watobo.sourceforge.com
8
- #
9
- # WATOBO is free software; you can redistribute it and/or modify
10
- # it under the terms of the GNU General Public License as published by
11
- # the Free Software Foundation version 2 of the License.
12
- #
13
- # WATOBO is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with WATOBO; if not, write to the Free Software
20
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
- # .
22
- # @private
23
- module Watobo#:nodoc: all
24
- module Utils
25
- # expand range creates an array out of
26
- def self.expand_range(pattern)
27
- vals = pattern.split(",")
28
-
29
- result = []
30
- vals.each do |v|
31
- v.strip!
32
- if v =~ /^(\d+)$/ then
33
- result.push $1.to_i
34
- elsif v =~ /^(\d+)-(\d+)$/
35
- start = $1
36
- stop = $2
37
- dummy = (start..stop).to_a
38
- result.concat dummy
39
- end
40
- end
41
- result.uniq!
42
- return result
43
- end
3
+ #.
4
+ # Copyright 2014 by siberas, http://www.siberas.de
5
+ # This file is part of WATOBO (Web Application Tool Box) http://watobo.sourceforge.com
6
+ # WATOBO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License.
7
+ # WATOBO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
+ # You should have received a copy of the GNU General Public License along with WATOBO; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
44
9
 
45
- end
10
+ # @private
11
+ module Watobo#:nodoc: all
12
+ module Utils
13
+ # expand range creates an array out of
14
+ def self.expand_range(pattern)
15
+ vals = pattern.split(",")
16
+
17
+ result = []
18
+ vals.each do |v|
19
+ v.strip!
20
+ if v =~ /^(\d+)$/ then
21
+ result.push $1.to_i
22
+ elsif v =~ /^(\d+)-(\d+)$/
23
+ start = $1
24
+ stop = $2
25
+ dummy = (start..stop).to_a
26
+ result.concat dummy
27
+ end
28
+ end
29
+ result.uniq!
30
+ return result
31
+ end
32
+
33
+ end
46
34
  end
@@ -0,0 +1,108 @@
1
+ #.
2
+ # export_xml.rb
3
+ #.
4
+ # Copyright 2014 by siberas, http://www.siberas.de
5
+ # This file is part of WATOBO (Web Application Tool Box) http://watobo.sourceforge.com
6
+ # WATOBO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License.
7
+ # WATOBO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
+ # You should have received a copy of the GNU General Public License along with WATOBO; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
9
+
10
+ module Watobo
11
+ module Utils
12
+ def self.finding2xml(finding,xml)
13
+ fnode = Nokogiri::XML::Node.new("Finding", xml)
14
+
15
+ dnode = Nokogiri::XML::Node.new("Request", xml)
16
+ dnode.content = Base64.strict_encode64( finding.request.join )
17
+ fnode << dnode
18
+
19
+ dnode = Nokogiri::XML::Node.new("Response", xml)
20
+ dnode.content = Base64.strict_encode64 finding.response.join
21
+ fnode << dnode
22
+
23
+ dnode = Nokogiri::XML::Node.new("Details", xml)
24
+ finding.details.each do |k,v|
25
+ d = Nokogiri::XML::Node.new(k.to_s, xml)
26
+ d.content = v
27
+ dnode << d
28
+ end
29
+ fnode << dnode
30
+
31
+ fnode
32
+ end
33
+
34
+ def self.chat2xml(chat,xml)
35
+ fnode = Nokogiri::XML::Node.new("Chat", xml)
36
+
37
+ dnode = Nokogiri::XML::Node.new("Request", xml)
38
+ dnode.content = Base64.strict_encode64( chat.request.join )
39
+ fnode << dnode
40
+
41
+ dnode = Nokogiri::XML::Node.new("Response", xml)
42
+ dnode.content = Base64.strict_encode64 chat.response.join
43
+ fnode << dnode
44
+
45
+ dnode = Nokogiri::XML::Node.new("Details", xml)
46
+ chat.settings.each do |k,v|
47
+ d = Nokogiri::XML::Node.new(k.to_s, xml)
48
+ d.content = v
49
+ dnode << d
50
+ end
51
+ fnode << dnode
52
+
53
+ fnode
54
+ end
55
+
56
+ def self.exportXML(*prefs)
57
+ # prefs ||= []
58
+ xml = Nokogiri::XML("")
59
+ env = Nokogiri::XML::Node.new("WatoboExportv1", xml)
60
+ xml << env
61
+
62
+ if prefs.include? :export_findings
63
+
64
+ findings = Nokogiri::XML::Node.new("Findings", xml)
65
+ env << findings
66
+
67
+ Watobo::Findings.each do |fid, finding|
68
+ if prefs.include? :scope_only
69
+ if Watobo::Scope.match_site?(finding.request.site)
70
+ if prefs.include? :ignore_fps
71
+ unless finding.false_positive?
72
+ findings << finding2xml(finding, xml)
73
+ end
74
+ else
75
+ findings << finding2xml(finding, xml)
76
+ end
77
+ end
78
+ else
79
+ if prefs.include? :ignore_fps
80
+ unless finding.false_positive?
81
+ findings << finding2xml(finding, xml)
82
+ end
83
+ else
84
+ findings << finding2xml(finding, xml)
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ chats = Nokogiri::XML::Node.new("Chats", xml)
91
+ env << chats
92
+
93
+ if prefs.include? :export_chats
94
+ Watobo::Chats.each do |chat|
95
+ if prefs.include? :scope_only
96
+ if Watobo::Scope.match_site?(chat.request.site)
97
+ chats << chat2xml(chat, xml)
98
+ end
99
+ else
100
+ chats << chat2xml(chat, xml)
101
+ end
102
+ end
103
+ end
104
+
105
+ xml
106
+ end
107
+ end
108
+ end
@@ -1,24 +1,12 @@
1
- # .
1
+ #.
2
2
  # file_management.rb
3
- #
4
- # Copyright 2013 by siberas, http://www.siberas.de
5
- #
6
- # This file is part of WATOBO (Web Application Tool Box)
7
- # http://watobo.sourceforge.com
8
- #
9
- # WATOBO is free software; you can redistribute it and/or modify
10
- # it under the terms of the GNU General Public License as published by
11
- # the Free Software Foundation version 2 of the License.
12
- #
13
- # WATOBO is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with WATOBO; if not, write to the Free Software
20
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
- # .
3
+ #.
4
+ # Copyright 2014 by siberas, http://www.siberas.de
5
+ # This file is part of WATOBO (Web Application Tool Box) http://watobo.sourceforge.com
6
+ # WATOBO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License.
7
+ # WATOBO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
+ # You should have received a copy of the GNU General Public License along with WATOBO; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
9
+
22
10
  # @private
23
11
  module Watobo#:nodoc: all
24
12
  module Utils
@@ -1,32 +1,20 @@
1
- # .
1
+ #.
2
2
  # hexprint.rb
3
- #
4
- # Copyright 2013 by siberas, http://www.siberas.de
5
- #
6
- # This file is part of WATOBO (Web Application Tool Box)
7
- # http://watobo.sourceforge.com
8
- #
9
- # WATOBO is free software; you can redistribute it and/or modify
10
- # it under the terms of the GNU General Public License as published by
11
- # the Free Software Foundation version 2 of the License.
12
- #
13
- # WATOBO is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with WATOBO; if not, write to the Free Software
20
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
- # .
3
+ #.
4
+ # Copyright 2014 by siberas, http://www.siberas.de
5
+ # This file is part of WATOBO (Web Application Tool Box) http://watobo.sourceforge.com
6
+ # WATOBO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License.
7
+ # WATOBO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
+ # You should have received a copy of the GNU General Public License along with WATOBO; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
9
+
22
10
  # @private
23
- module Watobo#:nodoc: all
24
- module Utils
25
- def self.hexprint(data)
26
- data.length.times do |i|
27
- print "%02X" % data[i].ord
28
- puts if data[i] == "\n"
29
- end
30
- end
31
- end
11
+ module Watobo#:nodoc: all
12
+ module Utils
13
+ def self.hexprint(data)
14
+ data.length.times do |i|
15
+ print "%02X" % data[i].ord
16
+ puts if data[i] == "\n"
17
+ end
18
+ end
19
+ end
32
20
  end
@@ -1,24 +1,12 @@
1
- # .
1
+ #.
2
2
  # load_chat.rb
3
- #
4
- # Copyright 2013 by siberas, http://www.siberas.de
5
- #
6
- # This file is part of WATOBO (Web Application Tool Box)
7
- # http://watobo.sourceforge.com
8
- #
9
- # WATOBO is free software; you can redistribute it and/or modify
10
- # it under the terms of the GNU General Public License as published by
11
- # the Free Software Foundation version 2 of the License.
12
- #
13
- # WATOBO is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with WATOBO; if not, write to the Free Software
20
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
- # .
3
+ #.
4
+ # Copyright 2014 by siberas, http://www.siberas.de
5
+ # This file is part of WATOBO (Web Application Tool Box) http://watobo.sourceforge.com
6
+ # WATOBO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License.
7
+ # WATOBO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
+ # You should have received a copy of the GNU General Public License along with WATOBO; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
9
+
22
10
  # @private
23
11
  module Watobo#:nodoc: all
24
12
  module Utils
@@ -1,24 +1,12 @@
1
- # .
1
+ #.
2
2
  # load_icon.rb
3
- #
4
- # Copyright 2013 by siberas, http://www.siberas.de
5
- #
6
- # This file is part of WATOBO (Web Application Tool Box)
7
- # http://watobo.sourceforge.com
8
- #
9
- # WATOBO is free software; you can redistribute it and/or modify
10
- # it under the terms of the GNU General Public License as published by
11
- # the Free Software Foundation version 2 of the License.
12
- #
13
- # WATOBO is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with WATOBO; if not, write to the Free Software
20
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
- # .
3
+ #.
4
+ # Copyright 2014 by siberas, http://www.siberas.de
5
+ # This file is part of WATOBO (Web Application Tool Box) http://watobo.sourceforge.com
6
+ # WATOBO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License.
7
+ # WATOBO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
+ # You should have received a copy of the GNU General Public License along with WATOBO; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
9
+
22
10
  # @private
23
11
  module Watobo#:nodoc: all
24
12
  module Utils
@@ -1,797 +1,875 @@
1
- # .
1
+ #.
2
2
  # ntlm.rb
3
- #
4
- # Copyright 2013 by siberas, http://www.siberas.de
5
- #
6
- # This file is part of WATOBO (Web Application Tool Box)
7
- # http://watobo.sourceforge.com
8
- #
9
- # WATOBO is free software; you can redistribute it and/or modify
10
- # it under the terms of the GNU General Public License as published by
11
- # the Free Software Foundation version 2 of the License.
12
- #
13
- # WATOBO is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with WATOBO; if not, write to the Free Software
20
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
- # .
22
- #
23
- # = net/ntlm.rb
24
- #
25
- # An NTLM Authentication Library for Ruby
26
- #
27
- # This code is a derivative of "dbf2.rb" written by yrock
28
- # and Minero Aoki. You can find original code here:
29
- # http://jp.rubyist.net/magazine/?0013-CodeReview
30
- # -------------------------------------------------------------
31
- # Copyright (c) 2005,2006 yrock
32
- #
33
- # This program is free software.
34
- # You can distribute/modify this program under the terms of the
35
- # Ruby License.
36
- #
37
- # 2006-02-11 refactored by Minero Aoki
38
- # -------------------------------------------------------------
39
- #
40
- # All protocol information used to write this code stems from
41
- # "The NTLM Authentication Protocol" by Eric Glass. The author
42
- # would thank to him for this tremendous work and making it
43
- # available on the net.
44
- # http://davenport.sourceforge.net/ntlm.html
45
- # -------------------------------------------------------------
46
- # Copyright (c) 2003 Eric Glass
47
- #
48
- # Permission to use, copy, modify, and distribute this document
49
- # for any purpose and without any fee is hereby granted,
50
- # provided that the above copyright notice and this list of
51
- # conditions appear in all copies.
52
- # -------------------------------------------------------------
53
- #
54
- # The author also looked Mozilla-Firefox-1.0.7 source code,
55
- # namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
56
- # Jonathan Bastien-Filiatrault's libntlm-ruby.
57
- # "http://x2a.org/websvn/filedetails.php?
58
- # repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
59
- # The latter has a minor bug in its separate_keys function.
60
- # The third key has to begin from the 14th character of the
61
- # input string instead of 13th:)
62
- #--
63
- # $Id: ntlm.rb,v 1.1 2006/10/05 01:36:52 koheik Exp $
64
- #++
65
-
66
- require 'base64'
67
- require 'openssl'
68
- require 'openssl/digest'
69
-
70
- require 'kconv'
71
-
72
- module Net #:nodoc:
73
- module NTLM
74
-
75
- module VERSION #:nodoc:
76
- MAJOR = 0
77
- MINOR = 1
78
- TINY = 1
79
- STRING = [MAJOR, MINOR, TINY].join('.')
80
- end
81
-
82
- SSP_SIGN = "NTLMSSP\0"
83
- BLOB_SIGN = 0x00000101
84
- LM_MAGIC = "KGS!@\#$%"
85
- TIME_OFFSET = 11644473600
86
- MAX64 = 0xffffffffffffffff
87
-
88
- FLAGS = {
89
- :UNICODE => 0x00000001,
90
- :OEM => 0x00000002,
91
- :REQUEST_TARGET => 0x00000004,
92
- # :UNKNOWN => 0x00000008,
93
- :SIGN => 0x00000010,
94
- :SEAL => 0x00000020,
95
- # :UNKNOWN => 0x00000040,
96
- :NETWARE => 0x00000100,
97
- :NTLM => 0x00000200,
98
- # :UNKNOWN => 0x00000400,
99
- # :UNKNOWN => 0x00000800,
100
- :DOMAIN_SUPPLIED => 0x00001000,
101
- :WORKSTATION_SUPPLIED => 0x00002000,
102
- :LOCAL_CALL => 0x00004000,
103
- :ALWAYS_SIGN => 0x00008000,
104
- :TARGET_TYPE_DOMAIN => 0x00010000,
105
- :TARGET_INFO => 0x00800000,
106
- :NTLM2_KEY => 0x00080000,
107
- :KEY128 => 0x20000000,
108
- :KEY56 => 0x80000000
109
- }
110
-
111
- FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] }
112
-
113
- DEFAULT_FLAGS = {
114
- :TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY],
115
- :TYPE2 => FLAGS[:UNICODE],
116
- :TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
117
- }
118
-
119
- # module functions
120
- class << self
121
- def decode_utf16le(str)
122
- Kconv.kconv(swap16(str), Kconv::ASCII, Kconv::UTF16)
123
- end
124
-
125
- def encode_utf16le(str)
126
- swap16(Kconv.kconv(str, Kconv::UTF16, Kconv::ASCII))
127
- end
128
-
129
- def pack_int64le(val)
130
- [val & 0x00000000ffffffff, val >> 32].pack("V2")
131
- end
132
-
133
- def swap16(str)
134
- str.unpack("v*").pack("n*")
135
- end
136
-
137
- def split7(str)
138
- s = str.dup
139
- until s.empty?
140
- (ret ||= []).push s.slice!(0, 7)
141
- end
142
- ret
143
- end
144
-
145
- def gen_keys(str)
146
- split7(str).map{ |str7|
147
- bits = split7(str7.unpack("B*")[0]).inject('')\
148
- {|ret, tkn| ret += tkn + (tkn.gsub('1', '').size % 2).to_s }
149
- [bits].pack("B*")
150
- }
151
- end
152
-
153
- def apply_des(plain, keys)
154
- dec = OpenSSL::Cipher::DES.new
155
- keys.map {|k|
156
- dec.key = k
157
- dec.encrypt.update(plain)
158
- }
159
- end
160
-
161
- def lm_hash(password)
162
- keys = gen_keys password.upcase.ljust(14, "\0")
163
- apply_des(LM_MAGIC, keys).join
164
- end
165
-
166
- def ntlm_hash(password, opt = {})
167
- pwd = password.dup
168
- unless opt[:unicode]
169
- pwd = encode_utf16le(pwd)
170
- end
171
- OpenSSL::Digest::MD4.digest pwd
172
- end
173
-
174
- def ntlmv2_hash(user, password, target, opt={})
175
- ntlmhash = ntlm_hash(password, opt)
176
- userdomain = (user + target).upcase
177
- unless opt[:unicode]
178
- userdomain = encode_utf16le(userdomain)
179
- end
180
- OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
181
- end
182
-
183
- # responses
184
- def lm_response(arg)
185
- begin
186
- hash = arg[:lm_hash]
187
- chal = arg[:challenge]
188
- rescue
189
- raise ArgumentError
190
- end
191
- chal = NTL::pack_int64le(chal) if chal.is_a?(Integer)
192
- keys = gen_keys hash.ljust(21, "\0")
193
- apply_des(chal, keys).join
194
- end
195
-
196
- def ntlm_response(arg)
197
- hash = arg[:ntlm_hash]
198
- chal = arg[:challenge]
199
- chal = NTL::pack_int64le(chal) if chal.is_a?(Integer)
200
- keys = gen_keys hash.ljust(21, "\0")
201
- apply_des(chal, keys).join
202
- end
203
-
204
- def ntlmv2_response(arg, opt = {})
205
- begin
206
- key = arg[:ntlmv2_hash]
207
- chal = arg[:challenge]
208
- ti = arg[:target_info]
209
- rescue
210
- raise ArgumentError
211
- end
212
- chal = NTL::pack_int64le(chal) if chal.is_a?(Integer)
213
-
214
- if opt[:client_challenge]
215
- cc = opt[:client_challenge]
216
- else
217
- cc = rand(MAX64)
218
- end
219
- cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
220
-
221
- if opt[:timestamp]
222
- ts = opt[:timestamp]
223
- else
224
- ts = Time.now.to_i
225
- end
226
- # epoch -> milsec from Jan 1, 1601
227
- ts = 10000000 * (ts + TIME_OFFSET)
228
-
229
- blob = Blob.new
230
- blob.timestamp = ts
231
- blob.challenge = cc
232
- blob.target_info = ti
233
-
234
- bb = blob.serialize
235
- OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
236
- end
237
-
238
- def lmv2_response(arg, opt = {})
239
- key = arg[:ntlmv2_hash]
240
- chal = arg[:challenge]
241
-
242
- chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
243
-
244
- if opt[:client_challenge]
245
- cc = opt[:client_challenge]
246
- else
247
- cc = rand(MAX64)
248
- end
249
- cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
250
-
251
- OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
252
- end
253
-
254
- def ntlm2_session(arg, opt = {})
255
- begin
256
- passwd_hash = arg[:ntlm_hash]
257
- chal = arg[:challenge]
258
- rescue
259
- raise ArgumentError
260
- end
261
-
262
- if opt[:client_challenge]
263
- cc = opt[:client_challenge]
264
- else
265
- cc = rand(MAX64)
266
- end
267
- cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
268
-
269
- keys = gen_keys passwd_hash.ljust(21, "\0")
270
- session_hash = OpenSSL::Digest::MD5.digest(chal + cc).slice(0, 8)
271
- response = apply_des(session_hash, keys).join
272
- [cc.ljust(24, "\0"), response]
273
- end
274
- end
275
-
276
-
277
- # base classes for primitives
278
- class Field
279
- attr_accessor :active, :value
280
-
281
- def initialize(opts)
282
- @value = opts[:value]
283
- @active = opts[:active].nil? ? true : opts[:active]
284
- end
285
-
286
- def size
287
- @active ? @size : 0
288
- end
289
- end
290
-
291
- class String < Field
292
- def initialize(opts)
293
- super(opts)
294
- @size = opts[:size]
295
- end
296
-
297
- def parse(str, offset=0)
298
- if @active and str.size >= offset + @size
299
- @value = str[offset, @size]
300
- @size
301
- else
302
- 0
303
- end
304
- end
305
-
306
- def serialize
307
- if @active
308
- @value
309
- else
310
- ""
311
- end
312
- end
313
-
314
- def value=(val)
315
- @value = val
316
- @size = @value.nil? ? 0 : @value.size
317
- @active = (@size > 0)
318
- end
319
- end
320
-
321
-
322
- class Int16LE < Field
323
- def initialize(opt)
324
- super(opt)
325
- @size = 2
326
- end
327
- def parse(str, offset=0)
328
- if @active and str.size >= offset + @size
329
- @value = str[offset, @size].unpack("v")[0]
330
- @size
331
- else
332
- 0
333
- end
334
- end
335
-
336
- def serialize
337
- [@value].pack("v")
338
- end
339
- end
340
-
341
- class Int32LE < Field
342
- def initialize(opt)
343
- super(opt)
344
- @size = 4
345
- end
346
-
347
- def parse(str, offset=0)
348
- if @active and str.size >= offset + @size
349
- @value = str.slice(offset, @size).unpack("V")[0]
350
- @size
351
- else
352
- 0
353
- end
354
- end
355
-
356
- def serialize
357
- [@value].pack("V") if @active
358
- end
359
- end
360
-
361
- class Int64LE < Field
362
- def initialize(opt)
363
- super(opt)
364
- @size = 8
365
- end
366
-
367
- def parse(str, offset=0)
368
- if @active and str.size >= offset + @size
369
- d, u = str.slice(offset, @size).unpack("V2")
370
- @value = (u * 0x100000000 + d)
371
- @size
372
- else
373
- 0
374
- end
375
- end
376
-
377
- def serialize
378
- [@value & 0x00000000ffffffff, @value >> 32].pack("V2") if @active
379
- end
380
- end
381
-
382
- # base class of data structure
383
- class FieldSet
384
- class << FieldSet
385
- def define(&block)
386
- c = Class.new(self)
387
- def c.inherited(subclass)
388
- proto = @proto
389
- subclass.instance_eval {
390
- @proto = proto
391
- }
392
- end
393
- c.module_eval(&block)
394
- c
395
- end
396
-
397
- def string(name, opts)
398
- add_field(name, String, opts)
399
- end
400
-
401
- def int16LE(name, opts)
402
- add_field(name, Int16LE, opts)
403
- end
404
-
405
- def int32LE(name, opts)
406
- add_field(name, Int32LE, opts)
407
- end
408
-
409
- def int64LE(name, opts)
410
- add_field(name, Int64LE, opts)
411
- end
412
-
413
- def security_buffer(name, opts)
414
- add_field(name, SecurityBuffer, opts)
415
- end
416
-
417
- def prototypes
418
- @proto
419
- end
420
-
421
- def names
422
- @proto.map{|n, t, o| n}
423
- end
424
-
425
- def types
426
- @proto.map{|n, t, o| t}
427
- end
428
-
429
- def opts
430
- @proto.map{|n, t, o| o}
431
- end
432
-
433
- private
434
-
435
- def add_field(name, type, opts)
436
- (@proto ||= []).push [name, type, opts]
437
- define_accessor name
438
- end
439
-
440
- def define_accessor(name)
441
- module_eval(<<-End, __FILE__, __LINE__ + 1)
442
- def #{name}
443
- self['#{name}'].value
444
- end
445
-
446
- def #{name}=(val)
447
- self['#{name}'].value = val
448
- end
449
- End
450
- end
451
- end
452
-
453
- def initialize
454
- @alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
455
- end
456
-
457
- def serialize
458
- @alist.map{|n, f| f.serialize }.join
459
- end
460
-
461
- def parse(str, offset=0)
462
- @alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
463
- end
464
-
465
- def size
466
- @alist.inject(0){|sum, a| sum += a[1].size}
467
- end
468
-
469
- def [](name)
470
- a = @alist.assoc(name.to_s.intern)
471
- raise ArgumentError, "no such field: #{name}" unless a
472
- a[1]
473
- end
474
-
475
- def []=(name, val)
476
- a = @alist.assoc(name.to_s.intern)
477
- raise ArgumentError, "no such field: #{name}" unless a
478
- a[1] = val
479
- end
480
-
481
- def enable(name)
482
- self[name].active = true
483
- end
484
-
485
- def disable(name)
486
- self[name].active = false
487
- end
488
- end
489
-
490
-
491
- Blob = FieldSet.define {
492
- int32LE :blob_signature, {:value => BLOB_SIGN}
493
- int32LE :reserved, {:value => 0}
494
- int64LE :timestamp, {:value => 0}
495
- string :challenge, {:value => "", :size => 8}
496
- int32LE :unknown1, {:value => 0}
497
- string :target_info, {:value => "", :size => 0}
498
- int32LE :unknown2, {:value => 0}
499
- }
500
-
501
- SecurityBuffer = FieldSet.define {
502
- int16LE :length, {:value => 0}
503
- int16LE :allocated, {:value => 0}
504
- int32LE :offset, {:value => 0}
505
- }
506
-
507
- class SecurityBuffer
508
- attr_accessor :active
509
- def initialize(opts)
510
- super()
511
- @value = opts[:value]
512
- @active = opts[:active].nil? ? true : opts[:active]
513
- @size = 8
514
- end
515
-
516
- def parse(str, offset=0)
517
- if @active and str.size >= offset + @size
518
- super(str, offset)
519
- @value = str[self.offset, self.length]
520
- @size
521
- else
522
- 0
523
- end
524
- end
525
-
526
- def serialize
527
- super if @active
528
- end
529
-
530
- def value
531
- @value
532
- end
533
-
534
- def value=(val)
535
- @value = val
536
- self.length = self.allocated = val.size
537
- end
538
-
539
- def data_size
540
- @active ? @value.size : 0
541
- end
542
- end
543
-
544
- class Message < FieldSet
545
- class << Message
546
- def parse(str)
547
- m = Type0.new
548
- m.parse(str)
549
- case m.type
550
- when 1
551
- t = Type1.parse(str)
552
- when 2
553
- t = Type2.parse(str)
554
- when 3
555
- t = Type3.parse(str)
556
- else
557
- raise ArgumentError, "unknown type: #{m.type}"
558
- end
559
- t
560
- end
561
-
562
- def decode64(str)
563
- parse(Base64.decode64(str))
564
- end
565
- end
566
-
567
- def has_flag?(flag)
568
- (self[:flag].value & FLAGS[flag]) == FLAGS[flag]
569
- end
570
-
571
- def set_flag(flag)
572
- self[:flag].value |= FLAGS[flag]
573
- end
574
-
575
- def dump_flags
576
- FLAG_KEYS.each{ |k| print(k, "=", flag?(k), "\n") }
577
- end
578
-
579
- def serialize
580
- deflag
581
- super + security_buffers.map{|n, f| f.value}.join
582
- end
583
-
584
- def encode64
585
- Base64.encode64(serialize).gsub(/\n/, '')
586
- end
587
-
588
- def decode64(str)
589
- parse(Base64.decode64(str))
590
- end
591
-
592
- alias head_size size
593
-
594
- def data_size
595
- security_buffers.inject(0){|sum, a| sum += a[1].data_size}
596
- end
597
-
598
- def size
599
- head_size + data_size
600
- end
601
-
602
-
603
- private
604
-
605
- def security_buffers
606
- @alist.find_all{|n, f| f.instance_of?(SecurityBuffer)}
607
- end
608
-
609
- def deflag
610
- security_buffers.inject(head_size){|cur, a|
611
- a[1].offset = cur
612
- cur += a[1].data_size
613
- }
614
- end
615
-
616
- def data_edge
617
- security_buffers.map{ |n, f| f.active ? f.offset : size}.min
618
- end
619
-
620
- # sub class definitions
621
-
622
- Type0 = Message.define {
623
- string :sign, {:size => 8, :value => SSP_SIGN}
624
- int32LE :type, {:value => 0}
625
- }
626
-
627
- Type1 = Message.define {
628
- string :sign, {:size => 8, :value => SSP_SIGN}
629
- int32LE :type, {:value => 1}
630
- int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE1] }
631
- security_buffer :domain, {:value => "", :active => false}
632
- security_buffer :workstation, {:value => "", :active => false}
633
- string :padding, {:size => 0, :value => "", :active => false }
634
- }
635
-
636
- class Type1
637
- class << Type1
638
- def parse(str)
639
- t = new
640
- t.parse(str)
641
- t
642
- end
643
- end
644
-
645
- def parse(str)
646
- super(str)
647
- enable(:domain) if has_flag?(:DOMAIN_SUPPLIED)
648
- enable(:workstation) if has_flag?(:WORKSTATION_SUPPLIED)
649
- super(str)
650
- if ( (len = data_edge - head_size) > 0)
651
- self.padding = "\0" * len
652
- super(str)
653
- end
654
- end
655
- end
656
-
657
- Type2 = Message.define{
658
- string :sign, {:size => 8, :value => SSP_SIGN}
659
- int32LE :type, {:value => 2}
660
- security_buffer :target_name, {:size => 0, :value => ""}
661
- int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE2]}
662
- int64LE :challenge, {:value => 0}
663
- int64LE :context, {:value => 0, :active => false}
664
- security_buffer :target_info, {:value => "", :active => false}
665
- string :padding, {:size => 0, :value => "", :active => false }
666
- }
667
-
668
- class Type2
669
- class << Type2
670
- def parse(str)
671
- t = new
672
- t.parse(str)
673
- t
674
- end
675
- end
676
-
677
- def parse(str)
678
- super(str)
679
- if has_flag?(:TARGET_INFO)
680
- enable(:context)
681
- enable(:target_info)
682
- super(str)
683
- end
684
- if ( (len = data_edge - head_size) > 0)
685
- self.padding = "\0" * len
686
- super(str)
687
- end
688
- end
689
-
690
- def response(arg, opt = {})
691
- usr = arg[:user]
692
- pwd = arg[:password]
693
- if usr.nil? or pwd.nil?
694
- raise ArgumentError, "user and password have to be supplied"
695
- end
696
-
697
- if opt[:workstation]
698
- ws = opt[:workstation]
699
- else
700
- ws = ""
701
- end
702
-
703
- if opt[:client_challenge]
704
- cc = opt[:client_challenge]
705
- else
706
- cc = rand(MAX64)
707
- end
708
- cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
709
- opt[:client_challenge] = cc
710
-
711
- if has_flag?(:OEM) and opt[:unicode]
712
- usr = NTLM::decode_utf16le(usr)
713
- pwd = NTLM::decode_utf16le(pwd)
714
- ws = NTLM::decode_utf16le(ws)
715
- opt[:unicode] = false
716
- end
717
-
718
- if has_flag?(:UNICODE) and !opt[:unicode]
719
- usr = NTLM::encode_utf16le(usr)
720
- pwd = NTLM::encode_utf16le(pwd)
721
- ws = NTLM::encode_utf16le(ws)
722
- opt[:unicode] = true
723
- end
724
-
725
- tgt = self.target_name
726
- ti = self.target_info
727
-
728
- chal = self[:challenge].serialize
729
-
730
- if opt[:ntlmv2]
731
- ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, tgt, opt), :challenge => chal, :target_info => ti}
732
- lm_res = NTLM::lmv2_response(ar, opt)
733
- ntlm_res = NTLM::ntlmv2_response(ar, opt)
734
- elsif has_flag?(:NTLM2_KEY)
735
- ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
736
- lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
737
- else
738
- lm_res = NTLM::lm_response(pwd, chal)
739
- ntlm_res = NTLM::ntlm_response(pwd, chal)
740
- end
741
-
742
- Type3.create({
743
- :lm_response => lm_res,
744
- :ntlm_response => ntlm_res,
745
- :domain => tgt,
746
- :user => usr,
747
- :workstation => ws,
748
- :flag => self.flag
749
- })
750
- end
751
- end
752
-
753
-
754
- Type3 = Message.define{
755
- string :sign, {:size => 8, :value => SSP_SIGN}
756
- int32LE :type, {:value => 3}
757
- security_buffer :lm_response, {:value => ""}
758
- security_buffer :ntlm_response, {:value => ""}
759
- security_buffer :domain, {:value => ""}
760
- security_buffer :user, {:value => ""}
761
- security_buffer :workstation, {:value => ""}
762
- security_buffer :session_key, {:value => "", :active => false }
763
- int64LE :flag, {:value => 0, :active => false }
764
- }
765
-
766
- class Type3
767
- class << Type3
768
- def parse(str)
769
- t = new
770
- t.parse(str)
771
- t
772
- end
773
-
774
- def create(arg, opt ={})
775
- t = new
776
- t.lm_response = arg[:lm_response]
777
- t.ntlm_response = arg[:ntlm_response]
778
- t.domain = arg[:domain]
779
- t.user = arg[:user]
780
- t.workstation = arg[:workstation]
781
-
782
- if arg[:session_key]
783
- t.enable(:session_key)
784
- t.session_key = arg[session_key]
785
- end
786
- if arg[:flag]
787
- t.enable(:session_key)
788
- t.enable(:flag)
789
- t.flag = arg[:flag]
790
- end
791
- t
792
- end
793
- end
794
- end
795
- end
796
- end
797
- end
3
+ #.
4
+ # Copyright 2014 by siberas, http://www.siberas.de
5
+ # This file is part of WATOBO (Web Application Tool Box) http://watobo.sourceforge.com
6
+ # WATOBO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License.
7
+ # WATOBO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
+ # You should have received a copy of the GNU General Public License along with WATOBO; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
9
+
10
+ # encoding: UTF-8
11
+ #
12
+ # = net/ntlm.rb
13
+ #
14
+ # An NTLM Authentication Library for Ruby
15
+ #
16
+ # This code is a derivative of "dbf2.rb" written by yrock
17
+ # and Minero Aoki. You can find original code here:
18
+ # http://jp.rubyist.net/magazine/?0013-CodeReview
19
+ # -------------------------------------------------------------
20
+ # Copyright (c) 2005,2006 yrock
21
+ #
22
+ # This program is free software.
23
+ # You can distribute/modify this program under the terms of the
24
+ # Ruby License.
25
+ #
26
+ # 2006-02-11 refactored by Minero Aoki
27
+ # -------------------------------------------------------------
28
+ #
29
+ # All protocol information used to write this code stems from
30
+ # "The NTLM Authentication Protocol" by Eric Glass. The author
31
+ # would thank to him for this tremendous work and making it
32
+ # available on the net.
33
+ # http://davenport.sourceforge.net/ntlm.html
34
+ # -------------------------------------------------------------
35
+ # Copyright (c) 2003 Eric Glass
36
+ #
37
+ # Permission to use, copy, modify, and distribute this document
38
+ # for any purpose and without any fee is hereby granted,
39
+ # provided that the above copyright notice and this list of
40
+ # conditions appear in all copies.
41
+ # -------------------------------------------------------------
42
+ #
43
+ # The author also looked Mozilla-Firefox-1.0.7 source code,
44
+ # namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
45
+ # Jonathan Bastien-Filiatrault's libntlm-ruby.
46
+ # "http://x2a.org/websvn/filedetails.php?
47
+ # repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
48
+ # The latter has a minor bug in its separate_keys function.
49
+ # The third key has to begin from the 14th character of the
50
+ # input string instead of 13th:)
51
+ #--
52
+ # $Id: ntlm.rb,v 1.1 2006/10/05 01:36:52 koheik Exp $
53
+ #++
54
+
55
+
56
+
57
+ module Watobo
58
+ module NTLM
59
+ # @private
60
+ module VERSION
61
+ MAJOR = 0
62
+ MINOR = 3
63
+ TINY = 4
64
+ STRING = [MAJOR, MINOR, TINY].join('.')
65
+ end
66
+
67
+ SSP_SIGN = "NTLMSSP\0"
68
+ BLOB_SIGN = 0x00000101
69
+ LM_MAGIC = "KGS!@\#$%"
70
+ TIME_OFFSET = 11644473600
71
+ MAX64 = 0xffffffffffffffff
72
+
73
+ FLAGS = {
74
+ :UNICODE => 0x00000001,
75
+ :OEM => 0x00000002,
76
+ :REQUEST_TARGET => 0x00000004,
77
+ :MBZ9 => 0x00000008,
78
+ :SIGN => 0x00000010,
79
+ :SEAL => 0x00000020,
80
+ :NEG_DATAGRAM => 0x00000040,
81
+ :NETWARE => 0x00000100,
82
+ :NTLM => 0x00000200,
83
+ :NEG_NT_ONLY => 0x00000400,
84
+ :MBZ7 => 0x00000800,
85
+ :DOMAIN_SUPPLIED => 0x00001000,
86
+ :WORKSTATION_SUPPLIED => 0x00002000,
87
+ :LOCAL_CALL => 0x00004000,
88
+ :ALWAYS_SIGN => 0x00008000,
89
+ :TARGET_TYPE_DOMAIN => 0x00010000,
90
+ :TARGET_INFO => 0x00800000,
91
+ :NTLM2_KEY => 0x00080000,
92
+ :KEY128 => 0x20000000,
93
+ :KEY56 => 0x80000000
94
+ }.freeze
95
+
96
+ FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] }
97
+
98
+ DEFAULT_FLAGS = {
99
+ :TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY],
100
+ :TYPE2 => FLAGS[:UNICODE],
101
+ :TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
102
+ }
103
+
104
+ class EncodeUtil
105
+ if RUBY_VERSION == "1.8.7"
106
+ require "kconv"
107
+
108
+ # Decode a UTF16 string to a ASCII string
109
+ # @param [String] str The string to convert
110
+ def self.decode_utf16le(str)
111
+ Kconv.kconv(swap16(str), Kconv::ASCII, Kconv::UTF16)
112
+ end
113
+
114
+ # Encodes a ASCII string to a UTF16 string
115
+ # @param [String] str The string to convert
116
+ def self.encode_utf16le(str)
117
+ swap16(Kconv.kconv(str, Kconv::UTF16, Kconv::ASCII))
118
+ end
119
+
120
+ # Taggle the strings endianness between big/little and little/big
121
+ # @param [String] str The string to swap the endianness on
122
+ def self.swap16(str)
123
+ str.unpack("v*").pack("n*")
124
+ end
125
+ else # Use native 1.9 string encoding functions
126
+
127
+ # Decode a UTF16 string to a ASCII string
128
+ # @param [String] str The string to convert
129
+ def self.decode_utf16le(str)
130
+ str.encode(Encoding::UTF_8, Encoding::UTF_16LE).force_encoding('UTF-8')
131
+ end
132
+
133
+ # Encodes a ASCII string to a UTF16 string
134
+ # @param [String] str The string to convert
135
+ # @note This implementation may seem stupid but the problem is that UTF16-LE and UTF-8 are incompatiable
136
+ # encodings. This library uses string contatination to build the packet bytes. The end result is that
137
+ # you can either marshal the encodings elsewhere of simply know that each time you call encode_utf16le
138
+ # the function will convert the string bytes to UTF-16LE and note the encoding as UTF-8 so that byte
139
+ # concatination works seamlessly.
140
+ def self.encode_utf16le(str)
141
+ str = str.force_encoding('UTF-8') if [::Encoding::ASCII_8BIT,::Encoding::US_ASCII].include?(str.encoding)
142
+ str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('UTF-8')
143
+ end
144
+ end
145
+ end
146
+
147
+ class << self
148
+
149
+ # Conver the value to a 64-Bit Little Endian Int
150
+ # @param [String] val The string to convert
151
+ def pack_int64le(val)
152
+ [val & 0x00000000ffffffff, val >> 32].pack("V2")
153
+ end
154
+
155
+ # Builds an array of strings that are 7 characters long
156
+ # @param [String] str The string to split
157
+ # @api private
158
+ def split7(str)
159
+ s = str.dup
160
+ until s.empty?
161
+ (ret ||= []).push s.slice!(0, 7)
162
+ end
163
+ ret
164
+ end
165
+
166
+ # Not sure what this is doing
167
+ # @param [String] str String to generate keys for
168
+ # @api private
169
+ def gen_keys(str)
170
+ split7(str).map{ |str7|
171
+ bits = split7(str7.unpack("B*")[0]).inject('')\
172
+ {|ret, tkn| ret += tkn + (tkn.gsub('1', '').size % 2).to_s }
173
+ [bits].pack("B*")
174
+ }
175
+ end
176
+
177
+ def apply_des(plain, keys)
178
+ dec = OpenSSL::Cipher::DES.new
179
+ keys.map {|k|
180
+ dec.key = k
181
+ dec.encrypt.update(plain)
182
+ }
183
+ end
184
+
185
+ # Generates a Lan Manager Hash
186
+ # @param [String] password The password to base the hash on
187
+ def lm_hash(password)
188
+ keys = gen_keys password.upcase.ljust(14, "\0")
189
+ apply_des(LM_MAGIC, keys).join
190
+ end
191
+
192
+ # Generate a NTLM Hash
193
+ # @param [String] password The password to base the hash on
194
+ # @option opt :unicode (false) Unicode encode the password
195
+ def ntlm_hash(password, opt = {})
196
+ pwd = password.dup
197
+ unless opt[:unicode]
198
+ pwd = EncodeUtil.encode_utf16le(pwd)
199
+ end
200
+ OpenSSL::Digest::MD4.digest pwd
201
+ end
202
+
203
+ # Generate a NTLMv2 Hash
204
+ # @param [String] user The username
205
+ # @param [String] password The password
206
+ # @param [String] target The domain or workstaiton to authenticate to
207
+ # @option opt :unicode (false) Unicode encode the domain
208
+ def ntlmv2_hash(user, password, target, opt={})
209
+ ntlmhash = ntlm_hash(password, opt)
210
+ userdomain = (user + target).upcase
211
+ unless opt[:unicode]
212
+ userdomain = EncodeUtil.encode_utf16le(userdomain)
213
+ end
214
+ OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
215
+ end
216
+
217
+ def lm_response(arg)
218
+ begin
219
+ hash = arg[:lm_hash]
220
+ chal = arg[:challenge]
221
+ rescue
222
+ raise ArgumentError
223
+ end
224
+ chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
225
+ keys = gen_keys hash.ljust(21, "\0")
226
+ apply_des(chal, keys).join
227
+ end
228
+
229
+ def ntlm_response(arg)
230
+ hash = arg[:ntlm_hash]
231
+ chal = arg[:challenge]
232
+ chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
233
+ keys = gen_keys hash.ljust(21, "\0")
234
+ apply_des(chal, keys).join
235
+ end
236
+
237
+ def ntlmv2_response(arg, opt = {})
238
+ begin
239
+ key = arg[:ntlmv2_hash]
240
+ chal = arg[:challenge]
241
+ ti = arg[:target_info]
242
+ rescue
243
+ raise ArgumentError
244
+ end
245
+ chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
246
+
247
+ if opt[:client_challenge]
248
+ cc = opt[:client_challenge]
249
+ else
250
+ cc = rand(MAX64)
251
+ end
252
+ cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
253
+
254
+ if opt[:timestamp]
255
+ ts = opt[:timestamp]
256
+ else
257
+ ts = Time.now.to_i
258
+ end
259
+ # epoch -> milsec from Jan 1, 1601
260
+ ts = 10000000 * (ts + TIME_OFFSET)
261
+
262
+ blob = Blob.new
263
+ blob.timestamp = ts
264
+ blob.challenge = cc
265
+ blob.target_info = ti
266
+
267
+ bb = blob.serialize
268
+ OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
269
+ end
270
+
271
+ def lmv2_response(arg, opt = {})
272
+ key = arg[:ntlmv2_hash]
273
+ chal = arg[:challenge]
274
+
275
+ chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
276
+
277
+ if opt[:client_challenge]
278
+ cc = opt[:client_challenge]
279
+ else
280
+ cc = rand(MAX64)
281
+ end
282
+ cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
283
+
284
+ OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
285
+ end
286
+
287
+ def ntlm2_session(arg, opt = {})
288
+ begin
289
+ passwd_hash = arg[:ntlm_hash]
290
+ chal = arg[:challenge]
291
+ rescue
292
+ raise ArgumentError
293
+ end
294
+
295
+ if opt[:client_challenge]
296
+ cc = opt[:client_challenge]
297
+ else
298
+ cc = rand(MAX64)
299
+ end
300
+ cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
301
+
302
+ keys = gen_keys passwd_hash.ljust(21, "\0")
303
+ session_hash = OpenSSL::Digest::MD5.digest(chal + cc).slice(0, 8)
304
+ response = apply_des(session_hash, keys).join
305
+ [cc.ljust(24, "\0"), response]
306
+ end
307
+ end
308
+
309
+
310
+ # base classes for primitives
311
+ # @private
312
+ class Field
313
+ attr_accessor :active, :value
314
+
315
+ def initialize(opts)
316
+ @value = opts[:value]
317
+ @active = opts[:active].nil? ? true : opts[:active]
318
+ end
319
+
320
+ def size
321
+ @active ? @size : 0
322
+ end
323
+ end
324
+
325
+ class String < Field
326
+ def initialize(opts)
327
+ super(opts)
328
+ @size = opts[:size]
329
+ end
330
+
331
+ def parse(str, offset=0)
332
+ if @active and str.size >= offset + @size
333
+ @value = str[offset, @size]
334
+ @size
335
+ else
336
+ 0
337
+ end
338
+ end
339
+
340
+ def serialize
341
+ if @active
342
+ @value
343
+ else
344
+ ""
345
+ end
346
+ end
347
+
348
+ def value=(val)
349
+ @value = val
350
+ @size = @value.nil? ? 0 : @value.size
351
+ @active = (@size > 0)
352
+ end
353
+ end
354
+
355
+ class Int16LE < Field
356
+ def initialize(opt)
357
+ super(opt)
358
+ @size = 2
359
+ end
360
+ def parse(str, offset=0)
361
+ if @active and str.size >= offset + @size
362
+ @value = str[offset, @size].unpack("v")[0]
363
+ @size
364
+ else
365
+ 0
366
+ end
367
+ end
368
+
369
+ def serialize
370
+ [@value].pack("v")
371
+ end
372
+ end
373
+
374
+ class Int32LE < Field
375
+ def initialize(opt)
376
+ super(opt)
377
+ @size = 4
378
+ end
379
+
380
+ def parse(str, offset=0)
381
+ if @active and str.size >= offset + @size
382
+ @value = str.slice(offset, @size).unpack("V")[0]
383
+ @size
384
+ else
385
+ 0
386
+ end
387
+ end
388
+
389
+ def serialize
390
+ [@value].pack("V") if @active
391
+ end
392
+ end
393
+
394
+ class Int64LE < Field
395
+ def initialize(opt)
396
+ super(opt)
397
+ @size = 8
398
+ end
399
+
400
+ def parse(str, offset=0)
401
+ if @active and str.size >= offset + @size
402
+ d, u = str.slice(offset, @size).unpack("V2")
403
+ @value = (u * 0x100000000 + d)
404
+ @size
405
+ else
406
+ 0
407
+ end
408
+ end
409
+
410
+ def serialize
411
+ [@value & 0x00000000ffffffff, @value >> 32].pack("V2") if @active
412
+ end
413
+ end
414
+
415
+ # base class of data structure
416
+ class FieldSet
417
+ class << FieldSet
418
+
419
+
420
+ # @macro string_security_buffer
421
+ # @method $1
422
+ # @method $1=
423
+ # @return [String]
424
+ def string(name, opts)
425
+ add_field(name, String, opts)
426
+ end
427
+
428
+ # @macro int16le_security_buffer
429
+ # @method $1
430
+ # @method $1=
431
+ # @return [Int16LE]
432
+ def int16LE(name, opts)
433
+ add_field(name, Int16LE, opts)
434
+ end
435
+
436
+ # @macro int32le_security_buffer
437
+ # @method $1
438
+ # @method $1=
439
+ # @return [Int32LE]
440
+ def int32LE(name, opts)
441
+ add_field(name, Int32LE, opts)
442
+ end
443
+
444
+ # @macro int64le_security_buffer
445
+ # @method $1
446
+ # @method $1=
447
+ # @return [Int64]
448
+ def int64LE(name, opts)
449
+ add_field(name, Int64LE, opts)
450
+ end
451
+
452
+ # @macro security_buffer
453
+ # @method $1
454
+ # @method $1=
455
+ # @return [SecurityBuffer]
456
+ def security_buffer(name, opts)
457
+ add_field(name, SecurityBuffer, opts)
458
+ end
459
+
460
+ def prototypes
461
+ @proto
462
+ end
463
+
464
+ def names
465
+ @proto.map{|n, t, o| n}
466
+ end
467
+
468
+ def types
469
+ @proto.map{|n, t, o| t}
470
+ end
471
+
472
+ def opts
473
+ @proto.map{|n, t, o| o}
474
+ end
475
+
476
+ private
477
+
478
+ def add_field(name, type, opts)
479
+ (@proto ||= []).push [name, type, opts]
480
+ define_accessor name
481
+ end
482
+
483
+ def define_accessor(name)
484
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
485
+ def #{name}
486
+ self['#{name}'].value
487
+ end
488
+
489
+ def #{name}=(val)
490
+ self['#{name}'].value = val
491
+ end
492
+ End
493
+ end
494
+ end
495
+
496
+ def initialize
497
+ @alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
498
+ end
499
+
500
+ def serialize
501
+ @alist.map{|n, f| f.serialize }.join
502
+ end
503
+
504
+ def parse(str, offset=0)
505
+ @alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
506
+ end
507
+
508
+ def size
509
+ @alist.inject(0){|sum, a| sum += a[1].size}
510
+ end
511
+
512
+ def [](name)
513
+ a = @alist.assoc(name.to_s.intern)
514
+ raise ArgumentError, "no such field: #{name}" unless a
515
+ a[1]
516
+ end
517
+
518
+ def []=(name, val)
519
+ a = @alist.assoc(name.to_s.intern)
520
+ raise ArgumentError, "no such field: #{name}" unless a
521
+ a[1] = val
522
+ end
523
+
524
+ def enable(name)
525
+ self[name].active = true
526
+ end
527
+
528
+ def disable(name)
529
+ self[name].active = false
530
+ end
531
+ end
532
+
533
+ class Blob < FieldSet
534
+ int32LE :blob_signature, {:value => BLOB_SIGN}
535
+ int32LE :reserved, {:value => 0}
536
+ int64LE :timestamp, {:value => 0}
537
+ string :challenge, {:value => "", :size => 8}
538
+ int32LE :unknown1, {:value => 0}
539
+ string :target_info, {:value => "", :size => 0}
540
+ int32LE :unknown2, {:value => 0}
541
+ end
542
+
543
+ class SecurityBuffer < FieldSet
544
+
545
+ int16LE :length, {:value => 0}
546
+ int16LE :allocated, {:value => 0}
547
+ int32LE :offset, {:value => 0}
548
+
549
+ attr_accessor :active
550
+ def initialize(opts)
551
+ super()
552
+ @value = opts[:value]
553
+ @active = opts[:active].nil? ? true : opts[:active]
554
+ @size = 8
555
+ end
556
+
557
+ def parse(str, offset=0)
558
+ if @active and str.size >= offset + @size
559
+ super(str, offset)
560
+ @value = str[self.offset, self.length]
561
+ @size
562
+ else
563
+ 0
564
+ end
565
+ end
566
+
567
+ def serialize
568
+ super if @active
569
+ end
570
+
571
+ def value
572
+ @value
573
+ end
574
+
575
+ def value=(val)
576
+ @value = val
577
+ self.length = self.allocated = val.size
578
+ end
579
+
580
+ def data_size
581
+ @active ? @value.size : 0
582
+ end
583
+ end
584
+
585
+ # @private false
586
+ class Message < FieldSet
587
+ class << Message
588
+ def parse(str)
589
+ m = Type0.new
590
+ m.parse(str)
591
+ case m.type
592
+ when 1
593
+ t = Type1.parse(str)
594
+ when 2
595
+ t = Type2.parse(str)
596
+ when 3
597
+ t = Type3.parse(str)
598
+ else
599
+ raise ArgumentError, "unknown type: #{m.type}"
600
+ end
601
+ t
602
+ end
603
+
604
+ def decode64(str)
605
+ parse(Base64.decode64(str))
606
+ end
607
+ end
608
+
609
+ def has_flag?(flag)
610
+ (self[:flag].value & FLAGS[flag]) == FLAGS[flag]
611
+ end
612
+
613
+ def set_flag(flag)
614
+ self[:flag].value |= FLAGS[flag]
615
+ end
616
+
617
+ def dump_flags
618
+ FLAG_KEYS.each{ |k| print(k, "=", flag?(k), "\n") }
619
+ end
620
+
621
+ def serialize
622
+ deflag
623
+ super + security_buffers.map{|n, f| f.value}.join
624
+ end
625
+
626
+ def encode64
627
+ Base64.encode64(serialize).gsub(/\n/, '')
628
+ end
629
+
630
+ def decode64(str)
631
+ parse(Base64.decode64(str))
632
+ end
633
+
634
+ alias head_size size
635
+
636
+ def data_size
637
+ security_buffers.inject(0){|sum, a| sum += a[1].data_size}
638
+ end
639
+
640
+ def size
641
+ head_size + data_size
642
+ end
643
+
644
+
645
+ def security_buffers
646
+ @alist.find_all{|n, f| f.instance_of?(SecurityBuffer)}
647
+ end
648
+
649
+ def deflag
650
+ security_buffers.inject(head_size){|cur, a|
651
+ a[1].offset = cur
652
+ cur += a[1].data_size
653
+ }
654
+ end
655
+
656
+ def data_edge
657
+ security_buffers.map{ |n, f| f.active ? f.offset : size}.min
658
+ end
659
+
660
+ # sub class definitions
661
+ class Type0 < Message
662
+ string :sign, {:size => 8, :value => SSP_SIGN}
663
+ int32LE :type, {:value => 0}
664
+ end
665
+
666
+ # @private false
667
+ class Type1 < Message
668
+
669
+ string :sign, {:size => 8, :value => SSP_SIGN}
670
+ int32LE :type, {:value => 1}
671
+ int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE1] }
672
+ security_buffer :domain, {:value => ""}
673
+ security_buffer :workstation, {:value => Socket.gethostname }
674
+ string :padding, {:size => 0, :value => "", :active => false }
675
+
676
+ class << Type1
677
+ # Parses a Type 1 Message
678
+ # @param [String] str A string containing Type 1 data
679
+ # @return [Type1] The parsed Type 1 message
680
+ def parse(str)
681
+ t = new
682
+ t.parse(str)
683
+ t
684
+ end
685
+ end
686
+
687
+ # @!visibility private
688
+ def parse(str)
689
+ super(str)
690
+ enable(:domain) if has_flag?(:DOMAIN_SUPPLIED)
691
+ enable(:workstation) if has_flag?(:WORKSTATION_SUPPLIED)
692
+ super(str)
693
+ if ( (len = data_edge - head_size) > 0)
694
+ self.padding = "\0" * len
695
+ super(str)
696
+ end
697
+ end
698
+ end
699
+
700
+
701
+ # @private false
702
+ class Type2 < Message
703
+
704
+ string :sign, {:size => 8, :value => SSP_SIGN}
705
+ int32LE :type, {:value => 2}
706
+ security_buffer :target_name, {:size => 0, :value => ""}
707
+ int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE2]}
708
+ int64LE :challenge, {:value => 0}
709
+ int64LE :context, {:value => 0, :active => false}
710
+ security_buffer :target_info, {:value => "", :active => false}
711
+ string :padding, {:size => 0, :value => "", :active => false }
712
+
713
+ class << Type2
714
+ # Parse a Type 2 packet
715
+ # @param [String] str A string containing Type 2 data
716
+ # @return [Type2]
717
+ def parse(str)
718
+ t = new
719
+ t.parse(str)
720
+ t
721
+ end
722
+ end
723
+
724
+ # @!visibility private
725
+ def parse(str)
726
+ super(str)
727
+ if has_flag?(:TARGET_INFO)
728
+ enable(:context)
729
+ enable(:target_info)
730
+ super(str)
731
+ end
732
+ if ( (len = data_edge - head_size) > 0)
733
+ self.padding = "\0" * len
734
+ super(str)
735
+ end
736
+ end
737
+
738
+ # Generates a Type 3 response based on the Type 2 Information
739
+ # @return [Type3]
740
+ # @option arg [String] :username The username to authenticate with
741
+ # @option arg [String] :password The user's password
742
+ # @option arg [String] :domain ('') The domain to authenticate to
743
+ # @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
744
+ # @option opt [Boolean] :use_default_target (False) Use the domain supplied by the server in the Type 2 packet
745
+ # @note An empty :domain option authenticates to the local machine.
746
+ # @note The :use_default_target has presidence over the :domain option
747
+ def response(arg, opt = {})
748
+ usr = arg[:user]
749
+ pwd = arg[:password]
750
+ domain = arg[:domain] ? arg[:domain] : ""
751
+ if usr.nil? or pwd.nil?
752
+ raise ArgumentError, "user and password have to be supplied"
753
+ end
754
+
755
+ if opt[:workstation]
756
+ ws = opt[:workstation]
757
+ else
758
+ ws = Socket.gethostname
759
+ end
760
+
761
+ if opt[:client_challenge]
762
+ cc = opt[:client_challenge]
763
+ else
764
+ cc = rand(MAX64)
765
+ end
766
+ cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
767
+ opt[:client_challenge] = cc
768
+
769
+ if has_flag?(:OEM) and opt[:unicode]
770
+ usr = NTLM::EncodeUtil.decode_utf16le(usr)
771
+ pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
772
+ ws = NTLM::EncodeUtil.decode_utf16le(ws)
773
+ domain = NTLM::EncodeUtil.decode_utf16le(domain)
774
+ opt[:unicode] = false
775
+ end
776
+
777
+ if has_flag?(:UNICODE) and !opt[:unicode]
778
+ usr = NTLM::EncodeUtil.encode_utf16le(usr)
779
+ pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
780
+ ws = NTLM::EncodeUtil.encode_utf16le(ws)
781
+ domain = NTLM::EncodeUtil.encode_utf16le(domain)
782
+ opt[:unicode] = true
783
+ end
784
+
785
+ if opt[:use_default_target]
786
+ domain = self.target_name
787
+ end
788
+
789
+ ti = self.target_info
790
+
791
+ chal = self[:challenge].serialize
792
+
793
+ if opt[:ntlmv2]
794
+ ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
795
+ lm_res = NTLM::lmv2_response(ar, opt)
796
+ ntlm_res = NTLM::ntlmv2_response(ar, opt)
797
+ elsif has_flag?(:NTLM2_KEY)
798
+ ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
799
+ lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
800
+ else
801
+ lm_res = NTLM::lm_response(pwd, chal)
802
+ ntlm_res = NTLM::ntlm_response(pwd, chal)
803
+ end
804
+
805
+ Type3.create({
806
+ :lm_response => lm_res,
807
+ :ntlm_response => ntlm_res,
808
+ :domain => domain,
809
+ :user => usr,
810
+ :workstation => ws,
811
+ :flag => self.flag
812
+ })
813
+ end
814
+ end
815
+
816
+ # @private false
817
+ class Type3 < Message
818
+
819
+ string :sign, {:size => 8, :value => SSP_SIGN}
820
+ int32LE :type, {:value => 3}
821
+ security_buffer :lm_response, {:value => ""}
822
+ security_buffer :ntlm_response, {:value => ""}
823
+ security_buffer :domain, {:value => ""}
824
+ security_buffer :user, {:value => ""}
825
+ security_buffer :workstation, {:value => ""}
826
+ security_buffer :session_key, {:value => "", :active => false }
827
+ int64LE :flag, {:value => 0, :active => false }
828
+
829
+ class << Type3
830
+ # Parse a Type 3 packet
831
+ # @param [String] str A string containing Type 3 data
832
+ # @return [Type2]
833
+ def parse(str)
834
+ t = new
835
+ t.parse(str)
836
+ t
837
+ end
838
+
839
+ # Builds a Type 3 packet
840
+ # @note All options must be properly encoded with either unicode or oem encoding
841
+ # @return [Type3]
842
+ # @option arg [String] :lm_response The LM hash
843
+ # @option arg [String] :ntlm_response The NTLM hash
844
+ # @option arg [String] :domain The domain to authenticate to
845
+ # @option arg [String] :workstation The name of the calling workstation
846
+ # @option arg [String] :session_key The session key
847
+ # @option arg [Integer] :flag Flags for the packet
848
+ def create(arg, opt ={})
849
+ t = new
850
+ t.lm_response = arg[:lm_response]
851
+ t.ntlm_response = arg[:ntlm_response]
852
+ t.domain = arg[:domain]
853
+ t.user = arg[:user]
854
+
855
+ if arg[:workstation]
856
+ t.workstation = arg[:workstation]
857
+ end
858
+
859
+ if arg[:session_key]
860
+ t.enable(:session_key)
861
+ t.session_key = arg[session_key]
862
+ end
863
+
864
+ if arg[:flag]
865
+ t.enable(:session_key)
866
+ t.enable(:flag)
867
+ t.flag = arg[:flag]
868
+ end
869
+ t
870
+ end
871
+ end
872
+ end
873
+ end
874
+ end
875
+ end