ruby_smb 2.0.9 → 2.0.13

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 (228) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/verify.yml +5 -15
  4. data/examples/auth_capture.rb +71 -0
  5. data/examples/dump_secrets_from_sid.rb +207 -0
  6. data/examples/enum_domain_users.rb +75 -0
  7. data/examples/get_computer_info.rb +42 -0
  8. data/examples/query_service_status.rb +42 -4
  9. data/lib/ruby_smb/client/negotiation.rb +1 -1
  10. data/lib/ruby_smb/client.rb +10 -20
  11. data/lib/ruby_smb/dcerpc/bind.rb +28 -20
  12. data/lib/ruby_smb/dcerpc/bind_ack.rb +29 -28
  13. data/lib/ruby_smb/dcerpc/client.rb +542 -0
  14. data/lib/ruby_smb/dcerpc/drsr/drs_bind_request.rb +24 -0
  15. data/lib/ruby_smb/dcerpc/drsr/drs_bind_response.rb +26 -0
  16. data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_request.rb +57 -0
  17. data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_response.rb +76 -0
  18. data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_request.rb +46 -0
  19. data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_response.rb +168 -0
  20. data/lib/ruby_smb/dcerpc/drsr/drs_extensions.rb +56 -0
  21. data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_request.rb +121 -0
  22. data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_response.rb +118 -0
  23. data/lib/ruby_smb/dcerpc/drsr/drs_unbind_request.rb +24 -0
  24. data/lib/ruby_smb/dcerpc/drsr/drs_unbind_response.rb +26 -0
  25. data/lib/ruby_smb/dcerpc/drsr.rb +909 -0
  26. data/lib/ruby_smb/dcerpc/epm/epm_ept_map_request.rb +26 -0
  27. data/lib/ruby_smb/dcerpc/epm/epm_ept_map_response.rb +25 -0
  28. data/lib/ruby_smb/dcerpc/epm/epm_twrt.rb +211 -0
  29. data/lib/ruby_smb/dcerpc/epm.rb +75 -0
  30. data/lib/ruby_smb/dcerpc/error.rb +17 -0
  31. data/lib/ruby_smb/dcerpc/ndr.rb +1159 -297
  32. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +3 -13
  33. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +3 -3
  34. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +3 -13
  35. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +1 -1
  36. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +3 -11
  37. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +1 -1
  38. data/lib/ruby_smb/dcerpc/netlogon.rb +5 -4
  39. data/lib/ruby_smb/dcerpc/p_syntax_id_t.rb +4 -3
  40. data/lib/ruby_smb/dcerpc/pdu_header.rb +7 -7
  41. data/lib/ruby_smb/dcerpc/ptypes.rb +1 -0
  42. data/lib/ruby_smb/dcerpc/request.rb +79 -32
  43. data/lib/ruby_smb/dcerpc/response.rb +45 -10
  44. data/lib/ruby_smb/dcerpc/rpc_auth3.rb +28 -0
  45. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +11 -11
  46. data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +118 -0
  47. data/lib/ruby_smb/dcerpc/samr/rpc_sid.rb +150 -0
  48. data/lib/ruby_smb/dcerpc/samr/samr_close_handle_request.rb +23 -0
  49. data/lib/ruby_smb/dcerpc/samr/samr_close_handle_response.rb +24 -0
  50. data/lib/ruby_smb/dcerpc/samr/samr_connect_request.rb +32 -0
  51. data/lib/ruby_smb/dcerpc/samr/samr_connect_response.rb +23 -0
  52. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request.rb +26 -0
  53. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response.rb +55 -0
  54. data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_request.rb +48 -0
  55. data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_response.rb +38 -0
  56. data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_request.rb +23 -0
  57. data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_response.rb +48 -0
  58. data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request.rb +24 -0
  59. data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response.rb +25 -0
  60. data/lib/ruby_smb/dcerpc/samr/samr_open_domain_request.rb +27 -0
  61. data/lib/ruby_smb/dcerpc/samr/samr_open_domain_response.rb +24 -0
  62. data/lib/ruby_smb/dcerpc/samr/samr_open_user_request.rb +26 -0
  63. data/lib/ruby_smb/dcerpc/samr/samr_open_user_response.rb +24 -0
  64. data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request.rb +23 -0
  65. data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response.rb +23 -0
  66. data/lib/ruby_smb/dcerpc/samr.rb +613 -0
  67. data/lib/ruby_smb/dcerpc/sec_trailer.rb +26 -0
  68. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +56 -79
  69. data/lib/ruby_smb/dcerpc/srvsvc.rb +27 -4
  70. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +13 -25
  71. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +2 -2
  72. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +1 -1
  73. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +1 -1
  74. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +1 -1
  75. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +4 -14
  76. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +1 -1
  77. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +3 -11
  78. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +1 -1
  79. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +1 -1
  80. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +12 -11
  81. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +1 -1
  82. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +9 -8
  83. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +3 -3
  84. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +1 -1
  85. data/lib/ruby_smb/dcerpc/svcctl.rb +1 -3
  86. data/lib/ruby_smb/dcerpc/uuid.rb +3 -0
  87. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +2 -2
  88. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +2 -13
  89. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +3 -3
  90. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +3 -20
  91. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +3 -20
  92. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +5 -14
  93. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +5 -14
  94. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +1 -9
  95. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +4 -3
  96. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +5 -6
  97. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +2 -2
  98. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +9 -18
  99. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +4 -14
  100. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +7 -15
  101. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +3 -1
  102. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +0 -9
  103. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +1 -1
  104. data/lib/ruby_smb/dcerpc/winreg.rb +10 -14
  105. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb +26 -0
  106. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response.rb +88 -0
  107. data/lib/ruby_smb/dcerpc/wkssvc.rb +65 -0
  108. data/lib/ruby_smb/dcerpc.rb +41 -11
  109. data/lib/ruby_smb/dialect.rb +45 -0
  110. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  111. data/lib/ruby_smb/field/file_time.rb +1 -1
  112. data/lib/ruby_smb/field/string16.rb +5 -1
  113. data/lib/ruby_smb/gss/provider/authenticator.rb +42 -0
  114. data/lib/ruby_smb/gss/provider/ntlm.rb +303 -0
  115. data/lib/ruby_smb/gss/provider.rb +35 -0
  116. data/lib/ruby_smb/gss.rb +56 -63
  117. data/lib/ruby_smb/ntlm.rb +61 -0
  118. data/lib/ruby_smb/server/server_client/negotiation.rb +156 -0
  119. data/lib/ruby_smb/server/server_client/session_setup.rb +82 -0
  120. data/lib/ruby_smb/server/server_client.rb +162 -0
  121. data/lib/ruby_smb/server.rb +54 -0
  122. data/lib/ruby_smb/signing.rb +59 -0
  123. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +11 -11
  124. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +1 -1
  125. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  126. data/lib/ruby_smb/smb1/pipe.rb +4 -0
  127. data/lib/ruby_smb/smb1/tree.rb +1 -1
  128. data/lib/ruby_smb/smb2/negotiate_context.rb +18 -2
  129. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +9 -0
  130. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +0 -1
  131. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -2
  132. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +1 -1
  133. data/lib/ruby_smb/smb2/pipe.rb +4 -0
  134. data/lib/ruby_smb/smb2/tree.rb +1 -1
  135. data/lib/ruby_smb/smb2.rb +3 -1
  136. data/lib/ruby_smb/version.rb +1 -1
  137. data/lib/ruby_smb.rb +2 -1
  138. data/spec/lib/ruby_smb/client_spec.rb +8 -11
  139. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +69 -41
  140. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +75 -21
  141. data/spec/lib/ruby_smb/dcerpc/client_spec.rb +714 -0
  142. data/spec/lib/ruby_smb/dcerpc/drsr_spec.rb +2169 -0
  143. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +3792 -1373
  144. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +4 -4
  145. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +4 -4
  146. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +2 -2
  147. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +2 -2
  148. data/spec/lib/ruby_smb/dcerpc/p_syntax_id_t_spec.rb +18 -4
  149. data/spec/lib/ruby_smb/dcerpc/pdu_header_spec.rb +27 -1
  150. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +76 -11
  151. data/spec/lib/ruby_smb/dcerpc/response_spec.rb +99 -9
  152. data/spec/lib/ruby_smb/dcerpc/rpc_auth3_spec.rb +75 -0
  153. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +29 -28
  154. data/spec/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string_spec.rb +340 -0
  155. data/spec/lib/ruby_smb/dcerpc/samr/rpc_sid_spec.rb +116 -0
  156. data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_request_spec.rb +40 -0
  157. data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_response_spec.rb +48 -0
  158. data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_request_spec.rb +56 -0
  159. data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_response_spec.rb +47 -0
  160. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request_spec.rb +63 -0
  161. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response_spec.rb +265 -0
  162. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request_spec.rb +52 -0
  163. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response_spec.rb +36 -0
  164. data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_request_spec.rb +56 -0
  165. data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_response_spec.rb +48 -0
  166. data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request_spec.rb +48 -0
  167. data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response_spec.rb +42 -0
  168. data/spec/lib/ruby_smb/dcerpc/samr_spec.rb +420 -0
  169. data/spec/lib/ruby_smb/dcerpc/sec_trailer_spec.rb +92 -0
  170. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +149 -110
  171. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +21 -17
  172. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +56 -79
  173. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +4 -4
  174. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +2 -2
  175. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +2 -2
  176. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +2 -2
  177. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +19 -29
  178. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +2 -2
  179. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +9 -15
  180. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +2 -2
  181. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +2 -2
  182. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +22 -22
  183. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +2 -2
  184. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +18 -14
  185. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +5 -4
  186. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +2 -2
  187. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +1 -5
  188. data/spec/lib/ruby_smb/dcerpc/uuid_spec.rb +15 -23
  189. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +2 -2
  190. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +4 -41
  191. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +4 -4
  192. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +4 -52
  193. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +4 -56
  194. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +10 -34
  195. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +10 -34
  196. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +2 -26
  197. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +2 -2
  198. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +17 -25
  199. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +2 -2
  200. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +20 -44
  201. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +8 -32
  202. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +10 -22
  203. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +4 -0
  204. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +0 -12
  205. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +2 -2
  206. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +18 -47
  207. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb +43 -0
  208. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb +410 -0
  209. data/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +70 -0
  210. data/spec/lib/ruby_smb/field/string16_spec.rb +22 -0
  211. data/spec/lib/ruby_smb/gss/provider/ntlm/account_spec.rb +32 -0
  212. data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +101 -0
  213. data/spec/lib/ruby_smb/gss/provider/ntlm/os_version_spec.rb +32 -0
  214. data/spec/lib/ruby_smb/gss/provider/ntlm_spec.rb +113 -0
  215. data/spec/lib/ruby_smb/server/server_client_spec.rb +156 -0
  216. data/spec/lib/ruby_smb/server_spec.rb +32 -0
  217. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +18 -37
  218. data/spec/lib/ruby_smb/smb1/tree_spec.rb +4 -4
  219. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +2 -2
  220. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +18 -16
  221. data/spec/lib/ruby_smb/smb2/tree_spec.rb +5 -5
  222. data/spec/support/bin_helper.rb +9 -0
  223. data.tar.gz.sig +2 -1
  224. metadata +119 -6
  225. metadata.gz.sig +0 -0
  226. data/lib/ruby_smb/client/signing.rb +0 -64
  227. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +0 -38
  228. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +0 -135
