ruby_smb 3.1.5 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/verify.yml +27 -4
- data/README.md +1 -2
- data/lib/ruby_smb/client/authentication.rb +4 -30
- data/lib/ruby_smb/dcerpc/client.rb +6 -261
- data/lib/ruby_smb/dcerpc/dfsnm/netr_dfs_add_std_root_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/dfsnm/netr_dfs_add_std_root_response.rb +21 -0
- data/lib/ruby_smb/dcerpc/dfsnm/netr_dfs_remove_std_root_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/dfsnm/netr_dfs_remove_std_root_response.rb +21 -0
- data/lib/ruby_smb/dcerpc/dfsnm.rb +84 -0
- data/lib/ruby_smb/dcerpc/error.rb +21 -0
- data/lib/ruby_smb/dcerpc/icpr/cert_server_request_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/icpr/cert_server_request_response.rb +28 -0
- data/lib/ruby_smb/dcerpc/icpr.rb +84 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +38 -2
- data/lib/ruby_smb/dcerpc/request.rb +10 -1
- data/lib/ruby_smb/dcerpc.rb +246 -12
- data/lib/ruby_smb/error.rb +25 -11
- data/lib/ruby_smb/peer_info.rb +39 -0
- data/lib/ruby_smb/signing.rb +2 -0
- data/lib/ruby_smb/smb1/pipe.rb +23 -1
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +5 -1
- data/lib/ruby_smb/smb2/pipe.rb +23 -0
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/dcerpc/dfsnm/netr_dfs_add_std_root_request_spec.rb +57 -0
- data/spec/lib/ruby_smb/dcerpc/dfsnm/netr_dfs_add_std_root_response_spec.rb +34 -0
- data/spec/lib/ruby_smb/dcerpc/dfsnm/netr_dfs_remove_std_root_request_spec.rb +49 -0
- data/spec/lib/ruby_smb/dcerpc/dfsnm/netr_dfs_remove_std_root_response_spec.rb +34 -0
- data/spec/lib/ruby_smb/dcerpc/icpr/cert_server_request_request_spec.rb +64 -0
- data/spec/lib/ruby_smb/dcerpc/icpr/cert_server_request_response_spec.rb +71 -0
- data/spec/lib/ruby_smb/dcerpc/icpr/cert_trans_blob_spec.rb +33 -0
- data/spec/lib/ruby_smb/dcerpc_spec.rb +2 -1
- data/spec/spec_helper.rb +6 -8
- data/spec/support/openssl.conf +14 -0
- data.tar.gz.sig +0 -0
- metadata +27 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ce8d4ea99335efaf6bbc1ef003231ffa8a809382760e54dc26be04b76e53529
|
4
|
+
data.tar.gz: f284dabf7fe032ae02933a517abb2371f7c7c37eb641876f1cbf2b8cc0e4c2ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5cf115ffd4c634f593bceef2bb503d2c0a2b19c5cfa73391887d35ce2691472fabda03e47462b716f8c870d7dedbc5dc815f644887b259d1268658752396ea17
|
7
|
+
data.tar.gz: 46ba6053ca246692ef29c545c8a9682b6675c4bf50d10d3611107bf537470226986aeece854516d59d99afa1642ee2d72fed99f39c1731112292fcbb6d8138d2
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -1,5 +1,21 @@
|
|
1
1
|
name: Verify
|
2
2
|
|
3
|
+
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
4
|
+
permissions:
|
5
|
+
actions: none
|
6
|
+
checks: none
|
7
|
+
contents: none
|
8
|
+
deployments: none
|
9
|
+
id-token: none
|
10
|
+
issues: none
|
11
|
+
discussions: none
|
12
|
+
packages: none
|
13
|
+
pages: none
|
14
|
+
pull-requests: none
|
15
|
+
repository-projects: none
|
16
|
+
security-events: none
|
17
|
+
statuses: none
|
18
|
+
|
3
19
|
on:
|
4
20
|
push:
|
5
21
|
branches:
|
@@ -10,7 +26,7 @@ on:
|
|
10
26
|
|
11
27
|
jobs:
|
12
28
|
test:
|
13
|
-
runs-on:
|
29
|
+
runs-on: ${{ matrix.os }}
|
14
30
|
timeout-minutes: 40
|
15
31
|
|
16
32
|
strategy:
|
@@ -19,15 +35,22 @@ jobs:
|
|
19
35
|
ruby:
|
20
36
|
- 2.6
|
21
37
|
- 2.7
|
22
|
-
- 3.0
|
23
|
-
- 3.1
|
38
|
+
- 3.0
|
39
|
+
- 3.1
|
40
|
+
os:
|
41
|
+
- ubuntu-18.04
|
42
|
+
- ubuntu-22.04
|
43
|
+
exclude:
|
44
|
+
- { os: ubuntu-22.04, ruby: 2.6 }
|
45
|
+
- { os: ubuntu-22.04, ruby: 2.7 }
|
46
|
+
- { os: ubuntu-22.04, ruby: 3.0 }
|
24
47
|
test_cmd:
|
25
48
|
- bundle exec rspec
|
26
49
|
|
27
50
|
env:
|
28
51
|
RAILS_ENV: test
|
29
52
|
|
30
|
-
name: Ruby ${{ matrix.ruby }} - ${{ matrix.test_cmd }}
|
53
|
+
name: ${{ matrix.os }} - Ruby ${{ matrix.ruby }} - ${{ matrix.test_cmd }}
|
31
54
|
steps:
|
32
55
|
- name: Checkout code
|
33
56
|
uses: actions/checkout@v2
|
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# RubySMB
|
2
2
|
|
3
3
|
[![Code Climate](https://codeclimate.com/github/rapid7/ruby_smb.png)](https://codeclimate.com/github/rapid7/ruby_smb)
|
4
|
-
[![Coverage Status](https://coveralls.io/repos/github/rapid7/ruby_smb/badge.svg?branch=master)](https://coveralls.io/github/rapid7/ruby_smb?branch=master)
|
5
4
|
|
6
5
|
This is a native Ruby implementation of the SMB Protocol Family. It currently supports:
|
7
6
|
|
@@ -187,7 +186,7 @@ Example:
|
|
187
186
|
```
|
188
187
|
### Using the Client
|
189
188
|
|
190
|
-
Sitting on top of the packet layer in RubySMB is the RubySMB::Client class. This is the abstraction that most users of RubySMB will interact with. It provides simple
|
189
|
+
Sitting on top of the packet layer in RubySMB is the RubySMB::Client class. This is the abstraction that most users of RubySMB will interact with. It provides simple convenience methods for performing SMB actions. It handles the creation, sending and receiving of packets for the user, providing reasonable defaults in many cases.
|
191
190
|
|
192
191
|
#### Negotiation
|
193
192
|
|
@@ -1,7 +1,11 @@
|
|
1
|
+
require 'ruby_smb/peer_info'
|
2
|
+
|
1
3
|
module RubySMB
|
2
4
|
class Client
|
3
5
|
# This module holds all the backend client methods for authentication.
|
4
6
|
module Authentication
|
7
|
+
include RubySMB::PeerInfo
|
8
|
+
|
5
9
|
# Responsible for handling Authentication and Session Setup for
|
6
10
|
# the SMB Client. It returns the final Status code from the authentication
|
7
11
|
# exchange.
|
@@ -356,36 +360,6 @@ module RubySMB
|
|
356
360
|
packet.security_mode.signing_enabled = 1
|
357
361
|
packet
|
358
362
|
end
|
359
|
-
|
360
|
-
# Extract and store useful information about the peer/server from the
|
361
|
-
# NTLM Type 2 (challenge) TargetInfo fields.
|
362
|
-
#
|
363
|
-
# @param target_info_str [String] the Target Info string
|
364
|
-
def store_target_info(target_info_str)
|
365
|
-
target_info = Net::NTLM::TargetInfo.new(target_info_str)
|
366
|
-
{
|
367
|
-
Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME => :@default_name,
|
368
|
-
Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME => :@default_domain,
|
369
|
-
Net::NTLM::TargetInfo::MSV_AV_DNS_COMPUTER_NAME => :@dns_host_name,
|
370
|
-
Net::NTLM::TargetInfo::MSV_AV_DNS_DOMAIN_NAME => :@dns_domain_name,
|
371
|
-
Net::NTLM::TargetInfo::MSV_AV_DNS_TREE_NAME => :@dns_tree_name
|
372
|
-
}.each do |constant, attribute|
|
373
|
-
if target_info.av_pairs[constant]
|
374
|
-
value = target_info.av_pairs[constant].dup
|
375
|
-
value.force_encoding('UTF-16LE')
|
376
|
-
instance_variable_set(attribute, value.encode('UTF-8'))
|
377
|
-
end
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
|
-
# Extract the peer/server version number from the NTLM Type 2 (challenge)
|
382
|
-
# Version field.
|
383
|
-
#
|
384
|
-
# @param version [String] the version number as a binary string
|
385
|
-
# @return [String] the formated version number (<major>.<minor>.<build>)
|
386
|
-
def extract_os_version(version)
|
387
|
-
version.unpack('CCS').join('.')
|
388
|
-
end
|
389
363
|
end
|
390
364
|
end
|
391
365
|
end
|
@@ -8,8 +8,11 @@ module RubySMB
|
|
8
8
|
require 'net/ntlm'
|
9
9
|
require 'ruby_smb/dcerpc'
|
10
10
|
require 'ruby_smb/gss'
|
11
|
+
require 'ruby_smb/peer_info'
|
11
12
|
|
13
|
+
include Dcerpc
|
12
14
|
include Epm
|
15
|
+
include PeerInfo
|
13
16
|
|
14
17
|
# The default maximum size of a RPC message that the Client accepts (in bytes)
|
15
18
|
MAX_BUFFER_SIZE = 64512
|
@@ -134,11 +137,11 @@ module RubySMB
|
|
134
137
|
end
|
135
138
|
|
136
139
|
# Connect to the RPC endpoint. If a TCP socket was not provided, it takes
|
137
|
-
# care of asking the
|
140
|
+
# care of asking the Endpoint Mapper Interface the port used by the given
|
138
141
|
# endpoint provided in #initialize and connect a TCP socket
|
139
142
|
#
|
140
143
|
# @param port [Integer] An optional port number to connect to. If
|
141
|
-
# provided, it will not ask the
|
144
|
+
# provided, it will not ask the Endpoint Mapper Interface for a port
|
142
145
|
# number.
|
143
146
|
# @return [TcpSocket] The connected TCP socket
|
144
147
|
def connect(port: nil)
|
@@ -172,218 +175,14 @@ module RubySMB
|
|
172
175
|
@tcp_socket.close if @tcp_socket && !@tcp_socket.closed?
|
173
176
|
end
|
174
177
|
|
175
|
-
# Add the authentication verifier to the packet. This includes a sec
|
176
|
-
# trailer and the actual authentication data.
|
177
|
-
#
|
178
|
-
# @param req [BinData::Record] the request to be updated
|
179
|
-
# @param auth [String] the authentication data
|
180
|
-
# @param auth_type [Integer] the authentication type
|
181
|
-
# @param auth_level [Integer] the authentication level
|
182
|
-
def add_auth_verifier(req, auth, auth_type, auth_level)
|
183
|
-
req.sec_trailer = {
|
184
|
-
auth_type: auth_type,
|
185
|
-
auth_level: auth_level,
|
186
|
-
auth_context_id: @ctx_id + @auth_ctx_id_base
|
187
|
-
}
|
188
|
-
req.auth_value = auth
|
189
|
-
req.pdu_header.auth_length = auth.length
|
190
|
-
|
191
|
-
nil
|
192
|
-
end
|
193
|
-
|
194
178
|
def process_ntlm_type2(type2_message)
|
195
|
-
|
196
|
-
type2_blob = type2_message.slice(ntlmssp_offset..-1)
|
197
|
-
type2_b64_message = [type2_blob].pack('m')
|
198
|
-
type3_message = @ntlm_client.init_context(type2_b64_message)
|
199
|
-
auth3 = type3_message.serialize
|
200
|
-
|
201
|
-
@session_key = @ntlm_client.session_key
|
179
|
+
auth3 = super
|
202
180
|
challenge_message = @ntlm_client.session.challenge_message
|
203
181
|
store_target_info(challenge_message.target_info) if challenge_message.has_flag?(:TARGET_INFO)
|
204
182
|
@os_version = extract_os_version(challenge_message.os_version.to_s) unless challenge_message.os_version.empty?
|
205
183
|
auth3
|
206
184
|
end
|
207
185
|
|
208
|
-
# Send a rpc_auth3 PDU that ends the authentication handshake.
|
209
|
-
#
|
210
|
-
# @param response [BindAck] the BindAck response packet
|
211
|
-
# @param auth_type [Integer] the authentication type
|
212
|
-
# @param auth_level [Integer] the authentication level
|
213
|
-
# @raise [ArgumentError] if `:auth_type` is unknown
|
214
|
-
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
215
|
-
def send_auth3(response, auth_type, auth_level)
|
216
|
-
case auth_type
|
217
|
-
when RPC_C_AUTHN_NONE
|
218
|
-
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
219
|
-
auth3 = process_ntlm_type2(response.auth_value)
|
220
|
-
when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
|
221
|
-
# TODO
|
222
|
-
raise NotImplementedError
|
223
|
-
else
|
224
|
-
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
225
|
-
end
|
226
|
-
|
227
|
-
rpc_auth3 = RpcAuth3.new
|
228
|
-
add_auth_verifier(rpc_auth3, auth3, auth_type, auth_level)
|
229
|
-
rpc_auth3.pdu_header.call_id = @call_id
|
230
|
-
|
231
|
-
# The server should not respond
|
232
|
-
send_packet(rpc_auth3)
|
233
|
-
@call_id += 1
|
234
|
-
|
235
|
-
nil
|
236
|
-
end
|
237
|
-
|
238
|
-
# Bind to the remote server interface endpoint. It takes care of adding
|
239
|
-
# the necessary authentication verifier if `:auth_level` is set to
|
240
|
-
# anything different than RPC_C_AUTHN_LEVEL_NONE
|
241
|
-
#
|
242
|
-
# @param endpoint [Module] the endpoint to bind to. This must be a Dcerpc
|
243
|
-
# class with UUID, VER_MAJOR and VER_MINOR constants defined.
|
244
|
-
# @param auth_level [Integer] the authentication level
|
245
|
-
# @param auth_type [Integer] the authentication type
|
246
|
-
# @return [BindAck] the BindAck response packet
|
247
|
-
# @raise [Error::InvalidPacket] if an invalid packet is received
|
248
|
-
# @raise [Error::BindError] if the response is not a BindAck packet or if the Bind result code is not ACCEPTANCE
|
249
|
-
# @raise [ArgumentError] if `:auth_type` is unknown
|
250
|
-
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
251
|
-
def bind(endpoint: @endpoint, auth_level: RPC_C_AUTHN_LEVEL_NONE, auth_type: nil)
|
252
|
-
bind_req = Bind.new(endpoint: endpoint)
|
253
|
-
bind_req.pdu_header.call_id = @call_id
|
254
|
-
# TODO: evasion: generate random UUIDs for bogus binds
|
255
|
-
|
256
|
-
if auth_level && auth_level != RPC_C_AUTHN_LEVEL_NONE
|
257
|
-
case auth_type
|
258
|
-
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
259
|
-
raise ArgumentError, "NTLM Client not initialized. Username and password must be provided" unless @ntlm_client
|
260
|
-
type1_message = @ntlm_client.init_context
|
261
|
-
auth = type1_message.serialize
|
262
|
-
when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE
|
263
|
-
when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL
|
264
|
-
# TODO
|
265
|
-
raise NotImplementedError
|
266
|
-
else
|
267
|
-
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
268
|
-
end
|
269
|
-
add_auth_verifier(bind_req, auth, auth_type, auth_level)
|
270
|
-
end
|
271
|
-
|
272
|
-
send_packet(bind_req)
|
273
|
-
bindack_response = recv_struct(BindAck)
|
274
|
-
# TODO: see if BindNack response should be handled too
|
275
|
-
|
276
|
-
res_list = bindack_response.p_result_list
|
277
|
-
if res_list.n_results == 0 ||
|
278
|
-
res_list.p_results[0].result != BindAck::ACCEPTANCE
|
279
|
-
raise Error::BindError,
|
280
|
-
"Bind Failed (Result: #{res_list.p_results[0].result}, Reason: #{res_list.p_results[0].reason})"
|
281
|
-
end
|
282
|
-
|
283
|
-
@max_buffer_size = bindack_response.max_xmit_frag
|
284
|
-
@call_id = bindack_response.pdu_header.call_id
|
285
|
-
|
286
|
-
if auth_level && auth_level != RPC_C_AUTHN_LEVEL_NONE
|
287
|
-
# The number of legs needed to build the security context is defined
|
288
|
-
# by the security provider
|
289
|
-
# (see [2.2.1.1.7 Security Providers](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/d4097450-c62f-484b-872f-ddf59a7a0d36))
|
290
|
-
case auth_type
|
291
|
-
when RPC_C_AUTHN_WINNT
|
292
|
-
send_auth3(bindack_response, auth_type, auth_level)
|
293
|
-
when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE
|
294
|
-
# TODO
|
295
|
-
raise NotImplementedError
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
nil
|
300
|
-
end
|
301
|
-
|
302
|
-
# Extract and store useful information about the peer/server from the
|
303
|
-
# NTLM Type 2 (challenge) TargetInfo fields.
|
304
|
-
#
|
305
|
-
# @param target_info_str [String] the Target Info string
|
306
|
-
def store_target_info(target_info_str)
|
307
|
-
target_info = Net::NTLM::TargetInfo.new(target_info_str)
|
308
|
-
{
|
309
|
-
Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME => :@default_name,
|
310
|
-
Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME => :@default_domain,
|
311
|
-
Net::NTLM::TargetInfo::MSV_AV_DNS_COMPUTER_NAME => :@dns_host_name,
|
312
|
-
Net::NTLM::TargetInfo::MSV_AV_DNS_DOMAIN_NAME => :@dns_domain_name,
|
313
|
-
Net::NTLM::TargetInfo::MSV_AV_DNS_TREE_NAME => :@dns_tree_name
|
314
|
-
}.each do |constant, attribute|
|
315
|
-
if target_info.av_pairs[constant]
|
316
|
-
value = target_info.av_pairs[constant].dup
|
317
|
-
value.force_encoding('UTF-16LE')
|
318
|
-
instance_variable_set(attribute, value.encode('UTF-8'))
|
319
|
-
end
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
# Extract the peer/server version number from the NTLM Type 2 (challenge)
|
324
|
-
# Version field.
|
325
|
-
#
|
326
|
-
# @param version [String] the version number as a binary string
|
327
|
-
# @return [String] the formated version number (<major>.<minor>.<build>)
|
328
|
-
def extract_os_version(version)
|
329
|
-
#version.unpack('CCS').join('.')
|
330
|
-
begin
|
331
|
-
os_version = NTLM::OSVersion.read(version)
|
332
|
-
rescue IOError
|
333
|
-
return ''
|
334
|
-
end
|
335
|
-
return "#{os_version.major}.#{os_version.minor}.#{os_version.build}"
|
336
|
-
end
|
337
|
-
|
338
|
-
# Add the authentication verifier to a Request packet. This includes a
|
339
|
-
# sec trailer and the signature of the packet. This also encrypts the
|
340
|
-
# Request stub if privacy is required (`:auth_level` option is
|
341
|
-
# RPC_C_AUTHN_LEVEL_PKT_PRIVACY).
|
342
|
-
#
|
343
|
-
# @param dcerpc_req [Request] the Request packet to be updated
|
344
|
-
# @param opts [Hash] the authenticaiton options: `:auth_type` and `:auth_level`
|
345
|
-
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
346
|
-
# @raise [ArgumentError] if `:auth_type` is unknown
|
347
|
-
def set_integrity_privacy(dcerpc_req, auth_level:, auth_type:)
|
348
|
-
dcerpc_req.sec_trailer = {
|
349
|
-
auth_type: auth_type,
|
350
|
-
auth_level: auth_level,
|
351
|
-
auth_context_id: @ctx_id + @auth_ctx_id_base
|
352
|
-
}
|
353
|
-
dcerpc_req.auth_value = ' ' * 16
|
354
|
-
dcerpc_req.pdu_header.auth_length = 16
|
355
|
-
|
356
|
-
data_to_sign = plain_stub = dcerpc_req.stub.to_binary_s + dcerpc_req.auth_pad.to_binary_s
|
357
|
-
if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
|
358
|
-
data_to_sign = dcerpc_req.to_binary_s[0..-(dcerpc_req.pdu_header.auth_length + 1)]
|
359
|
-
end
|
360
|
-
|
361
|
-
encrypted_stub = ''
|
362
|
-
if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
363
|
-
case auth_type
|
364
|
-
when RPC_C_AUTHN_NONE
|
365
|
-
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
366
|
-
encrypted_stub = @ntlm_client.session.seal_message(plain_stub)
|
367
|
-
when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
|
368
|
-
# TODO
|
369
|
-
raise NotImplementedError
|
370
|
-
else
|
371
|
-
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
signature = @ntlm_client.session.sign_message(data_to_sign)
|
376
|
-
|
377
|
-
unless encrypted_stub.empty?
|
378
|
-
pad_length = dcerpc_req.sec_trailer.auth_pad_length.to_i
|
379
|
-
dcerpc_req.enable_encrypted_stub
|
380
|
-
dcerpc_req.stub = encrypted_stub[0..-(pad_length + 1)]
|
381
|
-
dcerpc_req.auth_pad = encrypted_stub[-(pad_length)..-1]
|
382
|
-
end
|
383
|
-
dcerpc_req.auth_value = signature
|
384
|
-
dcerpc_req.pdu_header.auth_length = signature.size
|
385
|
-
end
|
386
|
-
|
387
186
|
# Send a DCERPC request with the provided stub packet.
|
388
187
|
#
|
389
188
|
# @param stub_packet [BinData::Record] the stub packet to be sent as
|
@@ -482,60 +281,6 @@ module RubySMB
|
|
482
281
|
raise Error::CommunicationError, "An error occurred reading from the Socket: #{e.message}"
|
483
282
|
end
|
484
283
|
|
485
|
-
# Process the security context received in a response. It decrypts the
|
486
|
-
# encrypted stub if `:auth_level` is set to anything different than
|
487
|
-
# RPC_C_AUTHN_LEVEL_PKT_PRIVACY. It also checks the packet signature and
|
488
|
-
# raises an InvalidPacket error if it fails. Note that the exception is
|
489
|
-
# disabled by default and can be enabled with the
|
490
|
-
# `:raise_signature_error` option
|
491
|
-
#
|
492
|
-
# @param dcerpc_response [Response] the Response packet
|
493
|
-
# containing the security context to process
|
494
|
-
# @param opts [Hash] the authenticaiton options: `:auth_type` and
|
495
|
-
# `:auth_level`. To enable errors when signature check fails, set the
|
496
|
-
# `:raise_signature_error` option to true
|
497
|
-
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
498
|
-
# @raise [Error::CommunicationError] if socket-related error occurs
|
499
|
-
def handle_integrity_privacy(dcerpc_response, auth_level:, auth_type:, raise_signature_error: false)
|
500
|
-
decrypted_stub = ''
|
501
|
-
if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
502
|
-
encrypted_stub = dcerpc_response.stub.to_binary_s + dcerpc_response.auth_pad.to_binary_s
|
503
|
-
case auth_type
|
504
|
-
when RPC_C_AUTHN_NONE
|
505
|
-
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
506
|
-
decrypted_stub = @ntlm_client.session.unseal_message(encrypted_stub)
|
507
|
-
when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
|
508
|
-
# TODO
|
509
|
-
raise NotImplementedError
|
510
|
-
else
|
511
|
-
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
512
|
-
end
|
513
|
-
end
|
514
|
-
|
515
|
-
unless decrypted_stub.empty?
|
516
|
-
pad_length = dcerpc_response.sec_trailer.auth_pad_length.to_i
|
517
|
-
dcerpc_response.stub = decrypted_stub[0..-(pad_length + 1)]
|
518
|
-
dcerpc_response.auth_pad = decrypted_stub[-(pad_length)..-1]
|
519
|
-
end
|
520
|
-
|
521
|
-
signature = dcerpc_response.auth_value
|
522
|
-
data_to_check = dcerpc_response.stub.to_binary_s
|
523
|
-
if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
|
524
|
-
data_to_check = dcerpc_response.to_binary_s[0..-(dcerpc_response.pdu_header.auth_length + 1)]
|
525
|
-
end
|
526
|
-
unless @ntlm_client.session.verify_signature(signature, data_to_check)
|
527
|
-
if raise_signature_error
|
528
|
-
raise Error::InvalidPacket.new(
|
529
|
-
"Wrong packet signature received (set `raise_signature_error` to false to ignore)"
|
530
|
-
)
|
531
|
-
end
|
532
|
-
end
|
533
|
-
|
534
|
-
@call_id += 1
|
535
|
-
|
536
|
-
nil
|
537
|
-
end
|
538
|
-
|
539
284
|
end
|
540
285
|
end
|
541
286
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Dfsnm
|
4
|
+
|
5
|
+
# [3.1.4.4.1 NetrDfsAddStdRoot (Opnum 12)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dfsnm/b18ef17a-7a9c-4e22-b1bf-6a4d07e87b2d)
|
6
|
+
class NetrDfsAddStdRootRequest < BinData::Record
|
7
|
+
attr_reader :opnum
|
8
|
+
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
ndr_conf_var_wide_stringz :server_name
|
12
|
+
ndr_conf_var_wide_stringz :root_share
|
13
|
+
ndr_conf_var_wide_stringz :comment
|
14
|
+
ndr_uint32 :api_flags
|
15
|
+
|
16
|
+
def initialize_instance
|
17
|
+
super
|
18
|
+
@opnum = NETR_DFS_ADD_STD_ROOT
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Dfsnm
|
4
|
+
|
5
|
+
# [3.1.4.4.1 NetrDfsAddStdRoot (Opnum 12)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dfsnm/b18ef17a-7a9c-4e22-b1bf-6a4d07e87b2d)
|
6
|
+
class NetrDfsAddStdRootResponse < BinData::Record
|
7
|
+
attr_reader :opnum
|
8
|
+
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
ndr_uint32 :error_status
|
12
|
+
|
13
|
+
def initialize_instance
|
14
|
+
super
|
15
|
+
@opnum = NETR_DFS_ADD_STD_ROOT
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Dfsnm
|
4
|
+
|
5
|
+
# [3.1.4.4.2 NetrDfsRemoveStdRoot (Opnum 13)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dfsnm/e9da023d-554a-49bc-837a-69f22d59fd18)
|
6
|
+
class NetrDfsRemoveStdRootRequest < BinData::Record
|
7
|
+
attr_reader :opnum
|
8
|
+
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
ndr_conf_var_wide_stringz :server_name
|
12
|
+
ndr_conf_var_wide_stringz :root_share
|
13
|
+
ndr_uint32 :api_flags
|
14
|
+
|
15
|
+
def initialize_instance
|
16
|
+
super
|
17
|
+
@opnum = NETR_DFS_REMOVE_STD_ROOT
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Dfsnm
|
4
|
+
|
5
|
+
# [3.1.4.4.2 NetrDfsRemoveStdRoot (Opnum 13)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dfsnm/e9da023d-554a-49bc-837a-69f22d59fd18)
|
6
|
+
class NetrDfsRemoveStdRootResponse < BinData::Record
|
7
|
+
attr_reader :opnum
|
8
|
+
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
ndr_uint32 :error_status
|
12
|
+
|
13
|
+
def initialize_instance
|
14
|
+
super
|
15
|
+
@opnum = NETR_DFS_REMOVE_STD_ROOT
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Dfsnm
|
4
|
+
|
5
|
+
UUID = '4fc742e0-4a10-11cf-8273-00aa004ae673'
|
6
|
+
VER_MAJOR = 3
|
7
|
+
VER_MINOR = 0
|
8
|
+
|
9
|
+
# Operation numbers
|
10
|
+
NETR_DFS_ADD_STD_ROOT = 0x000c
|
11
|
+
NETR_DFS_REMOVE_STD_ROOT = 0x000d
|
12
|
+
|
13
|
+
require 'ruby_smb/dcerpc/dfsnm/netr_dfs_add_std_root_request'
|
14
|
+
require 'ruby_smb/dcerpc/dfsnm/netr_dfs_add_std_root_response'
|
15
|
+
require 'ruby_smb/dcerpc/dfsnm/netr_dfs_remove_std_root_request'
|
16
|
+
require 'ruby_smb/dcerpc/dfsnm/netr_dfs_remove_std_root_response'
|
17
|
+
|
18
|
+
# Create a new stand-alone DFS namespace.
|
19
|
+
#
|
20
|
+
# @param server_name [String] The host name of the DFS root target.
|
21
|
+
# @param root_share [String] The DFS root target share name.
|
22
|
+
# @param comment [String] A comment associated with the DFS namespace.
|
23
|
+
# @return nothing is returned on success
|
24
|
+
# @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a
|
25
|
+
# NetrDfsAddStdRootResponse packet
|
26
|
+
# @raise [RubySMB::Dcerpc::Error::DfsnmError] if the response error status
|
27
|
+
# is not ERROR_SUCCESS
|
28
|
+
def netr_dfs_add_std_root(server_name, root_share, comment: '')
|
29
|
+
netr_dfs_add_std_root_request = NetrDfsAddStdRootRequest.new(
|
30
|
+
server_name: server_name,
|
31
|
+
root_share: root_share,
|
32
|
+
comment: comment
|
33
|
+
)
|
34
|
+
response = dcerpc_request(netr_dfs_add_std_root_request)
|
35
|
+
begin
|
36
|
+
netr_dfs_add_std_root_response = NetrDfsAddStdRootResponse.read(response)
|
37
|
+
rescue IOError
|
38
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading NetrDfsAddStdRootResponse'
|
39
|
+
end
|
40
|
+
unless netr_dfs_add_std_root_response.error_status == WindowsError::Win32::ERROR_SUCCESS
|
41
|
+
status_code = WindowsError::Win32.find_by_retval(netr_dfs_add_std_root_response.error_status.value).first
|
42
|
+
raise RubySMB::Dcerpc::Error::DfsnmError.new(
|
43
|
+
"Error returned with netr_dfs_add_std_root: #{status_code}",
|
44
|
+
status_code: status_code
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
# Delete the specified stand-alone DFS namespace.
|
52
|
+
#
|
53
|
+
# @param server_name [String] The host name of the DFS root target.
|
54
|
+
# @param root_share [String] The DFS root target share name.
|
55
|
+
# @return nothing is returned on success
|
56
|
+
# @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a
|
57
|
+
# NetrDfsRemoveStdRootResponse packet
|
58
|
+
# @raise [RubySMB::Dcerpc::Error::DfsnmError] if the response error status
|
59
|
+
# is not ERROR_SUCCESS
|
60
|
+
def netr_dfs_remove_std_root(server_name, root_share)
|
61
|
+
netr_dfs_remove_std_root_request = NetrDfsRemoveStdRootRequest.new(
|
62
|
+
server_name: server_name,
|
63
|
+
root_share: root_share
|
64
|
+
)
|
65
|
+
response = dcerpc_request(netr_dfs_remove_std_root_request)
|
66
|
+
begin
|
67
|
+
netr_dfs_remove_std_root_response = NetrDfsRemoveStdRootResponse.read(response)
|
68
|
+
rescue IOError
|
69
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading NetrDfsRemoveStdRootResponse'
|
70
|
+
end
|
71
|
+
unless netr_dfs_remove_std_root_response.error_status == WindowsError::Win32::ERROR_SUCCESS
|
72
|
+
status_code = WindowsError::Win32.find_by_retval(netr_dfs_remove_std_root_response.error_status.value).first
|
73
|
+
raise RubySMB::Dcerpc::Error::DfsnmError.new(
|
74
|
+
"Error returned with netr_dfs_remove_std_root: #{status_code}",
|
75
|
+
status_code: status_code
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -46,6 +46,27 @@ module RubySMB
|
|
46
46
|
|
47
47
|
# Raised when an error is returned during a Epm operation
|
48
48
|
class EpmError < DcerpcError; end
|
49
|
+
|
50
|
+
# Raised when an error is returned during a Dfsnm operation
|
51
|
+
class DfsnmError < DcerpcError
|
52
|
+
include RubySMB::Error::UnexpectedStatusCode::Mixin
|
53
|
+
|
54
|
+
def initialize(msg, status_code: nil)
|
55
|
+
self.status_code = status_code unless status_code.nil?
|
56
|
+
|
57
|
+
super(msg)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class IcprError < DcerpcError
|
62
|
+
include RubySMB::Error::UnexpectedStatusCode::Mixin
|
63
|
+
|
64
|
+
def initialize(msg, status_code: nil)
|
65
|
+
self.status_code = status_code unless status_code.nil?
|
66
|
+
|
67
|
+
super(msg)
|
68
|
+
end
|
69
|
+
end
|
49
70
|
end
|
50
71
|
end
|
51
72
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'ruby_smb/dcerpc/ndr'
|
2
|
+
|
3
|
+
module RubySMB
|
4
|
+
module Dcerpc
|
5
|
+
module Icpr
|
6
|
+
|
7
|
+
# [3.2.4.1.1 CertServerRequest (Opnum 0)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-icpr/0c6f150e-3ead-4006-b37f-ebbf9e2cf2e7)
|
8
|
+
class CertServerRequestRequest < BinData::Record
|
9
|
+
attr_reader :opnum
|
10
|
+
|
11
|
+
endian :little
|
12
|
+
|
13
|
+
ndr_uint32 :dw_flags
|
14
|
+
ndr_wide_stringz_ptr :pwsz_authority
|
15
|
+
ndr_uint32 :pdw_request_id
|
16
|
+
cert_trans_blob :pctb_attribs
|
17
|
+
cert_trans_blob :pctb_request
|
18
|
+
|
19
|
+
def initialize_instance
|
20
|
+
super
|
21
|
+
@opnum = CERT_SERVER_REQUEST
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|