ruby_smb 2.0.12 → 2.0.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (194) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/verify.yml +1 -1
  4. data/examples/dump_secrets_from_sid.rb +207 -0
  5. data/examples/enum_domain_users.rb +75 -0
  6. data/examples/get_computer_info.rb +42 -0
  7. data/examples/query_service_status.rb +42 -4
  8. data/lib/ruby_smb/client.rb +3 -14
  9. data/lib/ruby_smb/dcerpc/bind.rb +28 -20
  10. data/lib/ruby_smb/dcerpc/bind_ack.rb +29 -28
  11. data/lib/ruby_smb/dcerpc/client.rb +542 -0
  12. data/lib/ruby_smb/dcerpc/drsr/drs_bind_request.rb +24 -0
  13. data/lib/ruby_smb/dcerpc/drsr/drs_bind_response.rb +26 -0
  14. data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_request.rb +57 -0
  15. data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_response.rb +76 -0
  16. data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_request.rb +46 -0
  17. data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_response.rb +168 -0
  18. data/lib/ruby_smb/dcerpc/drsr/drs_extensions.rb +56 -0
  19. data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_request.rb +121 -0
  20. data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_response.rb +118 -0
  21. data/lib/ruby_smb/dcerpc/drsr/drs_unbind_request.rb +24 -0
  22. data/lib/ruby_smb/dcerpc/drsr/drs_unbind_response.rb +26 -0
  23. data/lib/ruby_smb/dcerpc/drsr.rb +909 -0
  24. data/lib/ruby_smb/dcerpc/epm/epm_ept_map_request.rb +26 -0
  25. data/lib/ruby_smb/dcerpc/epm/epm_ept_map_response.rb +25 -0
  26. data/lib/ruby_smb/dcerpc/epm/epm_twrt.rb +211 -0
  27. data/lib/ruby_smb/dcerpc/epm.rb +75 -0
  28. data/lib/ruby_smb/dcerpc/error.rb +17 -0
  29. data/lib/ruby_smb/dcerpc/ndr.rb +1159 -297
  30. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +3 -13
  31. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +3 -3
  32. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +3 -13
  33. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +1 -1
  34. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +3 -11
  35. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +1 -1
  36. data/lib/ruby_smb/dcerpc/netlogon.rb +5 -4
  37. data/lib/ruby_smb/dcerpc/p_syntax_id_t.rb +4 -3
  38. data/lib/ruby_smb/dcerpc/pdu_header.rb +7 -7
  39. data/lib/ruby_smb/dcerpc/ptypes.rb +1 -0
  40. data/lib/ruby_smb/dcerpc/request.rb +79 -32
  41. data/lib/ruby_smb/dcerpc/response.rb +45 -10
  42. data/lib/ruby_smb/dcerpc/rpc_auth3.rb +28 -0
  43. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +11 -11
  44. data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +118 -0
  45. data/lib/ruby_smb/dcerpc/samr/rpc_sid.rb +150 -0
  46. data/lib/ruby_smb/dcerpc/samr/samr_close_handle_request.rb +23 -0
  47. data/lib/ruby_smb/dcerpc/samr/samr_close_handle_response.rb +24 -0
  48. data/lib/ruby_smb/dcerpc/samr/samr_connect_request.rb +32 -0
  49. data/lib/ruby_smb/dcerpc/samr/samr_connect_response.rb +23 -0
  50. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request.rb +26 -0
  51. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response.rb +55 -0
  52. data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_request.rb +48 -0
  53. data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_response.rb +38 -0
  54. data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_request.rb +23 -0
  55. data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_response.rb +48 -0
  56. data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request.rb +24 -0
  57. data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response.rb +25 -0
  58. data/lib/ruby_smb/dcerpc/samr/samr_open_domain_request.rb +27 -0
  59. data/lib/ruby_smb/dcerpc/samr/samr_open_domain_response.rb +24 -0
  60. data/lib/ruby_smb/dcerpc/samr/samr_open_user_request.rb +26 -0
  61. data/lib/ruby_smb/dcerpc/samr/samr_open_user_response.rb +24 -0
  62. data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request.rb +23 -0
  63. data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response.rb +23 -0
  64. data/lib/ruby_smb/dcerpc/samr.rb +613 -0
  65. data/lib/ruby_smb/dcerpc/sec_trailer.rb +26 -0
  66. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +56 -79
  67. data/lib/ruby_smb/dcerpc/srvsvc.rb +27 -4
  68. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +13 -25
  69. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +2 -2
  70. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +1 -1
  71. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +1 -1
  72. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +1 -1
  73. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +4 -14
  74. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +1 -1
  75. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +3 -11
  76. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +1 -1
  77. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +1 -1
  78. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +12 -11
  79. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +1 -1
  80. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +9 -8
  81. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +3 -3
  82. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +1 -1
  83. data/lib/ruby_smb/dcerpc/svcctl.rb +1 -3
  84. data/lib/ruby_smb/dcerpc/uuid.rb +3 -0
  85. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +2 -2
  86. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +2 -13
  87. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +3 -3
  88. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +3 -20
  89. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +3 -20
  90. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +5 -14
  91. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +5 -14
  92. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +1 -9
  93. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +4 -3
  94. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +5 -6
  95. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +2 -2
  96. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +9 -18
  97. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +4 -14
  98. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +7 -15
  99. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +3 -1
  100. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +0 -9
  101. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +1 -1
  102. data/lib/ruby_smb/dcerpc/winreg.rb +10 -14
  103. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb +26 -0
  104. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response.rb +88 -0
  105. data/lib/ruby_smb/dcerpc/wkssvc.rb +65 -0
  106. data/lib/ruby_smb/dcerpc.rb +41 -11
  107. data/lib/ruby_smb/field/file_time.rb +1 -1
  108. data/lib/ruby_smb/field/string16.rb +5 -1
  109. data/lib/ruby_smb/ntlm.rb +18 -2
  110. data/lib/ruby_smb/smb1/pipe.rb +4 -0
  111. data/lib/ruby_smb/smb2/pipe.rb +4 -0
  112. data/lib/ruby_smb/version.rb +1 -1
  113. data/spec/lib/ruby_smb/client_spec.rb +1 -2
  114. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +69 -41
  115. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +75 -21
  116. data/spec/lib/ruby_smb/dcerpc/client_spec.rb +714 -0
  117. data/spec/lib/ruby_smb/dcerpc/drsr_spec.rb +2169 -0
  118. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +3792 -1373
  119. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +4 -4
  120. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +4 -4
  121. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +2 -2
  122. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +2 -2
  123. data/spec/lib/ruby_smb/dcerpc/p_syntax_id_t_spec.rb +18 -4
  124. data/spec/lib/ruby_smb/dcerpc/pdu_header_spec.rb +27 -1
  125. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +76 -11
  126. data/spec/lib/ruby_smb/dcerpc/response_spec.rb +99 -9
  127. data/spec/lib/ruby_smb/dcerpc/rpc_auth3_spec.rb +75 -0
  128. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +29 -28
  129. data/spec/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string_spec.rb +340 -0
  130. data/spec/lib/ruby_smb/dcerpc/samr/rpc_sid_spec.rb +116 -0
  131. data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_request_spec.rb +40 -0
  132. data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_response_spec.rb +48 -0
  133. data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_request_spec.rb +56 -0
  134. data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_response_spec.rb +47 -0
  135. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request_spec.rb +63 -0
  136. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response_spec.rb +265 -0
  137. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request_spec.rb +52 -0
  138. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response_spec.rb +36 -0
  139. data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_request_spec.rb +56 -0
  140. data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_response_spec.rb +48 -0
  141. data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request_spec.rb +48 -0
  142. data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response_spec.rb +42 -0
  143. data/spec/lib/ruby_smb/dcerpc/samr_spec.rb +420 -0
  144. data/spec/lib/ruby_smb/dcerpc/sec_trailer_spec.rb +92 -0
  145. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +149 -110
  146. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +21 -17
  147. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +56 -79
  148. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +4 -4
  149. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +2 -2
  150. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +2 -2
  151. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +2 -2
  152. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +19 -29
  153. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +2 -2
  154. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +9 -15
  155. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +2 -2
  156. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +2 -2
  157. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +22 -22
  158. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +2 -2
  159. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +18 -14
  160. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +5 -4
  161. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +2 -2
  162. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +1 -5
  163. data/spec/lib/ruby_smb/dcerpc/uuid_spec.rb +15 -23
  164. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +2 -2
  165. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +4 -41
  166. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +4 -4
  167. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +4 -52
  168. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +4 -56
  169. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +10 -34
  170. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +10 -34
  171. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +2 -26
  172. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +2 -2
  173. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +17 -25
  174. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +2 -2
  175. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +20 -44
  176. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +8 -32
  177. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +10 -22
  178. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +4 -0
  179. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +0 -12
  180. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +2 -2
  181. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +18 -47
  182. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb +43 -0
  183. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb +410 -0
  184. data/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +70 -0
  185. data/spec/lib/ruby_smb/field/string16_spec.rb +22 -0
  186. data/spec/lib/ruby_smb/gss/provider/ntlm/os_version_spec.rb +1 -1
  187. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +18 -37
  188. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +18 -16
  189. data/spec/support/bin_helper.rb +9 -0
  190. data.tar.gz.sig +0 -0
  191. metadata +96 -5
  192. metadata.gz.sig +0 -0
  193. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +0 -38
  194. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +0 -135