@@ -0,0 +1,45 @@
1
+ module RubySMB
2
+ # Definitions that define metadata around a particular SMB dialect. This is useful for grouping dialects into a
3
+ # hierarchy as well as printing them as human readable strings with varying degrees of specificity.
4
+ module Dialect
5
+ # the order (taxonomic ranking) of the family, 2 and 3 are intentionally combined
6
+ ORDER_SMB1 = 'SMB1'.freeze
7
+ ORDER_SMB2 = 'SMB2'.freeze
8
+
9
+ # the family of the dialect
10
+ FAMILY_SMB1 = 'SMB 1'.freeze
11
+ FAMILY_SMB2 = 'SMB 2.x'.freeze
12
+ FAMILY_SMB3 = 'SMB 3.x'.freeze
13
+
14
+ # the major version of the dialect
15
+ VERSION_SMB1 = 'SMB v1'.freeze
16
+ VERSION_SMB2 = 'SMB v2'.freeze
17
+ VERSION_SMB3 = 'SMB v3'.freeze
18
+
19
+ # the names are meant to be human readable and may change in the future, use the #dialect, #order and #family
20
+ # attributes for any programmatic comparisons
21
+ Definition = Struct.new(:dialect, :order, :family, :version_name, :full_name) do
22
+ alias_method :short_name, :version_name
23
+ end
24
+
25
+ ALL = [
26
+ Definition.new('NT LM 0.12', ORDER_SMB1, FAMILY_SMB1, VERSION_SMB1, 'SMB v1 (NT LM 0.12)'.freeze),
27
+ Definition.new('0x0202', ORDER_SMB2, FAMILY_SMB2, VERSION_SMB2, 'SMB v2.0.2'.freeze),
28
+ Definition.new('0x0210', ORDER_SMB2, FAMILY_SMB2, VERSION_SMB2, 'SMB v2.1'.freeze),
29
+ Definition.new('0x02ff', ORDER_SMB2, FAMILY_SMB2, VERSION_SMB2, 'SMB 2.???'.freeze), # wildcard revision
30
+ Definition.new('0x0300', ORDER_SMB2, FAMILY_SMB3, VERSION_SMB3, 'SMB v3.0'.freeze),
31
+ Definition.new('0x0302', ORDER_SMB2, FAMILY_SMB3, VERSION_SMB3, 'SMB v3.0.2'.freeze),
32
+ Definition.new('0x0311', ORDER_SMB2, FAMILY_SMB3, VERSION_SMB3, 'SMB v3.1.1'.freeze)
33
+ ].map { |definition| [definition.dialect, definition] }.to_h
34
+
35
+ #
36
+ # Retrieve a dialect definition. The definition contains metadata describing the particular dialect.
37
+ #
38
+ # @param [Integer, String] dialect the dialect to retrieve the definition for
39
+ # @return [Definition, nil] the definition if it was found
40
+ def self.[](dialect)
41
+ dialect = '0x%04x' % dialect if dialect.is_a? Integer
42
+ ALL[dialect]
43
+ end
44
+ end
45
+ end
@@ -9,7 +9,7 @@ module RubySMB
9
9
  def nbss(packet)
