ruby_smb 3.2.0 → 3.2.2
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 +8 -9
- data/README.md +1 -0
- data/lib/ruby_smb/client.rb +6 -4
- data/lib/ruby_smb/dcerpc/client.rb +6 -4
- data/lib/ruby_smb/dcerpc/icpr.rb +3 -2
- data/lib/ruby_smb/dcerpc/ndr.rb +30 -3
- data/lib/ruby_smb/dcerpc/samr.rb +1 -1
- data/lib/ruby_smb/dispatcher/socket.rb +1 -1
- data/lib/ruby_smb/field/file_time.rb +2 -2
- data/lib/ruby_smb/field/utime.rb +2 -2
- data/lib/ruby_smb/generic_packet.rb +2 -2
- data/lib/ruby_smb/gss/provider/ntlm.rb +12 -5
- data/lib/ruby_smb/ntlm/client.rb +4 -0
- data/lib/ruby_smb/ntlm/custom/ntlm.rb +19 -0
- data/lib/ruby_smb/ntlm.rb +37 -0
- data/lib/ruby_smb/server/server_client/tree_connect.rb +2 -2
- data/lib/ruby_smb/smb1/data_block.rb +2 -2
- data/lib/ruby_smb/smb1/parameter_block.rb +2 -2
- data/lib/ruby_smb/smb1/pipe.rb +4 -0
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +5 -5
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +1 -1
- data/lib/ruby_smb/smb2/pipe.rb +4 -0
- data/lib/ruby_smb/utils.rb +15 -0
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +2 -0
- data/spec/lib/ruby_smb/client_spec.rb +4 -4
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/read_andx_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/write_andx_request_spec.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +5 -4
- 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: 20c59ae356835caa5149193af377ecb8aaf5f1e635f34d8f660d9da1df6a94d5
|
4
|
+
data.tar.gz: dbb749afb04c0a9909af9667e0b602ebf43cf00770a7a1d2a4df0e0a520db897
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d826659151d98a13517cb5026dde99852ec2feaaeb6af17559782c2147026ae8cebc5ba0c19a576fb4e87e1c0950e33a3f9c9026fe8752d6503fe263796fdaf
|
7
|
+
data.tar.gz: 057f902a9540bdf4b312ec3000441fcdba9a930021a15ca12935c2b486647edae85475991c0837876e1f3b365d9c791aeecb11533970f9a63e4d9df6fe13d48a
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -33,17 +33,16 @@ jobs:
|
|
33
33
|
fail-fast: true
|
34
34
|
matrix:
|
35
35
|
ruby:
|
36
|
-
- 2.
|
37
|
-
-
|
38
|
-
- 3.
|
39
|
-
- 3.
|
36
|
+
- '2.7'
|
37
|
+
- '3.0'
|
38
|
+
- '3.1'
|
39
|
+
- '3.2'
|
40
40
|
os:
|
41
|
-
- ubuntu-
|
42
|
-
- ubuntu-
|
41
|
+
- ubuntu-20.04
|
42
|
+
- ubuntu-latest
|
43
43
|
exclude:
|
44
|
-
- { os: ubuntu-
|
45
|
-
- { os: ubuntu-
|
46
|
-
- { os: ubuntu-22.04, ruby: 3.0 }
|
44
|
+
- { os: ubuntu-latest, ruby: '2.7' }
|
45
|
+
- { os: ubuntu-latest, ruby: '3.0' }
|
47
46
|
test_cmd:
|
48
47
|
- bundle exec rspec
|
49
48
|
|
data/README.md
CHANGED
@@ -184,6 +184,7 @@ Example:
|
|
184
184
|
2.3.3 :013 > packet.to_binary_s
|
185
185
|
=> "\xFFSMB+\x00\x00\x00\x00\x98\x01`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
|
186
186
|
```
|
187
|
+
|
187
188
|
### Using the Client
|
188
189
|
|
189
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 convenience methods for performing SMB actions. It handles the creation, sending and receiving of packets for the user, providing reasonable defaults in many cases.
|
data/lib/ruby_smb/client.rb
CHANGED
@@ -4,6 +4,7 @@ module RubySMB
|
|
4
4
|
class Client
|
5
5
|
require 'ruby_smb/ntlm'
|
6
6
|
require 'ruby_smb/signing'
|
7
|
+
require 'ruby_smb/utils'
|
7
8
|
require 'ruby_smb/client/negotiation'
|
8
9
|
require 'ruby_smb/client/authentication'
|
9
10
|
require 'ruby_smb/client/tree_connect'
|
@@ -13,6 +14,7 @@ module RubySMB
|
|
13
14
|
require 'ruby_smb/client/encryption'
|
14
15
|
|
15
16
|
include RubySMB::Signing
|
17
|
+
include RubySMB::NTLM
|
16
18
|
include RubySMB::Client::Negotiation
|
17
19
|
include RubySMB::Client::Authentication
|
18
20
|
include RubySMB::Client::TreeConnect
|
@@ -322,7 +324,7 @@ module RubySMB
|
|
322
324
|
@pid = rand(0xFFFF)
|
323
325
|
@domain = domain
|
324
326
|
@local_workstation = local_workstation
|
325
|
-
@password =
|
327
|
+
@password = RubySMB::Utils.safe_encode((password||''), 'utf-8')
|
326
328
|
@sequence_counter = 0
|
327
329
|
@session_id = 0x00
|
328
330
|
@session_key = ''
|
@@ -332,7 +334,7 @@ module RubySMB
|
|
332
334
|
@smb1 = smb1
|
333
335
|
@smb2 = smb2
|
334
336
|
@smb3 = smb3
|
335
|
-
@username =
|
337
|
+
@username = RubySMB::Utils.safe_encode((username||''), 'utf-8')
|
336
338
|
@max_buffer_size = MAX_BUFFER_SIZE
|
337
339
|
# These sizes will be modified during negotiation
|
338
340
|
@server_max_buffer_size = SERVER_MAX_BUFFER_SIZE
|
@@ -415,8 +417,8 @@ module RubySMB
|
|
415
417
|
local_workstation: self.local_workstation, ntlm_flags: NTLM::DEFAULT_CLIENT_FLAGS)
|
416
418
|
@domain = domain
|
417
419
|
@local_workstation = local_workstation
|
418
|
-
@password =
|
419
|
-
@username =
|
420
|
+
@password = RubySMB::Utils.safe_encode((pass||''), 'utf-8')
|
421
|
+
@username = RubySMB::Utils.safe_encode((user||''), 'utf-8')
|
420
422
|
|
421
423
|
@ntlm_client = RubySMB::NTLM::Client.new(
|
422
424
|
@username,
|
@@ -5,14 +5,16 @@ module RubySMB
|
|
5
5
|
class Client
|
6
6
|
require 'bindata'
|
7
7
|
require 'windows_error'
|
8
|
-
require '
|
8
|
+
require 'ruby_smb/ntlm'
|
9
9
|
require 'ruby_smb/dcerpc'
|
10
10
|
require 'ruby_smb/gss'
|
11
11
|
require 'ruby_smb/peer_info'
|
12
|
+
require 'ruby_smb/utils'
|
12
13
|
|
13
14
|
include Dcerpc
|
14
15
|
include Epm
|
15
16
|
include PeerInfo
|
17
|
+
include Utils
|
16
18
|
|
17
19
|
# The default maximum size of a RPC message that the Client accepts (in bytes)
|
18
20
|
MAX_BUFFER_SIZE = 64512
|
@@ -118,15 +120,15 @@ module RubySMB
|
|
118
120
|
@read_timeout = read_timeout
|
119
121
|
@domain = domain
|
120
122
|
@local_workstation = local_workstation
|
121
|
-
@username =
|
122
|
-
@password =
|
123
|
+
@username = RubySMB::Utils.safe_encode(username, 'utf-8')
|
124
|
+
@password = RubySMB::Utils.safe_encode(password, 'utf-8')
|
123
125
|
@max_buffer_size = MAX_BUFFER_SIZE
|
124
126
|
@call_id = 1
|
125
127
|
@ctx_id = 0
|
126
128
|
@auth_ctx_id_base = rand(0xFFFFFFFF)
|
127
129
|
|
128
130
|
unless username.empty? && password.empty?
|
129
|
-
@ntlm_client =
|
131
|
+
@ntlm_client = RubySMB::NTLM::Client.new(
|
130
132
|
@username,
|
131
133
|
@password,
|
132
134
|
workstation: @local_workstation,
|
data/lib/ruby_smb/dcerpc/icpr.rb
CHANGED
@@ -35,7 +35,7 @@ module RubySMB
|
|
35
35
|
def cert_server_request(attributes:, authority:, csr:)
|
36
36
|
cert_server_request_request = CertServerRequestRequest.new(
|
37
37
|
pwsz_authority: authority,
|
38
|
-
pctb_attribs: { pb: (attributes.map { |k,v| "#{k}:#{v}" }.join("\n")
|
38
|
+
pctb_attribs: { pb: (RubySMB::Utils.safe_encode(attributes.map { |k,v| "#{k}:#{v}" }.join("\n"), 'UTF-16le').force_encoding('ASCII-8bit') + "\x00\x00".b) },
|
39
39
|
pctb_request: { pb: csr.to_der }
|
40
40
|
)
|
41
41
|
|
@@ -51,6 +51,7 @@ module RubySMB
|
|
51
51
|
end
|
52
52
|
|
53
53
|
ret = {
|
54
|
+
certificate: nil,
|
54
55
|
disposition: cert_server_request_response.pdw_disposition.value,
|
55
56
|
disposition_message: cert_server_request_response.pctb_disposition_message.buffer.chomp("\x00\x00").force_encoding('utf-16le').encode,
|
56
57
|
status: {
|
@@ -72,7 +73,7 @@ module RubySMB
|
|
72
73
|
status_code: status_code
|
73
74
|
)
|
74
75
|
end
|
75
|
-
|
76
|
+
elsif !cert_server_request_response.pctb_encoded_cert.buffer.empty?
|
76
77
|
ret[:certificate] = OpenSSL::X509::Certificate.new(cert_server_request_response.pctb_encoded_cert.buffer)
|
77
78
|
end
|
78
79
|
|
data/lib/ruby_smb/dcerpc/ndr.rb
CHANGED
@@ -247,12 +247,22 @@ module RubySMB::Dcerpc::Ndr
|
|
247
247
|
|
248
248
|
def do_read(io)
|
249
249
|
if is_a?(ConfPlugin) && should_process_max_count?
|
250
|
-
|
250
|
+
max_count = io.readbytes(4).unpack1('L<')
|
251
|
+
BinData.trace_message do |tracer|
|
252
|
+
tracer.trace_obj("#{debug_name}.max_count", max_count.to_s)
|
253
|
+
end
|
254
|
+
set_max_count(max_count)
|
251
255
|
end
|
252
256
|
|
253
257
|
if is_a?(VarPlugin)
|
254
258
|
@offset = io.readbytes(4).unpack('L<').first
|
259
|
+
BinData.trace_message do |tracer|
|
260
|
+
tracer.trace_obj("#{debug_name}.offset", @offset.to_s)
|
261
|
+
end
|
255
262
|
@actual_count = @read_until_index = io.readbytes(4).unpack('L<').first
|
263
|
+
BinData.trace_message do |tracer|
|
264
|
+
tracer.trace_obj("#{debug_name}.actual_count", @actual_count.to_s)
|
265
|
+
end
|
256
266
|
end
|
257
267
|
|
258
268
|
if has_elements_to_read?
|
@@ -523,7 +533,11 @@ module RubySMB::Dcerpc::Ndr
|
|
523
533
|
|
524
534
|
def do_read(io)
|
525
535
|
if should_process_max_count?
|
526
|
-
|
536
|
+
max_count = io.readbytes(4).unpack1('L<')
|
537
|
+
BinData.trace_message do |tracer|
|
538
|
+
tracer.trace_obj("#{debug_name}.max_count", max_count.to_s)
|
539
|
+
end
|
540
|
+
set_max_count(max_count)
|
527
541
|
end
|
528
542
|
super
|
529
543
|
end
|
@@ -582,7 +596,13 @@ module RubySMB::Dcerpc::Ndr
|
|
582
596
|
|
583
597
|
def do_read(io)
|
584
598
|
@offset = io.readbytes(4).unpack('L<').first
|
599
|
+
BinData.trace_message do |tracer|
|
600
|
+
tracer.trace_obj("#{debug_name}.offset", @offset.to_s)
|
601
|
+
end
|
585
602
|
@actual_count = io.readbytes(4).unpack('L<').first
|
603
|
+
BinData.trace_message do |tracer|
|
604
|
+
tracer.trace_obj("#{debug_name}.actual_count", @actual_count.to_s)
|
605
|
+
end
|
586
606
|
super if @actual_count > 0
|
587
607
|
end
|
588
608
|
|
@@ -702,7 +722,11 @@ module RubySMB::Dcerpc::Ndr
|
|
702
722
|
|
703
723
|
def do_read(io)
|
704
724
|
if should_process_max_count?
|
705
|
-
|
725
|
+
max_count = io.readbytes(4).unpack1('L<')
|
726
|
+
BinData.trace_message do |tracer|
|
727
|
+
tracer.trace_obj("#{debug_name}.max_count", max_count.to_s)
|
728
|
+
end
|
729
|
+
set_max_count(max_count)
|
706
730
|
|
707
731
|
# Align the structure according to the alignment rules for the structure
|
708
732
|
if respond_to?(:referent_bytes_align)
|
@@ -1034,6 +1058,9 @@ module RubySMB::Dcerpc::Ndr
|
|
1034
1058
|
end
|
1035
1059
|
else
|
1036
1060
|
@ref_id = io.readbytes(4).unpack('L<').first
|
1061
|
+
BinData.trace_message do |tracer|
|
1062
|
+
tracer.trace_obj("#{debug_name}.ref_id", @ref_id.to_s)
|
1063
|
+
end
|
1037
1064
|
parent_obj = nil
|
1038
1065
|
if parent&.is_a?(ConstructedTypePlugin)
|
1039
1066
|
parent_obj = parent.get_top_level_constructed_type
|
data/lib/ruby_smb/dcerpc/samr.rb
CHANGED
@@ -320,7 +320,7 @@ module RubySMB
|
|
320
320
|
|
321
321
|
def self.encrypt_password(password, key)
|
322
322
|
# see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/5fe3c4c4-e71b-440d-b2fd-8448bfaf6e04
|
323
|
-
password =
|
323
|
+
password = RubySMB::Utils.safe_encode(password, 'UTF-16LE').force_encoding('ASCII-8bit')
|
324
324
|
buffer = password.rjust(512, "\x00") + [ password.length ].pack('V')
|
325
325
|
salt = SecureRandom.random_bytes(16)
|
326
326
|
key = OpenSSL::Digest::MD5.new(salt + key).digest
|
@@ -24,7 +24,7 @@ module RubySMB
|
|
24
24
|
end
|
25
25
|
|
26
26
|
# @param host [String] passed to TCPSocket.new
|
27
|
-
# @param port [
|
27
|
+
# @param port [Integer] passed to TCPSocket.new
|
28
28
|
def self.connect(host, port: 445, socket: TCPSocket.new(host, port))
|
29
29
|
new(socket)
|
30
30
|
end
|
@@ -18,12 +18,12 @@ module RubySMB
|
|
18
18
|
val
|
19
19
|
end
|
20
20
|
|
21
|
-
# Sets the value of the field from a DateTime,Time,
|
21
|
+
# Sets the value of the field from a DateTime,Time,Integer, or object
|
22
22
|
# that can be converted to an integer. Datetime and Time objects get
|
23
23
|
# converted to account for the Windows/Unix Epoch difference. Any other
|
24
24
|
# parameter passed in will be assumed to already be correct.
|
25
25
|
#
|
26
|
-
# @param value [DateTime,Time,
|
26
|
+
# @param value [DateTime,Time,Integer,#to_i] the value to set
|
27
27
|
# @return
|
28
28
|
def set(value)
|
29
29
|
case value
|
data/lib/ruby_smb/field/utime.rb
CHANGED
@@ -16,11 +16,11 @@ module RubySMB
|
|
16
16
|
val
|
17
17
|
end
|
18
18
|
|
19
|
-
# Sets the value of the field from a DateTime,Time,
|
19
|
+
# Sets the value of the field from a DateTime,Time,Integer, or object
|
20
20
|
# that can be converted to an integer. Any other
|
21
21
|
# parameter passed in will be assumed to already be correct.
|
22
22
|
#
|
23
|
-
# @param value [DateTime,Time,
|
23
|
+
# @param value [DateTime,Time,Integer,#to_i] the value to set
|
24
24
|
# @return
|
25
25
|
def set(value)
|
26
26
|
case value
|
@@ -129,7 +129,7 @@ module RubySMB
|
|
129
129
|
# string representation.
|
130
130
|
#
|
131
131
|
# @param field [Hash] the hash representing the field
|
132
|
-
# @param depth [
|
132
|
+
# @param depth [Integer] the recursive depth level to track indentation
|
133
133
|
# @return [String] the formatted string representation of the field
|
134
134
|
def self.format_field(field, depth = 0)
|
135
135
|
name = field[:name].to_s
|
@@ -181,7 +181,7 @@ module RubySMB
|
|
181
181
|
# into a string representing the contents of that field.
|
182
182
|
#
|
183
183
|
# @param field [Hash] hash representation of the field to display
|
184
|
-
# @param depth [
|
184
|
+
# @param depth [Integer] the recursion depth for setting indent levels
|
185
185
|
# @param parents [Array<Symbol>] the name of the parent field, if any, of this field
|
186
186
|
# @return [String] a formatted string representing the field and it's current contents
|
187
187
|
def display_field(field, depth = 0, parents = [])
|
@@ -18,6 +18,7 @@ module RubySMB
|
|
18
18
|
end
|
19
19
|
|
20
20
|
class Authenticator < Authenticator::Base
|
21
|
+
|
21
22
|
def reset!
|
22
23
|
super
|
23
24
|
@server_challenge = nil
|
@@ -141,7 +142,10 @@ module RubySMB
|
|
141
142
|
case type3_msg.ntlm_version
|
142
143
|
when :ntlmv1
|
143
144
|
my_ntlm_response = Net::NTLM::ntlm_response(
|
144
|
-
ntlm_hash: Net::NTLM::ntlm_hash(
|
145
|
+
ntlm_hash: Net::NTLM::ntlm_hash(
|
146
|
+
RubySMB::Utils.safe_encode(account.password, 'UTF-16LE'),
|
147
|
+
unicode: true
|
148
|
+
),
|
145
149
|
challenge: @server_challenge
|
146
150
|
)
|
147
151
|
matches = my_ntlm_response == type3_msg.ntlm_response
|
@@ -151,9 +155,9 @@ module RubySMB
|
|
151
155
|
their_blob = type3_msg.ntlm_response[digest.digest_length..-1]
|
152
156
|
|
153
157
|
ntlmv2_hash = Net::NTLM.ntlmv2_hash(
|
154
|
-
account.username
|
155
|
-
account.password
|
156
|
-
type3_msg.domain
|
158
|
+
RubySMB::Utils.safe_encode(account.username, 'UTF-16LE'),
|
159
|
+
RubySMB::Utils.safe_encode(account.password, 'UTF-16LE'),
|
160
|
+
RubySMB::Utils.safe_encode(type3_msg.domain, 'UTF-16LE'), # don't use the account domain because of the special '.' value
|
157
161
|
{client_challenge: their_blob[16...24], unicode: true}
|
158
162
|
)
|
159
163
|
|
@@ -305,7 +309,10 @@ module RubySMB
|
|
305
309
|
username = username.downcase
|
306
310
|
domain = @default_domain if domain.nil? || domain == '.'.encode(domain.encoding)
|
307
311
|
domain = domain.downcase
|
308
|
-
@accounts.find
|
312
|
+
@accounts.find do |account|
|
313
|
+
RubySMB::Utils.safe_encode(account.username, username.encoding).downcase == username &&
|
314
|
+
RubySMB::Utils.safe_encode(account.domain, domain.encoding).downcase == domain
|
315
|
+
end
|
309
316
|
end
|
310
317
|
|
311
318
|
#
|
data/lib/ruby_smb/ntlm/client.rb
CHANGED
@@ -51,6 +51,10 @@ module RubySMB::NTLM
|
|
51
51
|
!challenge_message.has_flag?(:UNICODE) && challenge_message.has_flag?(:OEM)
|
52
52
|
end
|
53
53
|
|
54
|
+
def ntlmv2_hash
|
55
|
+
@ntlmv2_hash ||= RubySMB::NTLM.ntlmv2_hash(username, password, domain, {:client_challenge => client_challenge, :unicode => !use_oem_strings?})
|
56
|
+
end
|
57
|
+
|
54
58
|
def calculate_user_session_key!
|
55
59
|
if is_anonymous?
|
56
60
|
# see MS-NLMP section 3.4
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'net/ntlm'
|
2
|
+
|
3
|
+
module Custom
|
4
|
+
module NTLM
|
5
|
+
|
6
|
+
def self.prepended(base)
|
7
|
+
base.singleton_class.send(:prepend, ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def encode_utf16le(str)
|
12
|
+
str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('ASCII-8BIT')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Net::NTLM::EncodeUtil.send(:prepend, Custom::NTLM)
|
data/lib/ruby_smb/ntlm.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ruby_smb/ntlm/custom/ntlm'
|
2
|
+
|
1
3
|
module RubySMB
|
2
4
|
module NTLM
|
3
5
|
# [[MS-NLMP] 2.2.2.5](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832)
|
@@ -56,6 +58,41 @@ module RubySMB
|
|
56
58
|
"Version #{major}.#{minor} (Build #{build}); NTLM Current Revision #{ntlm_revision}"
|
57
59
|
end
|
58
60
|
end
|
61
|
+
|
62
|
+
class << self
|
63
|
+
|
64
|
+
# Generate a NTLMv2 Hash
|
65
|
+
# @param [String] user The username
|
66
|
+
# @param [String] password The password
|
67
|
+
# @param [String] target The domain or workstation to authenticate to
|
68
|
+
# @option opt :unicode (false) Unicode encode the domain
|
69
|
+
def ntlmv2_hash(user, password, target, opt={})
|
70
|
+
if Net::NTLM.is_ntlm_hash? password
|
71
|
+
decoded_password = Net::NTLM::EncodeUtil.decode_utf16le(password)
|
72
|
+
ntlmhash = [decoded_password.upcase[33,65]].pack('H32')
|
73
|
+
else
|
74
|
+
ntlmhash = Net::NTLM.ntlm_hash(password, opt)
|
75
|
+
end
|
76
|
+
|
77
|
+
if opt[:unicode]
|
78
|
+
# Uppercase operation on username containing non-ASCII characters
|
79
|
+
# after being unicode encoded with `EncodeUtil.encode_utf16le`
|
80
|
+
# doesn't play well. Upcase should be done before encoding.
|
81
|
+
user_upcase = Net::NTLM::EncodeUtil.decode_utf16le(user).upcase
|
82
|
+
user_upcase = Net::NTLM::EncodeUtil.encode_utf16le(user_upcase)
|
83
|
+
else
|
84
|
+
user_upcase = user.upcase
|
85
|
+
end
|
86
|
+
userdomain = user_upcase + target
|
87
|
+
|
88
|
+
unless opt[:unicode]
|
89
|
+
userdomain = Net::NTLM::EncodeUtil.encode_utf16le(userdomain)
|
90
|
+
end
|
91
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
59
96
|
end
|
60
97
|
end
|
61
98
|
|
@@ -7,7 +7,7 @@ module RubySMB
|
|
7
7
|
# see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/b062f3e3-1b65-4a9a-854a-0ee432499d8f
|
8
8
|
response = RubySMB::SMB1::Packet::TreeConnectResponse.new
|
9
9
|
|
10
|
-
share_name = request.data_block.path
|
10
|
+
share_name = RubySMB::Utils.safe_encode(request.data_block.path, 'UTF-8').split('\\', 4).last
|
11
11
|
share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
|
12
12
|
if share_provider.nil?
|
13
13
|
logger.warn("Received TREE_CONNECT request for non-existent share: #{share_name}")
|
@@ -49,7 +49,7 @@ module RubySMB
|
|
49
49
|
return response
|
50
50
|
end
|
51
51
|
|
52
|
-
share_name = request.path
|
52
|
+
share_name = RubySMB::Utils.safe_encode(request.path, 'UTF-8').split('\\', 4).last
|
53
53
|
share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
|
54
54
|
|
55
55
|
if share_provider.nil?
|
@@ -11,7 +11,7 @@ module RubySMB
|
|
11
11
|
# Class method to stub byte count calculation during
|
12
12
|
# lazy evaluation.
|
13
13
|
#
|
14
|
-
# @return [
|
14
|
+
# @return [Integer] will always return 0
|
15
15
|
def self.calculate_byte_count
|
16
16
|
0
|
17
17
|
end
|
@@ -28,7 +28,7 @@ module RubySMB
|
|
28
28
|
# Calculates the size of the other fields in the DataBlock
|
29
29
|
# in Bytes.
|
30
30
|
#
|
31
|
-
# @return [
|
31
|
+
# @return [Integer] The size of the DataBlock in Words
|
32
32
|
def calculate_byte_count
|
33
33
|
total_count = 0
|
34
34
|
self.class.data_fields.each do |field_name|
|
@@ -11,7 +11,7 @@ module RubySMB
|
|
11
11
|
# Class method to stub word count calculation during
|
12
12
|
# lazy evaluation.
|
13
13
|
#
|
14
|
-
# @param [
|
14
|
+
# @param [Integer] will always return 0
|
15
15
|
def self.calculate_word_count
|
16
16
|
0
|
17
17
|
end
|
@@ -28,7 +28,7 @@ module RubySMB
|
|
28
28
|
# Calculates the size of the other fields in the ParameterBlock
|
29
29
|
# in Words.
|
30
30
|
#
|
31
|
-
# @return [
|
31
|
+
# @return [Integer] The size of the ParameterBlock in Words
|
32
32
|
def calculate_word_count
|
33
33
|
total_count = 0
|
34
34
|
self.class.parameter_fields.each do |field_name|
|
data/lib/ruby_smb/smb1/pipe.rb
CHANGED
@@ -147,6 +147,10 @@ module RubySMB
|
|
147
147
|
break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
|
148
148
|
raw_data = read(bytes: @tree.client.max_buffer_size)
|
149
149
|
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
150
|
+
if options[:auth_level] &&
|
151
|
+
[RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(options[:auth_level])
|
152
|
+
handle_integrity_privacy(dcerpc_response, auth_level: options[:auth_level], auth_type: options[:auth_type])
|
153
|
+
end
|
150
154
|
stub_data << dcerpc_response.stub.to_s
|
151
155
|
end
|
152
156
|
stub_data
|
@@ -28,8 +28,8 @@ module RubySMB
|
|
28
28
|
|
29
29
|
# Adds a dialect to the Dialects array
|
30
30
|
#
|
31
|
-
# @param [
|
32
|
-
# @return [Array<
|
31
|
+
# @param [Integer] the numeric code for the dialect you wish to add
|
32
|
+
# @return [Array<Integer>] the array of all currently selected dialects
|
33
33
|
# @raise [ArgumentError] if the dialect is not an Integer
|
34
34
|
def add_dialect(dialect)
|
35
35
|
raise ArgumentError, 'Must be a number' unless dialect.is_a? Integer
|
@@ -40,8 +40,8 @@ module RubySMB
|
|
40
40
|
# the dialect_count field appropriately. Will erase any previously set
|
41
41
|
# dialects.
|
42
42
|
#
|
43
|
-
# @param [Array<
|
44
|
-
# @return [Array<
|
43
|
+
# @param [Array<Integer>] the array of dialects to set
|
44
|
+
# @return [Array<Integer>] the current value of the dialects array
|
45
45
|
def set_dialects(add_dialects = [])
|
46
46
|
self.dialects = []
|
47
47
|
add_dialects.each do |dialect|
|
@@ -53,7 +53,7 @@ module RubySMB
|
|
53
53
|
# Adds a Negotiate Context to the #negotiate_context_list
|
54
54
|
#
|
55
55
|
# @param [NegotiateContext] the Negotiate Context structure you wish to add
|
56
|
-
# @return [Array<
|
56
|
+
# @return [Array<Integer>] the array of all currently added Negotiate Contexts
|
57
57
|
# @raise [ArgumentError] if the dialect is not a NegotiateContext structure
|
58
58
|
def add_negotiate_context(nc)
|
59
59
|
raise ArgumentError, 'Must be a NegotiateContext' unless nc.is_a? NegotiateContext
|
@@ -48,7 +48,7 @@ module RubySMB
|
|
48
48
|
# Adds a Negotiate Context to the #negotiate_context_list
|
49
49
|
#
|
50
50
|
# @param [NegotiateContext] the Negotiate Context structure you wish to add
|
51
|
-
# @return [Array<
|
51
|
+
# @return [Array<Integer>] the array of all currently added Negotiate Contexts
|
52
52
|
# @raise [ArgumentError] if the dialect is not a NegotiateContext structure
|
53
53
|
def add_negotiate_context(nc)
|
54
54
|
raise ArgumentError, 'Must be a NegotiateContext' unless nc.is_a? NegotiateContext
|
data/lib/ruby_smb/smb2/pipe.rb
CHANGED
@@ -145,6 +145,10 @@ module RubySMB
|
|
145
145
|
break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
|
146
146
|
raw_data = read(bytes: @tree.client.max_buffer_size)
|
147
147
|
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
148
|
+
if options[:auth_level] &&
|
149
|
+
[RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(options[:auth_level])
|
150
|
+
handle_integrity_privacy(dcerpc_response, auth_level: options[:auth_level], auth_type: options[:auth_type])
|
151
|
+
end
|
148
152
|
stub_data << dcerpc_response.stub.to_s
|
149
153
|
end
|
150
154
|
stub_data
|
data/lib/ruby_smb/version.rb
CHANGED
data/lib/ruby_smb.rb
CHANGED
@@ -6,11 +6,13 @@ require 'openssl/ccm'
|
|
6
6
|
require 'openssl/cmac'
|
7
7
|
require 'windows_error'
|
8
8
|
require 'windows_error/nt_status'
|
9
|
+
require 'ruby_smb/ntlm/custom/ntlm'
|
9
10
|
# A packet parsing and manipulation library for the SMB1 and SMB2 protocols
|
10
11
|
#
|
11
12
|
# [[MS-SMB] Server Message Block (SMB) Protocol Version 1](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
|
12
13
|
# [[MS-SMB2] Server Message Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
|
13
14
|
module RubySMB
|
15
|
+
require 'ruby_smb/utils'
|
14
16
|
require 'ruby_smb/error'
|
15
17
|
require 'ruby_smb/create_actions'
|
16
18
|
require 'ruby_smb/dispositions'
|
@@ -2701,7 +2701,7 @@ RSpec.describe RubySMB::Client do
|
|
2701
2701
|
session_key,
|
2702
2702
|
"SMB2AESCCM\x00",
|
2703
2703
|
"ServerIn \x00",
|
2704
|
-
|
2704
|
+
length: 128
|
2705
2705
|
).and_call_original
|
2706
2706
|
client.smb3_encrypt(data)
|
2707
2707
|
end
|
@@ -2717,7 +2717,7 @@ RSpec.describe RubySMB::Client do
|
|
2717
2717
|
session_key,
|
2718
2718
|
"SMBC2SCipherKey\x00",
|
2719
2719
|
'',
|
2720
|
-
|
2720
|
+
length: 128
|
2721
2721
|
).and_call_original
|
2722
2722
|
client.smb3_encrypt(data)
|
2723
2723
|
end
|
@@ -2791,7 +2791,7 @@ RSpec.describe RubySMB::Client do
|
|
2791
2791
|
session_key,
|
2792
2792
|
"SMB2AESCCM\x00",
|
2793
2793
|
"ServerOut\x00",
|
2794
|
-
|
2794
|
+
length: 128
|
2795
2795
|
).and_call_original
|
2796
2796
|
client.smb3_decrypt(transform_packet)
|
2797
2797
|
end
|
@@ -2807,7 +2807,7 @@ RSpec.describe RubySMB::Client do
|
|
2807
2807
|
session_key,
|
2808
2808
|
"SMBS2CCipherKey\x00",
|
2809
2809
|
'',
|
2810
|
-
|
2810
|
+
length: 128
|
2811
2811
|
).and_call_original
|
2812
2812
|
client.smb3_decrypt(transform_packet)
|
2813
2813
|
end
|
@@ -631,7 +631,7 @@ RSpec.describe RubySMB::Dcerpc::Winreg do
|
|
631
631
|
|
632
632
|
it 'calls #enum_key the expected number of times' do
|
633
633
|
winreg.enum_registry_key(key)
|
634
|
-
expect(winreg).to have_received(:enum_key).with(subkey_handle, instance_of(
|
634
|
+
expect(winreg).to have_received(:enum_key).with(subkey_handle, instance_of(Integer)).twice
|
635
635
|
end
|
636
636
|
|
637
637
|
it 'closes the key' do
|
@@ -710,7 +710,7 @@ RSpec.describe RubySMB::Dcerpc::Winreg do
|
|
710
710
|
|
711
711
|
it 'calls #enum_key the expected number of times' do
|
712
712
|
winreg.enum_registry_values(key)
|
713
|
-
expect(winreg).to have_received(:enum_value).with(subkey_handle, instance_of(
|
713
|
+
expect(winreg).to have_received(:enum_value).with(subkey_handle, instance_of(Integer)).twice
|
714
714
|
end
|
715
715
|
|
716
716
|
it 'closes the key' do
|
@@ -112,7 +112,7 @@ RSpec.describe RubySMB::SMB1::Packet::ReadAndxRequest do
|
|
112
112
|
expect { packet.set_read_from_named_pipe('true') }.to raise_error(ArgumentError)
|
113
113
|
end
|
114
114
|
|
115
|
-
it 'raises an exception when the value is a
|
115
|
+
it 'raises an exception when the value is a Integer' do
|
116
116
|
expect { packet.set_read_from_named_pipe(1) }.to raise_error(ArgumentError)
|
117
117
|
end
|
118
118
|
|
@@ -138,7 +138,7 @@ RSpec.describe RubySMB::SMB1::Packet::ReadAndxRequest do
|
|
138
138
|
expect { packet.set_64_bit_offset('true') }.to raise_error(ArgumentError)
|
139
139
|
end
|
140
140
|
|
141
|
-
it 'raises an exception when the value is a
|
141
|
+
it 'raises an exception when the value is a Integer' do
|
142
142
|
expect { packet.set_64_bit_offset(1) }.to raise_error(ArgumentError)
|
143
143
|
end
|
144
144
|
|
@@ -137,7 +137,7 @@ RSpec.describe RubySMB::SMB1::Packet::WriteAndxRequest do
|
|
137
137
|
expect { packet.set_64_bit_offset('true') }.to raise_error(ArgumentError)
|
138
138
|
end
|
139
139
|
|
140
|
-
it 'raises an exception when the value is a
|
140
|
+
it 'raises an exception when the value is a Integer' do
|
141
141
|
expect { packet.set_64_bit_offset(1) }.to raise_error(ArgumentError)
|
142
142
|
end
|
143
143
|
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_smb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.2.
|
4
|
+
version: 3.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Metasploit Hackers
|
@@ -97,7 +97,7 @@ cert_chain:
|
|
97
97
|
EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
|
98
98
|
9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
|
99
99
|
-----END CERTIFICATE-----
|
100
|
-
date:
|
100
|
+
date: 2023-01-18 00:00:00.000000000 Z
|
101
101
|
dependencies:
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: redcarpet
|
@@ -493,6 +493,7 @@ files:
|
|
493
493
|
- lib/ruby_smb/nbss/session_request.rb
|
494
494
|
- lib/ruby_smb/ntlm.rb
|
495
495
|
- lib/ruby_smb/ntlm/client.rb
|
496
|
+
- lib/ruby_smb/ntlm/custom/ntlm.rb
|
496
497
|
- lib/ruby_smb/peer_info.rb
|
497
498
|
- lib/ruby_smb/server.rb
|
498
499
|
- lib/ruby_smb/server/cli.rb
|
@@ -673,6 +674,7 @@ files:
|
|
673
674
|
- lib/ruby_smb/smb2/smb2_header.rb
|
674
675
|
- lib/ruby_smb/smb2/tree.rb
|
675
676
|
- lib/ruby_smb/smb_error.rb
|
677
|
+
- lib/ruby_smb/utils.rb
|
676
678
|
- lib/ruby_smb/version.rb
|
677
679
|
- ruby_smb.gemspec
|
678
680
|
- spec/lib/ruby_smb/client_spec.rb
|
@@ -999,8 +1001,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
999
1001
|
- !ruby/object:Gem::Version
|
1000
1002
|
version: '0'
|
1001
1003
|
requirements: []
|
1002
|
-
|
1003
|
-
rubygems_version: 2.7.10
|
1004
|
+
rubygems_version: 3.3.26
|
1004
1005
|
signing_key:
|
1005
1006
|
specification_version: 4
|
1006
1007
|
summary: A pure Ruby implementation of the SMB Protocol Family
|
metadata.gz.sig
CHANGED
Binary file
|