@@ -0,0 +1,714 @@
1
+ require 'ruby_smb/dcerpc/client'
2
+
3
+ RSpec.describe RubySMB::Dcerpc::Client do
4
+ it 'includes the RubySMB::Dcerpc::Epm class' do
5
+ expect(described_class < RubySMB::Dcerpc::Epm).to be true
6
+ end
7
+
8
+ let(:host) { '1.2.3.4' }
9
+ let(:endpoint) { RubySMB::Dcerpc::Samr }
10
+
11
+ subject(:client) { described_class.new(host, endpoint) }
12
+ subject(:auth_client) { described_class.new(host, endpoint, username: 'testuser', password: '1234') }
13
+
14
+ it { is_expected.to respond_to :domain }
15
+ it { is_expected.to respond_to :local_workstation }
16
+ it { is_expected.to respond_to :ntlm_client }
17
+ it { is_expected.to respond_to :username }
18
+ it { is_expected.to respond_to :password }
19
+ it { is_expected.to respond_to :default_name }
20
+ it { is_expected.to respond_to :default_domain }
21
+ it { is_expected.to respond_to :dns_host_name }
22
+ it { is_expected.to respond_to :dns_domain_name }
23
+ it { is_expected.to respond_to :dns_tree_name }
24
+ it { is_expected.to respond_to :os_version }
25
+ it { is_expected.to respond_to :max_buffer_size }
26
+ it { is_expected.to respond_to :tcp_socket }
27
+
28
+ describe '#auth_ctx_id_base' do
29
+ it 'is randomly generated' do
30
+ expect(
31
+ 20.times.map do
32
+ described_class.new(host, endpoint).instance_variable_get(:@auth_ctx_id_base)
33
+ end.uniq.size
34
+ ).to eq(20)
35
+ end
36
+ end
37
+
38
+ describe '#ntlm_client' do
39
+ context 'without a username and a password' do
40
+ it 'does not instantiate a net::ntlm::client' do
41
+ expect(client.ntlm_client).to be nil
42
+ end
43
+ end
44
+
45
+ context 'with a username and a password' do
46
+ it 'instantiates a Net::NTLM::Client' do
47
+ expect(auth_client.ntlm_client).to be_a Net::NTLM::Client
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#connect' do
53
+ let(:tcp_socket) { double('TcpSocket') }
54
+ before :example do
55
+ allow(TCPSocket).to receive(:new).and_return(tcp_socket)
56
+ end
57
+
58
+ it 'does nothing if a TcpSocket has been provided already' do
59
+ client.tcp_socket = double('TcpSocket')
60
+ expect(client.connect).to be nil
61
+ expect(TCPSocket).to_not have_received(:new)
62
+ end
63
+
64
+ context 'without initial TcpSocket' do
65
+ context 'when a TCP port is provided' do
66
+ it 'connects to this port' do
67
+ expect(client.connect(port: 123)).to eq(tcp_socket)
68
+ expect(TCPSocket).to have_received(:new).with(host, 123)
69
+ end
70
+ end
71
+
72
+ context 'without TCP port' do
73
+ let(:host_port) { {host: '0.9.8.7', port: 999} }
74
+ let(:epm_tcp_socket) { double('EPM TcpSocket') }
75
+ before :example do
76
+ allow(TCPSocket).to receive(:new).with(host, 135).and_return(epm_tcp_socket)
77
+ allow(client).to receive(:bind)
78
+ allow(client).to receive(:get_host_port_from_ept_mapper).and_return(host_port)
79
+ allow(epm_tcp_socket).to receive(:close)
80
+ end
81
+
82
+ it 'connects to port 135' do
83
+ client.connect
84
+ expect(TCPSocket).to have_received(:new).with(host, 135)
85
+ end
86
+
87
+ it 'binds to the Endpoint Mapper endpoint' do
88
+ client.connect
89
+ expect(client).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Epm)
90
+ end
91
+
92
+ it 'gets host and port information from the Endpoint Mapper' do
93
+ client.connect
94
+ expect(client).to have_received(:get_host_port_from_ept_mapper).with(
95
+ uuid: endpoint::UUID,
96
+ maj_ver: endpoint::VER_MAJOR,
97
+ min_ver: endpoint::VER_MINOR
98
+ )
99
+ end
100
+
101
+ it 'closes the EPM socket' do
102
+ client.connect
103
+ expect(epm_tcp_socket).to have_received(:close)
104
+ end
105
+
106
+ it 'connects to the endpoint and returns the socket' do
107
+ expect(client.connect).to eq(tcp_socket)
108
+ expect(TCPSocket).to have_received(:new).with(host, 999)
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ describe '#close' do
115
+ let(:tcp_socket) { double('TcpSocket') }
116
+ before :example do
117
+ client.tcp_socket = tcp_socket
118
+ allow(tcp_socket).to receive(:close)
119
+ end
120
+
121
+ it 'closes the socket' do
122
+ allow(tcp_socket).to receive(:closed?).and_return(false)
123
+ client.close
124
+ expect(tcp_socket).to have_received(:close)
125
+ end
126
+
127
+ it 'does not closes the socket if it is already closed' do
128
+ allow(tcp_socket).to receive(:closed?).and_return(true)
129
+ client.close
130
+ expect(tcp_socket).to_not have_received(:close)
131
+ end
132
+ end
133
+
134
+ context 'with authentication' do
135
+ describe '#add_auth_verifier' do
136
+ let(:req) { RubySMB::Dcerpc::Bind.new }
137
+ let(:auth_type) { RubySMB::Dcerpc::RPC_C_AUTHN_WINNT }
138
+ let(:auth_level) { 0 }
139
+ let(:auth) { 'serialized auth value' }
140
+
141
+ it 'sets #auth_value field to the expected value' do
142
+ auth_client.add_auth_verifier(req, auth, auth_type, auth_level)
143
+ expect(req.auth_value).to eq(auth)
144
+ end
145
+
146
+ it 'sets PDUHeader #auth_length field to the expected value' do
147
+ auth_client.add_auth_verifier(req, auth, auth_type, auth_level)
148
+ expect(req.pdu_header.auth_length).to eq(auth.length)
149
+ end
150
+
151
+ it 'sets #sec_trailer field to the expected value' do
152
+ auth_client.add_auth_verifier(req, auth, auth_type, auth_level)
153
+ expect(req.sec_trailer.auth_type).to eq(auth_type)
154
+ expect(req.sec_trailer.auth_level).to eq(auth_level)
155
+ expect(req.sec_trailer.auth_context_id).to eq(auth_client.instance_variable_get(:@auth_ctx_id_base))
156
+ end
157
+ end
158
+
159
+ describe '#process_ntlm_type2' do
160
+ let(:type2_message) do
161
+ '4e544c4d53535000020000000a000a0038000000358289e2e7acb2d8d2f0e12100000000000000'\
162
+ '00aa00aa00420000000602f0230000000f4d0059004c004100420002000a004d0059004c004100'\
163
+ '420001001c00570049004e002d004400500030004d00310042004300370036003800040016006d'\
164
+ '0079006c00610062002e006c006f00630061006c0003003400570049004e002d00440050003000'\
165
+ '4d003100420043003700360038002e006d0079006c00610062002e006c006f00630061006c0005'\
166
+ '0016006d0079006c00610062002e006c006f00630061006c0007000800c0ff2034bd95d7010000'\
167
+ '0000'.unhexlify
168
+ end
169
+
170
+ it 'returns the expected type3 message' do
171
+ expect(auth_client.process_ntlm_type2(type2_message)).to start_with("NTLMSSP\x00\x03\x00\x00\x00")
172
+ end
173
+
174
+ it 'stores the session key' do
175
+ auth_client.process_ntlm_type2(type2_message)
176
+ expect(auth_client.instance_variable_get(:@session_key).size).to eq(16)
177
+ end
178
+
179
+ it 'stores the target information' do
180
+ expect(auth_client).to receive(:store_target_info)
181
+ auth_client.process_ntlm_type2(type2_message)
182
+ end
183
+
184
+ it 'stores the OS version' do
185
+ auth_client.process_ntlm_type2(type2_message)
186
+ expect(auth_client.os_version).to eq('6.2.9200')
187
+ end
188
+ end
189
+
190
+ describe '#send_auth3' do
191
+ let(:auth) { 'NTLMSSP security blob' }
192
+ let(:bindack) { RubySMB::Dcerpc::BindAck.new(auth_value: auth) }
193
+ let(:auth_type) { RubySMB::Dcerpc::RPC_C_AUTHN_WINNT }
194
+ let(:auth_level) { 0 }
195
+ let(:auth3) { double('Type3 message') }
196
+ let(:rpc_auth3) { RubySMB::Dcerpc::RpcAuth3.new }
197
+ before :example do
198
+ allow(auth_client).to receive(:process_ntlm_type2).and_return(auth3)
199
+ allow(RubySMB::Dcerpc::RpcAuth3).to receive(:new).and_return(rpc_auth3)
200
+ allow(auth_client).to receive(:add_auth_verifier)
201
+ allow(auth_client).to receive(:send_packet)
202
+ end
203
+
204
+ it 'add an auth verifier to the RpcAuth3 packet' do
205
+ auth_client.send_auth3(bindack, auth_type, auth_level)
206
+ expect(auth_client).to have_received(:add_auth_verifier).with(rpc_auth3, auth3, auth_type, auth_level)
207
+ end
208
+
209
+ it 'sets the PDUHeader #call_id to the expected value' do
210
+ auth_client.instance_variable_set(:@call_id, 56)
211
+ auth_client.send_auth3(bindack, auth_type, auth_level)
212
+ expect(rpc_auth3.pdu_header.call_id).to eq(56)
213
+ end
214
+
215
+ it 'sends the RpcAuth3 packet' do
216
+ auth_client.send_auth3(bindack, auth_type, auth_level)
217
+ expect(auth_client).to have_received(:send_packet).with(rpc_auth3)
218
+ end
219
+
220
+ it 'increments #call_id' do
221
+ auth_client.send_auth3(bindack, auth_type, auth_level)
222
+ expect(auth_client.instance_variable_get(:@call_id)).to eq(2)
223
+ end
224
+
225
+ context 'with RPC_C_AUTHN_WINNT auth_type' do
226
+ it 'processes NTLM type2 message' do
227
+ auth_client.send_auth3(bindack, auth_type, auth_level)
228
+ expect(auth_client).to have_received(:process_ntlm_type2).with(auth)
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ describe '#bind' do
235
+ let(:bind_req) { RubySMB::Dcerpc::Bind.new(endpoint: endpoint) }
236
+ let(:bindack_response) do
237
+ RubySMB::Dcerpc::BindAck.new({
238
+ p_result_list: {
239
+ p_results: [{
240
+ result: RubySMB::Dcerpc::BindAck::ACCEPTANCE
241
+ }]
242
+ }
243
+ })
244
+ end
245
+ before :example do
246
+ allow(RubySMB::Dcerpc::Bind).to receive(:new).and_return(bind_req)
247
+ allow(client).to receive(:send_packet)
248
+ allow(client).to receive(:recv_struct).and_return(bindack_response)
249
+ end
250
+
251
+ it 'sets the expected call_id value on the Bind request' do
252
+ client.instance_variable_set(:@call_id, 56)
253
+ client.bind
254
+ expect(bind_req.pdu_header.call_id).to eq(56)
255
+ end
256
+
257
+ it 'sends the expected Bind packet' do
258
+ client.bind
259
+ expect(client).to have_received(:send_packet).with(bind_req)
260
+ end
261
+
262
+ context 'without presentation context in the response' do
263
+ let(:bindack_response) { RubySMB::Dcerpc::BindAck.new }
264
+
265
+ it 'raises a BindError exception' do
266
+ expect { client.bind }.to raise_error(RubySMB::Dcerpc::Error::BindError)
267
+ end
268
+ end
269
+
270
+ context 'with a response that refused the presentation context' do
271
+ let(:bindack_response) do
272
+ RubySMB::Dcerpc::BindAck.new({
273
+ p_result_list: {
274
+ p_results: [{
275
+ result: RubySMB::Dcerpc::BindAck::PROVIDER_REJECTION
276
+ }]
277
+ }
278
+ })
279
+ end
280
+
281
+ it 'raises a BindError exception' do
282
+ expect { client.bind }.to raise_error(RubySMB::Dcerpc::Error::BindError)
283
+ end
284
+ end
285
+
286
+ it 'sets @max_buffer_size from the response value' do
287
+ bindack_response.max_xmit_frag = 12345
288
+ client.bind
289
+ expect(client.max_buffer_size).to eq(12345)
290
+ end
291
+
292
+ it 'sets @call_id from the response value' do
293
+ bindack_response.pdu_header.call_id = 45
294
+ client.bind
295
+ expect(client.instance_variable_get(:@call_id)).to eq(45)
296
+ end
297
+
298
+ context 'with authentication' do
299
+ subject(:client) { described_class.new(host, endpoint, username: 'testuser', password: '1234') }
300
+
301
+ context 'with RPC_C_AUTHN_WINNT auth_type' do
302
+ let(:kwargs) do {
303
+ auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_DEFAULT,
304
+ auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
305
+ }
306
+ end
307
+ let(:type1_message) { double('type1 message') }
308
+ let(:auth) { double('auth') }
309
+ before :example do
310
+ allow(client.ntlm_client).to receive(:init_context).and_return(type1_message)
311
+ allow(type1_message).to receive(:serialize).and_return(auth)
312
+ allow(client).to receive(:add_auth_verifier)
313
+ allow(client).to receive(:send_auth3)
314
+ end
315
+
316
+ it 'raises an exception if the NTLM client is not initialized' do
317
+ client.ntlm_client = nil
318
+ expect {client.bind(**kwargs)}.to raise_error(ArgumentError)
319
+ end
320
+
321
+ it 'adds the auth verifier with a NTLM type1 message' do
322
+ client.bind(**kwargs)
323
+ expect(client).to have_received(:add_auth_verifier).with(bind_req, auth, kwargs[:auth_type], kwargs[:auth_level])
324
+ end
325
+
326
+ it 'sends an auth3 request' do
327
+ client.bind(**kwargs)
328
+ expect(client).to have_received(:send_auth3).with(bindack_response, kwargs[:auth_type], kwargs[:auth_level])
329
+ end
330
+ end
331
+ end
332
+ end
333
+
334
+ describe '#store_target_info' do
335
+ let(:target_info_str) do
336
+ '02001400540045005300540044004f004d00410049004e000100100054004500530054004e0041'\
337
+ '004d00450004002000740065007300740064006f006d00610069006e002e006c006f0063006100'\
338
+ '6c000300320074006500730074006e0061006d0065002e00740065007300740064006f006d0061'\
339
+ '0069006e002e006c006f00630061006c0005002000740065007300740066006f00720065007300'\
340
+ '74002e006c006f00630061006c0007000800513777014668d30100000000'.unhexlify
341
+ end
342
+
343
+ it 'creates a Net::NTLM::TargetInfo object from the target_info string' do
344
+ expect(Net::NTLM::TargetInfo).to receive(:new).with(target_info_str).and_call_original
345
+ client.store_target_info(target_info_str)
346
+ end
347
+
348
+ it 'sets the expected Client\'s attribute' do
349
+ client.store_target_info(target_info_str)
350
+ expect(client.default_name).to eq 'TESTNAME'
351
+ expect(client.default_domain).to eq 'TESTDOMAIN'
352
+ expect(client.dns_host_name).to eq 'testname.testdomain.local'
353
+ expect(client.dns_domain_name).to eq 'testdomain.local'
354
+ expect(client.dns_tree_name).to eq 'testforest.local'
355
+ end
356
+
357
+ it 'stores the strings with UTF-8 encoding' do
358
+ client.store_target_info(target_info_str)
359
+ expect(client.default_name.encoding.name).to eq 'UTF-8'
360
+ expect(client.default_domain.encoding.name).to eq 'UTF-8'
361
+ expect(client.dns_host_name.encoding.name).to eq 'UTF-8'
362
+ expect(client.dns_domain_name.encoding.name).to eq 'UTF-8'
363
+ expect(client.dns_tree_name.encoding.name).to eq 'UTF-8'
364
+ end
365
+ end
366
+
367
+ describe '#extract_os_version' do
368
+ it 'returns the expected version number' do
369
+ expect(client.extract_os_version("\x06\x00q\x17\x00\x00\x00\x0F")).to eq '6.0.6001'
370
+ end
371
+ end
372
+
373
+ describe '#set_integrity_privacy' do
374
+ let(:dcerpc_req) do
375
+ RubySMB::Dcerpc::Request.new(
376
+ { opnum: RubySMB::Dcerpc::Winreg::REG_ENUM_KEY },
377
+ { endpoint: 'Winreg' }
378
+ )
379
+ end
380
+ let(:auth_level) { RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY }
381
+ let(:auth_type) { RubySMB::Dcerpc::RPC_C_AUTHN_WINNT }
382
+ let(:session) { double('NTLM Session') }
383
+ let(:encrypted_stub) { 'Encrypted Stub'.b }
384
+ let(:auth_pad) { "\x00" * dcerpc_req.sec_trailer.auth_pad_length.to_i }
385
+ let(:signature) { 'Signature'.b }
386
+ before :example do
387
+ allow(auth_client.ntlm_client).to receive(:session).and_return(session)
388
+ # Make sure the encrypted stub includes the correct pad to make sure sec_trailer is 16-bytes aligned
389
+ allow(session).to receive(:seal_message).and_return(encrypted_stub + auth_pad)
390
+ allow(session).to receive(:sign_message).and_return(signature)
391
+ end
392
+
393
+ it 'sets the sec_trailer to the correct values' do
394
+ auth_client.set_integrity_privacy(dcerpc_req, auth_level: auth_level, auth_type: auth_type)
395
+ expect(dcerpc_req.sec_trailer.auth_type).to eq(auth_type)
396
+ expect(dcerpc_req.sec_trailer.auth_level).to eq(auth_level)
397
+ expect(dcerpc_req.sec_trailer.auth_context_id).to eq(auth_client.instance_variable_get(:@auth_ctx_id_base))
398
+ end
399
+
400
+ it 'sets the PDU header auth_length field to the signature size' do
401
+ auth_client.set_integrity_privacy(dcerpc_req, auth_level: auth_level, auth_type: auth_type)
402
+ expect(dcerpc_req.pdu_header.auth_length).to eq(signature.size)
403
+ end
404
+
405
+ it 'signs the correct message' do
406
+ plain_stub = dcerpc_req.stub.to_binary_s
407
+ auth_client.set_integrity_privacy(dcerpc_req, auth_level: auth_level, auth_type: auth_type)
408
+ expect(dcerpc_req.auth_value).to eq(signature)
409
+ expect(dcerpc_req.pdu_header.auth_length).to eq(signature.size)
410
+
411
+ # Restore the request structure to what it was before signing:
412
+ # First, remove the signature but keep the PDU header auth_length value
413
+ dcerpc_req.auth_value = ''
414
+ dcerpc_req.pdu_header.auth_length = 16
415
+ # Restore the plaintext stub
416
+ dcerpc_req.stub = plain_stub
417
+ # Restore the original fragment length: packet.size + original_signature.size
418
+ dcerpc_req.pdu_header.frag_length = dcerpc_req.num_bytes + 16
419
+ expect(session).to have_received(:sign_message).with(dcerpc_req.to_binary_s)
420
+ end
421
+
422
+ context 'without NTLM EXTENDED_SECURITY flag' do
423
+ it 'signs the correct message' do
424
+ flags = auth_client.ntlm_client.flags ^ RubySMB::NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY]
425
+ allow(auth_client.ntlm_client).to receive(:flags).and_return(flags)
426
+ plain_stub = dcerpc_req.stub.to_binary_s
427
+ auth_client.set_integrity_privacy(dcerpc_req, auth_level: auth_level, auth_type: auth_type)
428
+ expect(dcerpc_req.auth_value).to eq(signature)
429
+ expect(dcerpc_req.pdu_header.auth_length).to eq(signature.size)
430
+ expect(session).to have_received(:sign_message).with(plain_stub + auth_pad)
431
+ end
432
+ end
433
+
434
+ it 'encrypts the stub' do
435
+ plain_stub = dcerpc_req.stub.to_binary_s + dcerpc_req.auth_pad.to_binary_s
436
+ auth_client.set_integrity_privacy(dcerpc_req, auth_level: auth_level, auth_type: auth_type)
437
+ expect(dcerpc_req.stub).to eq(encrypted_stub)
438
+ expect(session).to have_received(:seal_message).with(plain_stub)
439
+ end
440
+
441
+ context 'without RPC_C_AUTHN_LEVEL_PKT_PRIVACY auth_level' do
442
+ it 'does not encrypt the stub' do
443
+ plain_stub = dcerpc_req.stub.to_binary_s
444
+ auth_client.set_integrity_privacy(dcerpc_req, auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, auth_type: auth_type)
445
+ expect(dcerpc_req.stub.to_binary_s).to eq(plain_stub)
446
+ expect(session).to_not have_received(:seal_message)
447
+ end
448
+ end
449
+
450
+ context 'with an unsupported auth_level' do
451
+ it 'raises an Argument exception' do
452
+ expect { auth_client.set_integrity_privacy(dcerpc_req, auth_level: auth_level, auth_type: 88) }.to raise_error(ArgumentError)
453
+ end
454
+ end
455
+ end
456
+
457
+ describe '#dcerpc_request' do
458
+ let(:request_stub) { RubySMB::Dcerpc::Winreg::EnumKeyRequest.new(lp_name: 'Test Name Request') }
459
+ let(:dcerpc_req) do
460
+ RubySMB::Dcerpc::Request.new(
461
+ { opnum: RubySMB::Dcerpc::Winreg::REG_ENUM_KEY },
462
+ { endpoint: endpoint }
463
+ )
464
+ end
465
+ let(:response_stub) { RubySMB::Dcerpc::Winreg::EnumKeyResponse.new(lp_name: 'Test Name Response') }
466
+ let(:dcerpc_res) { RubySMB::Dcerpc::Response.new(stub: response_stub.to_binary_s) }
467
+ before :example do
468
+ allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(dcerpc_req)
469
+ allow(client).to receive(:send_packet)
470
+ allow(client).to receive(:recv_struct).and_return(dcerpc_res)
471
+ end
472
+
473
+ it 'returns the correct response stub' do
474
+ expect(client.dcerpc_request(request_stub)).to eq(response_stub.to_binary_s)
475
+ end
476
+
477
+ it 'sends the correct Request packet' do
478
+ client.dcerpc_request(request_stub)
479
+ expect(client).to have_received(:send_packet).with(dcerpc_req)
480
+ end
481
+
482
+ it 'receives the correct packet' do
483
+ client.dcerpc_request(request_stub)
484
+ expect(client).to have_received(:recv_struct).with(RubySMB::Dcerpc::Response)
485
+ end
486
+
487
+ context 'when the first packet is not the first fragment' do
488
+ it 'raises an InvalidPacket exception' do
489
+ dcerpc_res.pdu_header.pfc_flags.first_frag = 0
490
+ expect { client.dcerpc_request(request_stub) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
491
+ end
492
+ end
493
+
494
+ context 'when requiring privacy' do
495
+ let(:auth_level) { RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY }
496
+ let(:auth_type) { RubySMB::Dcerpc::RPC_C_AUTHN_WINNT }
497
+ before :example do
498
+ allow(client).to receive(:set_integrity_privacy)
499
+ allow(client).to receive(:handle_integrity_privacy)
500
+ end
501
+
502
+ it 'sets integrity and privacy' do
503
+ client.dcerpc_request(request_stub, auth_level: auth_level, auth_type: auth_type)
504
+ expect(client).to have_received(:set_integrity_privacy).with(dcerpc_req, auth_level: auth_level, auth_type: auth_type)
505
+ end
506
+
507
+ it 'processes the integrity and privacy information from the response' do
508
+ client.dcerpc_request(request_stub, auth_level: auth_level, auth_type: auth_type)
509
+ expect(client).to have_received(:handle_integrity_privacy).with(dcerpc_res, auth_level: auth_level, auth_type: auth_type)
510
+ end
511
+ end
512
+
513
+ context 'with a fragmented response' do
514
+ let(:nb_of_fragments) { 3 }
515
+ before :example do
516
+ dcerpc_res.pdu_header.pfc_flags.last_frag = 0
517
+ count = 0
518
+ allow(client).to receive(:recv_struct) do
519
+ count += 1
520
+ dcerpc_res.pdu_header.pfc_flags.last_frag = 1 if count == nb_of_fragments
521
+ dcerpc_res
522
+ end
523
+ end
524
+
525
+ it 'receives the correct number of fragments' do
526
+ client.dcerpc_request(request_stub)
527
+ expect(client).to have_received(:recv_struct).exactly(nb_of_fragments).times.with(RubySMB::Dcerpc::Response)
528
+ end
529
+
530
+ it 'returns the correct stub' do
531
+ expect(client.dcerpc_request(request_stub)).to eq(response_stub.to_binary_s * nb_of_fragments)
532
+ end
533
+
534
+ context 'when requiring privacy' do
535
+ let(:auth_level) { RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY }
536
+ let(:auth_type) { RubySMB::Dcerpc::RPC_C_AUTHN_WINNT }
537
+ before :example do
538
+ allow(client).to receive(:set_integrity_privacy)
539
+ allow(client).to receive(:handle_integrity_privacy)
540
+ end
541
+
542
+ it 'processes the integrity and privacy information for each fragment' do
543
+ client.dcerpc_request(request_stub, auth_level: auth_level, auth_type: auth_type)
544
+ expect(client).to have_received(:recv_struct).exactly(nb_of_fragments).times.with(RubySMB::Dcerpc::Response)
545
+ expect(client).to have_received(:handle_integrity_privacy).exactly(nb_of_fragments).times.with(dcerpc_res, auth_level: auth_level, auth_type: auth_type)
546
+ end
547
+ end
548
+ end
549
+ end
550
+
551
+ describe '#send_packet' do
552
+ let(:packet) { double('Packet') }
553
+ let(:data) { 'My data to be sent' }
554
+ let(:sock) { double('Socket') }
555
+ before :example do
556
+ client.tcp_socket = sock
557
+ allow(packet).to receive(:to_binary_s).and_return(data)
558
+ allow(sock).to receive(:write).and_return(data.size)
559
+ end
560
+
561
+ it 'sends the correct data' do
562
+ client.send_packet(packet)
563
+ expect(sock).to have_received(:write).with(data)
564
+ end
565
+
566
+ context 'when sending multiple chunks of data' do
567
+ it 'sends the correct chunks of data' do
568
+ chunk_size = 6
569
+ allow(sock).to receive(:write).and_return(chunk_size)
570
+ data_sent = data.dup
571
+ client.send_packet(packet)
572
+ loop do
573
+ break if data_sent.empty?
574
+ expect(sock).to have_received(:write).with(data_sent)
575
+ data_sent = data_sent[chunk_size..-1]
576
+ end
577
+ end
578
+ end
579
+
580
+ context 'when an error occurs with the socket' do
581
+ it 'raises a CommunicationError' do
582
+ allow(sock).to receive(:write).and_raise (Errno::EPIPE)
583
+ expect { client.send_packet(packet) }.to raise_error(RubySMB::Dcerpc::Error::CommunicationError)
584
+ end
585
+ end
586
+ end
587
+
588
+ describe '#recv_struct' do
589
+ let(:socket) { double('Socket') }
590
+ let(:struct) { RubySMB::Dcerpc::Request }
591
+ let(:response) { RubySMB::Dcerpc::Response.new(pdu_header: { ptype: struct::PTYPE}) }
592
+ before :example do
593
+ client.tcp_socket = socket
594
+ allow(socket).to receive(:closed?).and_return false
595
+ allow(IO).to receive(:select).and_return [[client.tcp_socket], [], []]
596
+ allow(struct).to receive(:read).and_return(response)
597
+ end
598
+
599
+ it 'reads the socket' do
600
+ client.recv_struct(struct)
601
+ expect(struct).to have_received(:read).with(socket)
602
+ end
603
+
604
+ context 'when the socket is already closed' do
605
+ it 'raises a CommunicationError' do
606
+ allow(socket).to receive(:closed?).and_return true
607
+ expect { client.recv_struct(struct) }.to raise_error(RubySMB::Dcerpc::Error::CommunicationError)
608
+ end
609
+ end
610
+
611
+ context 'when the read timeout expires' do
612
+ it 'raises a CommunicationError' do
613
+ allow(IO).to receive(:select).and_return nil
614
+ expect { client.recv_struct(struct) }.to raise_error(RubySMB::Dcerpc::Error::CommunicationError)
615
+ end
616
+ end
617
+
618
+ context 'when an error occurs when reading the socket' do
619
+ it 'raises an InvalidPacket' do
620
+ allow(struct).to receive(:read).and_raise(IOError)
621
+ expect { client.recv_struct(struct) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
622
+ end
623
+ end
624
+
625
+ context 'when response has a wrong ptype' do
626
+ it 'raises an InvalidPacket' do
627
+ response.pdu_header.ptype = struct::PTYPE + 1
628
+ expect { client.recv_struct(struct) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
629
+ end
630
+ end
631
+
632
+ context 'when the read timeout expires' do
633
+ it 'raises a CommunicationError' do
634
+ allow(struct).to receive(:read).and_raise (Errno::EPIPE)
635
+ expect { client.recv_struct(struct) }.to raise_error(RubySMB::Dcerpc::Error::CommunicationError)
636
+ end
637
+ end
638
+ end
639
+
640
+ describe '#handle_integrity_privacy' do
641
+ let(:stub) { 'Encrypted Stub' }
642
+ let(:dcerpc_res) do
643
+ RubySMB::Dcerpc::Response.new(
644
+ stub: stub,
645
+ auth_value: signature
646
+ )
647
+ end
648
+ let(:auth_level) { RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY }
649
+ let(:auth_type) { RubySMB::Dcerpc::RPC_C_AUTHN_WINNT }
650
+ let(:session) { double('NTLM Session') }
651
+ let(:decrypted_stub) { 'Decrypted Stub'.b }
652
+ let(:auth_pad) { "\x00" * dcerpc_res.sec_trailer.auth_pad_length.to_i }
653
+ let(:signature) { 'Signature'.b }
654
+ before :example do
655
+ allow(auth_client.ntlm_client).to receive(:session).and_return(session)
656
+ # Make sure the encrypted stub includes the correct pad to make sure sec_trailer is 16-bytes aligned
657
+ allow(session).to receive(:unseal_message).and_return(decrypted_stub + auth_pad)
658
+ allow(session).to receive(:verify_signature).and_return true
659
+ end
660
+
661
+ it 'verifies the signature' do
662
+ auth_client.handle_integrity_privacy(dcerpc_res, auth_level: auth_level, auth_type: auth_type)
663
+ data_to_check = dcerpc_res.to_binary_s[0..-(dcerpc_res.pdu_header.auth_length + 1)]
664
+ expect(session).to have_received(:verify_signature).with(signature, data_to_check)
665
+ end
666
+
667
+ context 'without NTLM EXTENDED_SECURITY flag' do
668
+ it 'verifies the signature against the correct data' do
669
+ flags = auth_client.ntlm_client.flags ^ RubySMB::NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY]
670
+ allow(auth_client.ntlm_client).to receive(:flags).and_return(flags)
671
+ auth_client.handle_integrity_privacy(dcerpc_res, auth_level: auth_level, auth_type: auth_type)
672
+ data_to_check = dcerpc_res.stub.to_binary_s
673
+ expect(session).to have_received(:verify_signature).with(signature, data_to_check)
674
+ end
675
+ end
676
+
677
+ context 'when raise_signature_error is set and the signature is wrong' do
678
+ it 'raises an InvalidPacket exception' do
679
+ allow(session).to receive(:verify_signature).and_return false
680
+ expect {
681
+ auth_client.handle_integrity_privacy(
682
+ dcerpc_res,
683
+ auth_level: auth_level,
684
+ auth_type: auth_type,
685
+ raise_signature_error: true
686
+ )
687
+ }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
688
+ end
689
+ end
690
+
691
+ it 'decrypts the stub' do
692
+ encrypted_stub = dcerpc_res.stub.to_binary_s + dcerpc_res.auth_pad.to_binary_s
693
+ auth_client.handle_integrity_privacy(dcerpc_res, auth_level: auth_level, auth_type: auth_type)
694
+ expect(dcerpc_res.stub).to eq(decrypted_stub)
695
+ expect(session).to have_received(:unseal_message).with(encrypted_stub)
696
+ end
697
+
698
+ context 'without RPC_C_AUTHN_LEVEL_PKT_PRIVACY auth_level' do
699
+ it 'does not encrypt the stub' do
700
+ plain_stub = dcerpc_res.stub.to_binary_s
701
+ auth_client.handle_integrity_privacy(dcerpc_res, auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, auth_type: auth_type)
702
+ expect(dcerpc_res.stub).to eq(plain_stub)
703
+ expect(session).to_not have_received(:unseal_message)
704
+ end
705
+ end
706
+
707
+ context 'with an unsupported auth_level' do
708
+ it 'raises an Argument exception' do
709
+ expect { auth_client.handle_integrity_privacy(dcerpc_res, auth_level: auth_level, auth_type: 88) }.to raise_error(ArgumentError)
710
+ end
711
+ end
712
+ end
713
+ end
714
+