10
10
  nbss = RubySMB::Nbss::SessionHeader.new
11
11
  nbss.session_packet_type = RubySMB::Nbss::SESSION_MESSAGE
12
- nbss.stream_protocol_length = packet.do_num_bytes
12
+ nbss.stream_protocol_length = packet.do_num_bytes.to_i
13
13
  nbss.to_binary_s
14
14
  end
15
15
 
@@ -3,7 +3,7 @@ require 'date'
3
3
  module RubySMB
4
4
  module Field
5
5
  # Represents a Windows FILETIME structure as defined in
6
- # [FILETIME structure](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx)
6
+ # [2.3.3 FILETIME](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/2c57429b-fdd4-488f-b5fc-9e4cf020fcdf).aspx)
7
7
  class FileTime < BinData::Primitive
8
8
  # Difference between the Windows and Unix epochs, in 100ns intervals
9
9
  EPOCH_DIFF_100NS = 116_444_736_000_000_000
@@ -3,12 +3,16 @@ module RubySMB
3
3
  # Represents a String in UTF-16LE
4
4
  class String16 < BinData::String
5
5
  def assign(val)
6
- super(val.encode('utf-16le'))
6
+ super(val.to_s.encode('utf-16le')).force_encoding('utf-16le')
7
7
  end
8
8
 
9
9
  def snapshot
10
10
  super.force_encoding('utf-16le')
11
11
  end
12
+
13
+ def read_and_return_value(io)
14
+ super.force_encoding('utf-16le')
15
+ end
12
16
  end
13
17
  end
14
18
  end
@@ -0,0 +1,42 @@
1
+ module RubySMB
2
+ module Gss
3
+ module Provider
4
+ module Authenticator
5
+ #
6
+ # The base class for a GSS provider's unique authenticator. This provides a common interface and is not usable
7
+ # on it's own. The provider-specific authentication logic is defined within this authenticator class which
8
+ # actually runs the authentication routine.
9
+ #
10
+ class Base
11
+ # @param [Provider::Base] provider the GSS provider that this instance is an authenticator for
12
+ # @param server_client the client instance that this will be an authenticator for
13
+ def initialize(provider, server_client)
14
+ @provider = provider
15
+ @server_client = server_client
16
+ @session_key = nil
17
+ reset!
18
+ end
19
+
20
+ #
21
+ # Process a GSS authentication buffer. If no buffer is specified, the request is assumed to be the first in
22
+ # the negotiation sequence.
23
+ #
24
+ # @param [String, nil] buffer the request GSS request buffer that should be processed
25
+ # @return [Gss::Provider::Result] the result of the processed GSS request
26
+ def process(request_buffer=nil)
27
+ raise NotImplementedError
28
+ end
29
+
30
+ #
31
+ # Reset the authenticator's state, wiping anything related to a partial or complete authentication process.
32
+ #
33
+ def reset!
34
+ @session_key = nil
35
+ end
36
+
37
+ attr_accessor :session_key
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,303 @@
1
+ require 'ruby_smb/ntlm'
2
+
3
+ module RubySMB
4
+ module Gss
5
+ module Provider
6
+ #
7
+ # A GSS provider that authenticates clients via the NT LAN Manager (NTLM) Security Support Provider (NTLMSSP)
8
+ # protocol.
9
+ #
10
+ class NTLM < Base
11
+ include RubySMB::NTLM
12
+
13
+ # An account representing an identity for which this provider will accept authentication attempts.
14
+ Account = Struct.new(:username, :password, :domain) do
15
+ def to_s
16
+ "#{domain}\\#{username}"
17
+ end
18
+ end
19
+
20
+ class Authenticator < Authenticator::Base
21
+ def reset!
22
+ super
23
+ @server_challenge = nil
24
+ end
25
+
26
+ def process(request_buffer=nil)
27
+ if request_buffer.nil?
28
+ # this is only NTLMSSP (as opposed to SPNEGO + NTLMSSP)
29
+ buffer = OpenSSL::ASN1::ASN1Data.new([
30
+ Gss::OID_SPNEGO,
31
+ OpenSSL::ASN1::ASN1Data.new([
32
+ OpenSSL::ASN1::Sequence.new([
33
+ OpenSSL::ASN1::ASN1Data.new([
34
+ OpenSSL::ASN1::Sequence.new([
35
+ Gss::OID_NTLMSSP
36
+ ])
37
+ ], 0, :CONTEXT_SPECIFIC),
38
+ OpenSSL::ASN1::ASN1Data.new([
39
+ OpenSSL::ASN1::ASN1Data.new([
40
+ OpenSSL::ASN1::ASN1Data.new([
41
+ OpenSSL::ASN1::GeneralString.new('not_defined_in_RFC4178@please_ignore')
42
+ ], 0, :CONTEXT_SPECIFIC)
43
+ ], 16, :UNIVERSAL)
44
+ ], 3, :CONTEXT_SPECIFIC)
45
+ ])
46
+ ], 0, :CONTEXT_SPECIFIC)
47
+ ], 0, :APPLICATION).to_der
48
+ return Result.new(buffer, WindowsError::NTStatus::STATUS_SUCCESS)
49
+ end
50
+
51
+ begin
52
+ gss_api = OpenSSL::ASN1.decode(request_buffer)
53
+ rescue OpenSSL::ASN1::ASN1Error
54
+ return
55
+ end
56
+
57
+ if gss_api&.tag == 0 && gss_api&.tag_class == :APPLICATION
58
+ result = process_gss_type1(gss_api)
59
+ elsif gss_api&.tag == 1 && gss_api&.tag_class == :CONTEXT_SPECIFIC
60
+ result = process_gss_type3(gss_api)
61
+ end
62
+
63
+ result
64
+ end
65
+
66
+ #
67
+ # Process the NTLM type 1 message and build a type 2 response message.
68
+ #
69
+ # @param [Net::NTLM::Message::Type1] type1_msg the NTLM type 1 message received by the client that should be
70
+ # processed
71
+ # @return [Net::NTLM::Message::Type2] the NTLM type 2 response message with which to reply to the client
72
+ def process_ntlm_type1(type1_msg)
73
+ type2_msg = Net::NTLM::Message::Type2.new.tap do |msg|
74
+ msg.target_name = 'LOCALHOST'.encode('UTF-16LE').b
75
+ msg.flag = 0
76
+ %i{ KEY56 KEY128 KEY_EXCHANGE UNICODE TARGET_INFO VERSION_INFO }.each do |flag|
77
+ msg.flag |= NTLM::NEGOTIATE_FLAGS.fetch(flag)
78
+ end
79
+
80
+ @server_challenge = @provider.generate_server_challenge
81
+ msg.challenge = @server_challenge.unpack1('Q<') # 64-bit unsigned, little endian (uint64_t)
82
+ target_info = Net::NTLM::TargetInfo.new('')
83
+ target_info.av_pairs.merge!({
84
+ Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME => @provider.netbios_domain.encode('UTF-16LE').b,
85
+ Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME => @provider.netbios_hostname.encode('UTF-16LE').b,
86
+ Net::NTLM::TargetInfo::MSV_AV_DNS_DOMAIN_NAME => @provider.dns_domain.encode('UTF-16LE').b,
87
+ Net::NTLM::TargetInfo::MSV_AV_DNS_COMPUTER_NAME => @provider.dns_hostname.encode('UTF-16LE').b,
88
+ Net::NTLM::TargetInfo::MSV_AV_TIMESTAMP => [(Time.now.to_i + Net::NTLM::TIME_OFFSET) * Field::FileTime::NS_MULTIPLIER].pack('Q')
89
+ })
90
+ msg.target_info = target_info.to_s
91
+ msg.enable(:target_info)
92
+ msg.context = 0
93
+ msg.enable(:context)
94
+ msg.os_version = NTLM::OSVersion.new(major: 6, minor: 3).to_binary_s
95
+ msg.enable(:os_version)
96
+ end
97
+
98
+ type2_msg
99
+ end
100
+
101
+ #
102
+ # Process the NTLM type 3 message and either accept or reject the authentication attempt.
103
+ #
104
+ # @param [Net::NTLM::Message::Type3] type3_msg the NTLM type 3 message received by the client that should be
105
+ # processed
106
+ # @return [WindowsError::ErrorCode] an NT Status error code representing the operations outcome where
107
+ # STATUS_SUCCESS is a successful authentication attempt and anything else is a failure
108
+ def process_ntlm_type3(type3_msg)
109
+ if type3_msg.user == '' && type3_msg.domain == ''
110
+ if @provider.allow_anonymous
111
+ return WindowsError::NTStatus::STATUS_SUCCESS
112
+ end
113
+
114
+ return WindowsError::NTStatus::STATUS_LOGON_FAILURE
115
+ end
116
+
117
+ account = @provider.get_account(
118
+ type3_msg.user,
119
+ domain: type3_msg.domain
120
+ )
121
+ return WindowsError::NTStatus::STATUS_LOGON_FAILURE if account.nil?
122
+
123
+ matches = false
124
+ case type3_msg.ntlm_version
125
+ when :ntlmv1
126
+ my_ntlm_response = Net::NTLM::ntlm_response(
127
+ ntlm_hash: Net::NTLM::ntlm_hash(account.password.encode('UTF-16LE'), unicode: true),
128
+ challenge: @server_challenge
129
+ )
130
+ matches = my_ntlm_response == type3_msg.ntlm_response
131
+ when :ntlmv2
132
+ digest = OpenSSL::Digest::MD5.new
133
+ their_nt_proof_str = type3_msg.ntlm_response[0...digest.digest_length]
134
+ their_blob = type3_msg.ntlm_response[digest.digest_length..-1]
135
+
136
+ ntlmv2_hash = Net::NTLM.ntlmv2_hash(
137
+ account.username.encode('UTF-16LE'),
138
+ account.password.encode('UTF-16LE'),
139
+ type3_msg.domain.encode('UTF-16LE'), # don't use the account domain because of the special '.' value
140
+ {client_challenge: their_blob[16...24], unicode: true}
141
+ )
142
+
143
+ my_nt_proof_str = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, @server_challenge + their_blob)
144
+ matches = my_nt_proof_str == their_nt_proof_str
145
+ if matches
146
+ user_session_key = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, my_nt_proof_str)
147
+ if type3_msg.flag & NTLM::NEGOTIATE_FLAGS[:KEY_EXCHANGE] == NTLM::NEGOTIATE_FLAGS[:KEY_EXCHANGE] && type3_msg.session_key.length == 16
148
+ rc4 = OpenSSL::Cipher.new('rc4')
149
+ rc4.decrypt
150
+ rc4.key = user_session_key
151
+ @session_key = rc4.update type3_msg.session_key
152
+ @session_key << rc4.final
153
+ else
154
+ @session_key = user_session_key
155
+ end
156
+ end
157
+ else
158
+ # the only other value Net::NTLM will return for this is ntlm_session
159
+ raise NotImplementedError, "authentication via ntlm version #{type3_msg.ntlm_version} is not supported"
160
+ end
161
+
162
+ return WindowsError::NTStatus::STATUS_LOGON_FAILURE unless matches
163
+
164
+ WindowsError::NTStatus::STATUS_SUCCESS
165
+ end
166
+
167
+ attr_accessor :server_challenge
168
+
169
+ private
170
+
171
+ # take the GSS blob, extract the NTLM type 1 message and pass it to the process method to build the response
172
+ # which is then put back into a new GSS reply-blob
173
+ def process_gss_type1(gss_api)
174
+ unless Gss.asn1dig(gss_api, 1, 0, 0, 0, 0)&.value == Gss::OID_NTLMSSP.value
175
+ return
176
+ end
177
+
178
+ raw_type1_msg = Gss.asn1dig(gss_api, 1, 0, 1, 0)&.value
179
+ return unless raw_type1_msg
180
+
181
+ type1_msg = Net::NTLM::Message.parse(raw_type1_msg)
182
+ if type1_msg.flag & NTLM::NEGOTIATE_FLAGS[:UNICODE] == NTLM::NEGOTIATE_FLAGS[:UNICODE]
183
+ type1_msg.domain.force_encoding('UTF-16LE')
184
+ type1_msg.workstation.force_encoding('UTF-16LE')
185
+ end
186
+ type2_msg = process_ntlm_type1(type1_msg)
187
+
188
+ Result.new(Gss.gss_type2(type2_msg.serialize), WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED)
189
+ end
190
+
191
+ # take the GSS blob, extract the NTLM type 3 message and pass it to the process method to build the response
192
+ # which is then put back into a new GSS reply-blob
193
+ def process_gss_type3(gss_api)
194
+ neg_token_init = Hash[RubySMB::Gss.asn1dig(gss_api, 0).value.map { |obj| [obj.tag, obj.value[0].value] }]
195
+ raw_type3_msg = neg_token_init[2]
196
+
197
+ type3_msg = Net::NTLM::Message.parse(raw_type3_msg)
198
+ if type3_msg.flag & NTLM::NEGOTIATE_FLAGS[:UNICODE] == NTLM::NEGOTIATE_FLAGS[:UNICODE]
199
+ type3_msg.domain.force_encoding('UTF-16LE')
200
+ type3_msg.user.force_encoding('UTF-16LE')
201
+ type3_msg.workstation.force_encoding('UTF-16LE')
202
+ end
203
+
204
+ nt_status = process_ntlm_type3(type3_msg)
205
+ buffer = identity = nil
206
+
207
+ case nt_status
208
+ when WindowsError::NTStatus::STATUS_SUCCESS
209
+ buffer = OpenSSL::ASN1::ASN1Data.new([
210
+ OpenSSL::ASN1::Sequence.new([
211
+ OpenSSL::ASN1::ASN1Data.new([
212
+ OpenSSL::ASN1::Enumerated.new(OpenSSL::BN.new(0)),
213
+ ], 0, :CONTEXT_SPECIFIC)
214
+ ])
215
+ ], 1, :CONTEXT_SPECIFIC).to_der
216
+
217
+ account = @provider.get_account(
218
+ type3_msg.user,
219
+ domain: type3_msg.domain
220
+ )
221
+ if account.nil?
222
+ if @provider.allow_anonymous
223
+ identity = IDENTITY_ANONYMOUS
224
+ end
225
+ else
226
+ identity = account.to_s
227
+ end
228
+ end
229
+
230
+ Result.new(buffer, nt_status, identity)
231
+ end
232
+ end
233
+
234
+ # @param [Boolean] allow_anonymous whether or not to allow anonymous authentication attempts
235
+ # @param [String] default_domain the default domain to use for authentication, unless specified 'WORKGROUP' will
236
+ # be used
237
+ def initialize(allow_anonymous: false, default_domain: 'WORKGROUP')
238
+ raise ArgumentError, 'Must specify a default domain' unless default_domain
239
+
240
+ @allow_anonymous = allow_anonymous
241
+ @default_domain = default_domain
242
+ @accounts = []
243
+ @generate_server_challenge = -> { SecureRandom.bytes(8) }
244
+
245
+ @dns_domain = @netbios_domain = 'LOCALDOMAIN'
246
+ @dns_hostname = @netbios_hostname = 'LOCALHOST'
247
+ end
248
+
249
+ #
250
+ # Generate the 8-byte server challenge. If a block is specified, it's used as the challenge generation routine
251
+ # and should return an 8-byte value.
252
+ #
253
+ # @return [String] an 8-byte challenge value
254
+ def generate_server_challenge(&block)
255
+ if block.nil?
256
+ @generate_server_challenge.call
257
+ else
258
+ @generate_server_challenge = block
259
+ end
260
+ end
261
+
262
+ def new_authenticator(server_client)
263
+ # build and return an instance that can process and track stateful information for a particular connection but
264
+ # that's backed by this particular provider
265
+ Authenticator.new(self, server_client)
266
+ end
267
+
268
+ #
269
+ # Lookup and return an account based on the username and optionally, the domain. If no domain is specified or
270
+ # or it is the special value '.', the default domain will be used. The username and domain values are case
271
+ # insensitive.
272
+ #
273
+ # @param [String] username the username of the account to fetch.
274
+ # @param [String, nil] domain the domain in which the account to fetch exists.
275
+ # @return [Account, nil] the account if it was found
276
+ def get_account(username, domain: nil)
277
+ # the username and password values should use the native encoding for the comparison in the #find operation
278
+ username = username.downcase
279
+ domain = @default_domain if domain.nil? || domain == '.'.encode(domain.encoding)
280
+ domain = domain.downcase
281
+ @accounts.find { |account| account.username.encode(username.encoding).downcase == username && account.domain.encode(domain.encoding).downcase == domain }
282
+ end
283
+
284
+ #
285
+ # Add an account to the database.
286
+ #
287
+ # @param [String] username the username of the account to add
288
+ # @param [String] password either the plaintext password or the NTLM hash of the account to add
289
+ # @param [String] domain the domain of the account to add, if not specified, the @default_domain will be used
290
+ def put_account(username, password, domain: nil)
291
+ domain = @default_domain if domain.nil? || domain == '.'.encode(domain.encoding)
292
+ @accounts << Account.new(username, password, domain)
293
+ end
294
+
295
+ #
296
+ # The default domain value to use for accounts which do not have one specified or use the special '.' value.
297
+ attr_reader :default_domain
298
+
299
+ attr_accessor :dns_domain, :dns_hostname, :netbios_domain, :netbios_hostname
300
+ end
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,35 @@
1
+ module RubySMB
2
+ module Gss
3
+ #
4
+ # This module provides GSS based authentication.
5
+ #
6
+ module Provider
7
+ # A special constant implying that the authenticated user is anonymous.
8
+ IDENTITY_ANONYMOUS = :anonymous
9
+ # The result of a processed GSS request.
10
+ Result = Struct.new(:buffer, :nt_status, :identity)
11
+
12
+ #
13
+ # The base class for a GSS authentication provider. This class defines a common interface and is not usable as a
14
+ # provider on its own.
15
+ #
16
+ class Base
17
+ # Create a new, client-specific authenticator instance. This new instance is then able to track the unique state
18
+ # of a particular client / connection.
19
+ #
20
+ # @param [Server::ServerClient] server_client the client instance that this the authenticator will be for
21
+ def new_authenticator(server_client)
22
+ raise NotImplementedError
23
+ end
24
+
25
+ #
26
+ # Whether or not anonymous authentication attempts should be permitted.
27
+ #
28
+ attr_accessor :allow_anonymous
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ require 'ruby_smb/gss/provider/authenticator'
35
+ require 'ruby_smb/gss/provider/ntlm'
data/lib/ruby_smb/gss.rb CHANGED
@@ -2,6 +2,27 @@ module RubySMB
2
2
  # module containing methods required for using the [GSS-API](http://www.rfc-editor.org/rfc/rfc2743.txt)
3
3
  # for Secure Protected Negotiation(SPNEGO) in SMB Authentication.
4
4
  module Gss
5
+ require 'ruby_smb/gss/provider'
6
+
7
+ OID_SPNEGO = OpenSSL::ASN1::ObjectId.new('1.3.6.1.5.5.2')
8
+ OID_NEGOEX = OpenSSL::ASN1::ObjectId.new('1.3.6.1.4.1.311.2.2.30')
9
+ OID_NTLMSSP = OpenSSL::ASN1::ObjectId.new('1.3.6.1.4.1.311.2.2.10')
10
+
11
+ # Allow safe navigation of a decoded ASN.1 data structure. Similar to Ruby's
12
+ # builtin Hash#dig method but using the #value attribute of each ASN object.
13
+ #
14
+ # @param asn The ASN object to apply the traversal path on.
15
+ # @param [Array] path The path to traverse, each element is passed to the
16
+ # ASN object's #value's #[] operator.
17
+ def self.asn1dig(asn, *path)
18
+ path.each do |part|
19
+ return nil unless asn&.value
20
+ asn = asn.value[part]
21
+ end
22
+
23
+ asn
24
+ end
25
+
5
26
  # Cargo culted from Rex. Hacked Together ASN1 encoding that works for our GSS purposes
6
27
  # @todo Document these magic numbers
7
28
  def self.asn1encode(str = '')
@@ -25,78 +46,50 @@ module RubySMB
25
46
  end
26
47
 
27
48
  # Create a GSS Security Blob of an NTLM Type 1 Message.
28
- # This code has been cargo culted and needs to be researched
29
- # and refactored into something better later.
30
- # @todo Refactor this into non-magical code
31
49
  def self.gss_type1(type1)
32
- "\x60".force_encoding('binary') + asn1encode(
33
- "\x06".force_encoding('binary') + asn1encode(
34
- "\x2b\x06\x01\x05\x05\x02".force_encoding('binary')
35
- ) +
36
- "\xa0".force_encoding('binary') + asn1encode(
37
- "\x30".force_encoding('binary') + asn1encode(
38
- "\xa0".force_encoding('binary') + asn1encode(
39
- "\x30".force_encoding('binary') + asn1encode(
40
- "\x06".force_encoding('binary') + asn1encode(
41
- "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a".force_encoding('binary')
42
- )
43
- )
44
- ) +
45
- "\xa2".force_encoding('binary') + asn1encode(
46
- "\x04".force_encoding('binary') + asn1encode(
47
- type1
48
- )
49
- )
50
- )
51
- )
52
- )
50
+ OpenSSL::ASN1::ASN1Data.new([
51
+ OID_SPNEGO,
52
+ OpenSSL::ASN1::ASN1Data.new([
53
+ OpenSSL::ASN1::Sequence.new([
54
+ OpenSSL::ASN1::ASN1Data.new([
55
+ OpenSSL::ASN1::Sequence.new([
56
+ OID_NTLMSSP
57
+ ])
58
+ ], 0, :CONTEXT_SPECIFIC),
59
+ OpenSSL::ASN1::ASN1Data.new([
60
+ OpenSSL::ASN1::OctetString.new(type1)
61
+ ], 2, :CONTEXT_SPECIFIC)
62
+ ])
63
+ ], 0, :CONTEXT_SPECIFIC)
64
+ ], 0, :APPLICATION).to_der
53
65
  end
54
66
 
55
67
  # Create a GSS Security Blob of an NTLM Type 2 Message.
56
- # This code has been cargo culted and needs to be researched
57
- # and refactored into something better later.
58
68
  def self.gss_type2(type2)
59
- blob =
60
- "\xa1" + asn1encode(
61
- "\x30" + asn1encode(
62
- "\xa0" + asn1encode(
63
- "\x0a" + asn1encode(
64
- "\x01"
65
- )
66
- ) +
67
- "\xa1" + asn1encode(
68
- "\x06" + asn1encode(
69
- "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
70
- )
71
- ) +
72
- "\xa2" + asn1encode(
73
- "\x04" + asn1encode(
74
- type2
75
- )
76
- )
77
- )
78
- )
79
-
80
- blob
69
+ OpenSSL::ASN1::ASN1Data.new([
70
+ OpenSSL::ASN1::Sequence.new([
71
+ OpenSSL::ASN1::ASN1Data.new([
72
+ OpenSSL::ASN1::Enumerated.new(OpenSSL::BN.new(1))
73
+ ], 0, :CONTEXT_SPECIFIC),
74
+ OpenSSL::ASN1::ASN1Data.new([
75
+ OID_NTLMSSP
76
+ ], 1, :CONTEXT_SPECIFIC),
77
+ OpenSSL::ASN1::ASN1Data.new([
78
+ OpenSSL::ASN1::OctetString.new(type2)
79
+ ], 2, :CONTEXT_SPECIFIC)
80
+ ])
81
+ ], 1, :CONTEXT_SPECIFIC).to_der
81
82
  end
82
83
 
83
84
  # Create a GSS Security Blob of an NTLM Type 3 Message.
84
- # This code has been cargo culted and needs to be researched
85
- # and refactored into something better later.
86
- # @todo Refactor this into non-magical code
87
85
  def self.gss_type3(type3)
88
- gss =
89
- "\xa1".force_encoding('binary') + asn1encode(
90
- "\x30".force_encoding('binary') + asn1encode(
91
- "\xa2".force_encoding('binary') + asn1encode(
92
- "\x04".force_encoding('binary') + asn1encode(
93
- type3
94
- )
95
- )
96
- )
97
- )
98
-
99
- gss
86
+ OpenSSL::ASN1::ASN1Data.new([
87
+ OpenSSL::ASN1::Sequence.new([
88
+ OpenSSL::ASN1::ASN1Data.new([
89
+ OpenSSL::ASN1::OctetString.new(type3)
90
+ ], 2, :CONTEXT_SPECIFIC)
91
+ ])
92
+ ], 1, :CONTEXT_SPECIFIC).to_der
100
93
  end
101
94
  end
102
95
  end
@@ -0,0 +1,61 @@
1
+ module RubySMB
2
+ module NTLM
3
+ # [[MS-NLMP] 2.2.2.5](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832)
4
+ NEGOTIATE_FLAGS = {
5
+ :UNICODE => 1 << 0,
6
+ :OEM => 1 << 1,
7
+ :REQUEST_TARGET => 1 << 2,
8
+ :SIGN => 1 << 4,
9
+ :SEAL => 1 << 5,
10
+ :DATAGRAM => 1 << 6,
11
+ :LAN_MANAGER_KEY => 1 << 7,
12
+ :NTLM => 1 << 9,
13
+ :NT_ONLY => 1 << 10,
14
+ :ANONYMOUS => 1 << 11,
15
+ :OEM_DOMAIN_SUPPLIED => 1 << 12,
16
+ :OEM_WORKSTATION_SUPPLIED => 1 << 13,
17
+ :ALWAYS_SIGN => 1 << 15,
18
+ :TARGET_TYPE_DOMAIN => 1 << 16,
19
+ :TARGET_TYPE_SERVER => 1 << 17,
20
+ :TARGET_TYPE_SHARE => 1 << 18,
21
+ :EXTENDED_SECURITY => 1 << 19,
22
+ :IDENTIFY => 1 << 20,
23
+ :NON_NT_SESSION => 1 << 22,
24
+ :TARGET_INFO => 1 << 23,
25
+ :VERSION_INFO => 1 << 25,
26
+ :KEY128 => 1 << 29,
27
+ :KEY_EXCHANGE => 1 << 30,
28
+ :KEY56 => 1 << 31
29
+ }.freeze
30
+
31
+ DEFAULT_CLIENT_FLAGS =
32
+ NEGOTIATE_FLAGS[:UNICODE] |
33
+ NEGOTIATE_FLAGS[:SIGN] |
34
+ NEGOTIATE_FLAGS[:SEAL] |
35
+ NEGOTIATE_FLAGS[:REQUEST_TARGET] |
36
+ NEGOTIATE_FLAGS[:NTLM] |
37
+ NEGOTIATE_FLAGS[:ALWAYS_SIGN] |
38
+ NEGOTIATE_FLAGS[:EXTENDED_SECURITY] |
39
+ NEGOTIATE_FLAGS[:KEY128] |
40
+ NEGOTIATE_FLAGS[:KEY_EXCHANGE] |
41
+ NEGOTIATE_FLAGS[:KEY56] |
42
+ NEGOTIATE_FLAGS[:TARGET_INFO] |
43
+ NEGOTIATE_FLAGS[:VERSION_INFO]
44
+
45
+ # [[MS-NLMP] 2.2.2.10 VERSION](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b1a6ceb2-f8ad-462b-b5af-f18527c48175)
46
+ class OSVersion < BinData::Record
47
+ endian :little
48
+
49
+ uint8 :major
50
+ uint8 :minor
51
+ uint16 :build
52
+ uint24 :reserved
53
+ uint8 :ntlm_revision, initial_value: 15
54
+
55
+ def to_s
56
+ "Version #{major}.#{minor} (Build #{build}); NTLM Current Revision #{ntlm_revision}"
57
+ end
58
+ end
59
+ end
60
+ end
61
+