ruby_smb 2.0.7 → 2.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/verify.yml +57 -0
- data/README.md +0 -1
- data/examples/auth_capture.rb +71 -0
- data/lib/ruby_smb/client/negotiation.rb +11 -13
- data/lib/ruby_smb/client.rb +32 -27
- data/lib/ruby_smb/compression/lznt1.rb +164 -0
- data/lib/ruby_smb/compression.rb +7 -0
- data/lib/ruby_smb/dialect.rb +45 -0
- data/lib/ruby_smb/dispatcher/base.rb +1 -1
- data/lib/ruby_smb/dispatcher/socket.rb +1 -1
- data/lib/ruby_smb/gss/provider/authenticator.rb +42 -0
- data/lib/ruby_smb/gss/provider/ntlm.rb +303 -0
- data/lib/ruby_smb/gss/provider.rb +35 -0
- data/lib/ruby_smb/gss.rb +56 -63
- data/lib/ruby_smb/ntlm.rb +45 -0
- data/lib/ruby_smb/server/server_client/negotiation.rb +155 -0
- data/lib/ruby_smb/server/server_client/session_setup.rb +82 -0
- data/lib/ruby_smb/server/server_client.rb +163 -0
- data/lib/ruby_smb/server.rb +54 -0
- data/lib/ruby_smb/signing.rb +59 -0
- data/lib/ruby_smb/smb1/packet/negotiate_response.rb +11 -11
- data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
- data/lib/ruby_smb/smb1/tree.rb +1 -1
- data/lib/ruby_smb/smb2/negotiate_context.rb +18 -2
- data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +4 -0
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +9 -0
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +0 -1
- data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -2
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +1 -1
- data/lib/ruby_smb/smb2.rb +3 -1
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +5 -3
- data/spec/lib/ruby_smb/client_spec.rb +24 -16
- data/spec/lib/ruby_smb/compression/lznt1_spec.rb +32 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm/account_spec.rb +32 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +101 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm/os_version_spec.rb +32 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm_spec.rb +113 -0
- data/spec/lib/ruby_smb/server/server_client_spec.rb +156 -0
- data/spec/lib/ruby_smb/server_spec.rb +32 -0
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +4 -4
- data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +5 -5
- data/spec/spec_helper.rb +1 -1
- data.tar.gz.sig +3 -1
- metadata +30 -4
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -6
- data/lib/ruby_smb/client/signing.rb +0 -64
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module RubySMB
|
4
|
+
# This class provides the SMB server core. Settings that are relevant server wide are managed by this object.
|
5
|
+
# Currently, the server only supports negotiating and authenticating requests. No other server functionality is
|
6
|
+
# available at this time. The negotiating and authentication is supported for SMB versions 1 through 3.1.1.
|
7
|
+
class Server
|
8
|
+
require 'ruby_smb/server/server_client'
|
9
|
+
require 'ruby_smb/gss/provider/ntlm'
|
10
|
+
|
11
|
+
Connection = Struct.new(:client, :thread)
|
12
|
+
|
13
|
+
# @param server_sock the socket on which the server should listen
|
14
|
+
# @param [Gss::Provider] the authentication provider
|
15
|
+
def initialize(server_sock: nil, gss_provider: nil)
|
16
|
+
server_sock = ::TCPServer.new(445) if server_sock.nil?
|
17
|
+
|
18
|
+
@guid = Random.new.bytes(16)
|
19
|
+
@socket = server_sock
|
20
|
+
@connections = []
|
21
|
+
@gss_provider = gss_provider || Gss::Provider::NTLM.new
|
22
|
+
# reject the wildcard dialect because it's not a real dialect we can use for this purpose
|
23
|
+
@dialects = RubySMB::Dialect::ALL.keys.reject { |dialect| dialect == "0x%04x" % RubySMB::SMB2::SMB2_WILDCARD_REVISION }.reverse
|
24
|
+
end
|
25
|
+
|
26
|
+
# Run the server and accept any connections. For each connection, the block will be executed if specified. When the
|
27
|
+
# block returns false, the loop will exit and the server will no long accept new connections.
|
28
|
+
def run(&block)
|
29
|
+
loop do
|
30
|
+
sock = @socket.accept
|
31
|
+
server_client = ServerClient.new(self, RubySMB::Dispatcher::Socket.new(sock))
|
32
|
+
@connections << Connection.new(server_client, Thread.new { server_client.run })
|
33
|
+
|
34
|
+
break unless block.nil? || block.call(server_client)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# The dialects that this server will negotiate with clients, in ascending order of preference.
|
39
|
+
# @!attribute [r] dialects
|
40
|
+
# @return [Array<String>]
|
41
|
+
attr_accessor :dialects
|
42
|
+
|
43
|
+
# The GSS Provider instance that this server will use to authenticate
|
44
|
+
# incoming client connections.
|
45
|
+
# @!attribute [r] gss_provider
|
46
|
+
# @return [RubySMB::Gss::Provider::Base]
|
47
|
+
attr_reader :gss_provider
|
48
|
+
|
49
|
+
# The 16 byte GUID that uniquely identifies this server instance.
|
50
|
+
# @!attribute [r] guid
|
51
|
+
attr_reader :guid
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module RubySMB
|
2
|
+
# Contains the methods for handling packet signing
|
3
|
+
module Signing
|
4
|
+
# The NTLM Session Key used for signing
|
5
|
+
# @!attribute [rw] session_key
|
6
|
+
# @return [String]
|
7
|
+
attr_accessor :session_key
|
8
|
+
|
9
|
+
# Take an SMB1 packet and sign it.
|
10
|
+
#
|
11
|
+
# @param packet [RubySMB::GenericPacket] the packet to sign
|
12
|
+
# @return [RubySMB::GenericPacket] the signed packet
|
13
|
+
def smb1_sign(packet)
|
14
|
+
# Pack the Sequence counter into a int64le
|
15
|
+
packed_sequence_counter = [sequence_counter].pack('Q<')
|
16
|
+
packet.smb_header.security_features = packed_sequence_counter
|
17
|
+
signature = OpenSSL::Digest::MD5.digest(session_key + packet.to_binary_s)[0, 8]
|
18
|
+
packet.smb_header.security_features = signature
|
19
|
+
@sequence_counter += 1
|
20
|
+
|
21
|
+
packet
|
22
|
+
end
|
23
|
+
|
24
|
+
# Take an SMB2 packet and sign it.
|
25
|
+
#
|
26
|
+
# @param packet [RubySMB::GenericPacket] the packet to sign
|
27
|
+
# @return [RubySMB::GenericPacket] the signed packet
|
28
|
+
def smb2_sign(packet)
|
29
|
+
packet.smb2_header.flags.signed = 1
|
30
|
+
packet.smb2_header.signature = "\x00" * 16
|
31
|
+
hmac = OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA256'), session_key, packet.to_binary_s)
|
32
|
+
packet.smb2_header.signature = hmac[0, 16]
|
33
|
+
|
34
|
+
packet
|
35
|
+
end
|
36
|
+
|
37
|
+
# Take an SMB3 packet and sign it.
|
38
|
+
#
|
39
|
+
# @param packet [RubySMB::GenericPacket] the packet to sign
|
40
|
+
# @return [RubySMB::GenericPacket] the signed packet
|
41
|
+
def smb3_sign(packet)
|
42
|
+
case @dialect
|
43
|
+
when '0x0300', '0x0302'
|
44
|
+
signing_key = Crypto::KDF.counter_mode(@session_key, "SMB2AESCMAC\x00", "SmbSign\x00")
|
45
|
+
when '0x0311'
|
46
|
+
signing_key = Crypto::KDF.counter_mode(@session_key, "SMBSigningKey\x00", @preauth_integrity_hash_value)
|
47
|
+
else
|
48
|
+
raise Error::SigningError.new("Dialect #{@dialect.inspect} is incompatible with SMBv3 signing")
|
49
|
+
end
|
50
|
+
|
51
|
+
packet.smb2_header.flags.signed = 1
|
52
|
+
packet.smb2_header.signature = "\x00" * 16
|
53
|
+
hmac = OpenSSL::CMAC.digest('AES', signing_key, packet.to_binary_s)
|
54
|
+
packet.smb2_header.signature = hmac[0, 16]
|
55
|
+
|
56
|
+
packet
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -8,17 +8,17 @@ module RubySMB
|
|
8
8
|
|
9
9
|
# An SMB_Parameters Block as defined by the {NegotiateResponse}.
|
10
10
|
class ParameterBlock < RubySMB::SMB1::ParameterBlock
|
11
|
-
uint16 :dialect_index,
|
12
|
-
security_mode :security_mode
|
13
|
-
uint16 :max_mpx_count, label: 'Max Multiplex Count'
|
14
|
-
uint16 :max_number_vcs, label: 'Max Virtual Circuits'
|
15
|
-
uint32 :max_buffer_size, label: 'Max Buffer Size'
|
16
|
-
uint32 :max_raw_size, label: 'Max Raw Size'
|
17
|
-
uint32 :session_key, label: 'Session Key'
|
18
|
-
capabilities :capabilities
|
19
|
-
file_time :system_time, label: 'Server System Time'
|
20
|
-
int16 :server_time_zone, label: 'Server TimeZone'
|
21
|
-
uint8 :challenge_length, label: 'Challenge Length', initial_value: 0x08
|
11
|
+
uint16 :dialect_index, label: 'Dialect Index'
|
12
|
+
security_mode :security_mode, onlyif: -> { dialect_index != 0xffff }
|
13
|
+
uint16 :max_mpx_count, label: 'Max Multiplex Count', onlyif: -> { dialect_index != 0xffff }
|
14
|
+
uint16 :max_number_vcs, label: 'Max Virtual Circuits', onlyif: -> { dialect_index != 0xffff }
|
15
|
+
uint32 :max_buffer_size, label: 'Max Buffer Size', onlyif: -> { dialect_index != 0xffff }
|
16
|
+
uint32 :max_raw_size, label: 'Max Raw Size', onlyif: -> { dialect_index != 0xffff }
|
17
|
+
uint32 :session_key, label: 'Session Key', onlyif: -> { dialect_index != 0xffff }
|
18
|
+
capabilities :capabilities, onlyif: -> { dialect_index != 0xffff }
|
19
|
+
file_time :system_time, label: 'Server System Time', onlyif: -> { dialect_index != 0xffff }
|
20
|
+
int16 :server_time_zone, label: 'Server TimeZone', onlyif: -> { dialect_index != 0xffff }
|
21
|
+
uint8 :challenge_length, label: 'Challenge Length', initial_value: 0x08, onlyif: -> { dialect_index != 0xffff }
|
22
22
|
end
|
23
23
|
|
24
24
|
# An SMB_Data Block as defined by the {NegotiateResponse}
|
@@ -8,7 +8,7 @@ module RubySMB
|
|
8
8
|
|
9
9
|
# An SMB_Parameters Block as defined by the {NegotiateResponseExtended}.
|
10
10
|
class ParameterBlock < RubySMB::SMB1::ParameterBlock
|
11
|
-
uint16 :dialect_index,
|
11
|
+
uint16 :dialect_index, label: 'Dialect Index'
|
12
12
|
security_mode :security_mode
|
13
13
|
uint16 :max_mpx_count, label: 'Max Multiplex Count'
|
14
14
|
uint16 :max_number_vcs, label: 'Max Virtual Circuits'
|
@@ -47,7 +47,7 @@ module RubySMB
|
|
47
47
|
|
48
48
|
# Takes an NTLM Type 3 Message and creates the GSS Security Blob
|
49
49
|
# for it and sets it in the {RubySMB::SMB1::Packet::SessionSetupRequest::DataBlock#security_blob}
|
50
|
-
# field. It also
|
50
|
+
# field. It also automatically sets the length in
|
51
51
|
# {RubySMB::SMB1::Packet::SessionSetupRequest::ParameterBlock#security_blob_length}
|
52
52
|
#
|
53
53
|
# @param type3_message [String] the serialized Type 3 NTLM message
|
data/lib/ruby_smb/smb1/tree.rb
CHANGED
@@ -69,9 +69,22 @@ module RubySMB
|
|
69
69
|
class NetnameNegotiateContextId < BinData::Record
|
70
70
|
endian :little
|
71
71
|
|
72
|
-
|
72
|
+
count_bytes_remaining :bytes_remaining
|
73
|
+
default_parameter data_length: nil
|
74
|
+
hide :bytes_remaining
|
75
|
+
|
76
|
+
string16 :net_name, label: 'Net Name', read_length: -> { data_length.nil? ? bytes_remaining : data_length }
|
73
77
|
end
|
74
78
|
|
79
|
+
# An SMB2 TRANSPORT_CAPABILITIES context struct as defined in
|
80
|
+
# [2.2.3.1.5 SMB2_TRANSPORT_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/450a1888-a645-4988-8638-5a11f4617545)
|
81
|
+
class TransportCapabilities < BinData::Record
|
82
|
+
SMB2_ACCEPT_TRANSPORT_LEVEL_SECURITY = 1 # Transport security is offered to skip SMB2 encryption on this connection.
|
83
|
+
|
84
|
+
endian :little
|
85
|
+
|
86
|
+
uint32 :flags, label: 'Flags'
|
87
|
+
end
|
75
88
|
|
76
89
|
# An SMB2 NEGOTIATE_CONTEXT struct as defined in
|
77
90
|
# [2.2.3.1 SMB2 NEGOTIATE_CONTEXT Request Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/15332256-522e-4a53-8cd7-0bd17678a2f7)
|
@@ -84,6 +97,8 @@ module RubySMB
|
|
84
97
|
SMB2_COMPRESSION_CAPABILITIES = 0x0003
|
85
98
|
# The NegotiateContext Data field contains the server name to which the client connects.
|
86
99
|
SMB2_NETNAME_NEGOTIATE_CONTEXT_ID = 0x0005
|
100
|
+
# The NegotiateContext Data field contains the transport capabilities, as specified in section 2.2.3.1.5.
|
101
|
+
SMB2_TRANSPORT_CAPABILITIES = 0x0006
|
87
102
|
|
88
103
|
endian :little
|
89
104
|
|
@@ -95,7 +110,8 @@ module RubySMB
|
|
95
110
|
preauth_integrity_capabilities SMB2_PREAUTH_INTEGRITY_CAPABILITIES, label: 'Preauthentication Integrity Capabilities'
|
96
111
|
encryption_capabilities SMB2_ENCRYPTION_CAPABILITIES, label: 'Encryption Capabilities'
|
97
112
|
compression_capabilities SMB2_COMPRESSION_CAPABILITIES, label: 'Compression Capabilities'
|
98
|
-
netname_negotiate_context_id SMB2_NETNAME_NEGOTIATE_CONTEXT_ID, label: 'Netname Negotiate Context ID'
|
113
|
+
netname_negotiate_context_id SMB2_NETNAME_NEGOTIATE_CONTEXT_ID, label: 'Netname Negotiate Context ID', data_length: :data_length
|
114
|
+
transport_capabilities SMB2_TRANSPORT_CAPABILITIES, label: 'Transport Capabilities'
|
99
115
|
end
|
100
116
|
|
101
117
|
def pad_length
|
@@ -3,6 +3,9 @@ module RubySMB
|
|
3
3
|
module Packet
|
4
4
|
# An SMB2 COMPRESSION_TRANSFORM_HEADER Packet as defined in
|
5
5
|
# [2.2.42 SMB2 COMPRESSION_TRANSFORM_HEADER](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/1d435f21-9a21-4f4c-828e-624a176cf2a0)
|
6
|
+
# NOTE: On 2021-04-06 the official documentation split the definition of COMPRESSION_TRANSFORM_HEADER into the following two variants:
|
7
|
+
# * SMB2_COMPRESSION_TRANSFORM_HEADER_CHAINED
|
8
|
+
# * SMB2_COMPRESSION_TRANSFORM_HEADER_UNCHAINED
|
6
9
|
class CompressionTransformHeader < BinData::Record
|
7
10
|
endian :little
|
8
11
|
|
@@ -11,6 +14,7 @@ module RubySMB
|
|
11
14
|
uint16 :compression_algorithm, label: 'Compression Algorithm'
|
12
15
|
uint16 :flags, label: 'Flags'
|
13
16
|
uint32 :offset, label: 'Offset / Length'
|
17
|
+
array :compressed_data, label: 'Compressed Data', type: :uint8, read_until: :eof
|
14
18
|
end
|
15
19
|
|
16
20
|
# An SMB2 SMB2_COMPRESSION_TRANSFORM_HEADER_PAYLOAD Packet as defined in
|
@@ -64,6 +64,15 @@ module RubySMB
|
|
64
64
|
self.negotiate_context_list
|
65
65
|
end
|
66
66
|
|
67
|
+
# Find the first Negotiate Context structure that matches the given
|
68
|
+
# context type
|
69
|
+
#
|
70
|
+
# @param [Integer] the Negotiate Context structure you wish to add
|
71
|
+
# @return [NegotiateContext] the Negotiate Context structure or nil if
|
72
|
+
# not found
|
73
|
+
def find_negotiate_context(type)
|
74
|
+
negotiate_context_list.find { |nc| nc.context_type == type }
|
75
|
+
end
|
67
76
|
|
68
77
|
private
|
69
78
|
|
@@ -11,8 +11,8 @@ module RubySMB
|
|
11
11
|
uint16 :structure_size, label: 'Structure Size', initial_value: 9
|
12
12
|
session_flags :session_flags
|
13
13
|
uint16 :security_buffer_offset, label: 'Security Buffer Offset', initial_value: 0x48
|
14
|
-
uint16 :security_buffer_length, label: 'Security Buffer Length'
|
15
|
-
string :buffer, label: 'Security Buffer',
|
14
|
+
uint16 :security_buffer_length, label: 'Security Buffer Length', initial_value: -> { buffer.length }
|
15
|
+
string :buffer, label: 'Security Buffer', read_length: -> { security_buffer_length }
|
16
16
|
|
17
17
|
def initialize_instance
|
18
18
|
super
|
@@ -101,7 +101,7 @@ module RubySMB
|
|
101
101
|
path.to_binary_s.length
|
102
102
|
end
|
103
103
|
end
|
104
|
-
string16 :path, label: 'Path Buffer', onlyif: -> { flags != SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT }
|
104
|
+
string16 :path, label: 'Path Buffer', onlyif: -> { flags != SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT }, read_length: -> { path_length }
|
105
105
|
tree_connect_request_extension :tree_connect_request_extension, label: 'Tree Connect Request Extension', onlyif: -> { flags == SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT }
|
106
106
|
end
|
107
107
|
end
|
data/lib/ruby_smb/smb2/tree.rb
CHANGED
@@ -61,7 +61,7 @@ module RubySMB
|
|
61
61
|
opts = opts.dup
|
62
62
|
opts[:filename] = opts[:filename].dup
|
63
63
|
opts[:filename] = opts[:filename][1..-1] if opts[:filename].start_with? '\\'
|
64
|
-
open_file(opts)
|
64
|
+
open_file(**opts)
|
65
65
|
end
|
66
66
|
|
67
67
|
def open_file(filename:, attributes: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
|
data/lib/ruby_smb/smb2.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
module RubySMB
|
2
2
|
# A packet parsing and manipulation library for the SMB2 protocol
|
3
3
|
#
|
4
|
-
# [[MS-SMB2] Server
|
4
|
+
# [[MS-SMB2] Server Message Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
|
5
5
|
module SMB2
|
6
6
|
# Protocol ID value. Translates to \xFESMB
|
7
7
|
SMB2_PROTOCOL_ID = 0xFE534D42
|
8
|
+
# Wildcard revision, see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/63abf97c-0d09-47e2-88d6-6bfa552949a5
|
9
|
+
SMB2_WILDCARD_REVISION = 0x02ff
|
8
10
|
|
9
11
|
require 'ruby_smb/smb2/info_type'
|
10
12
|
require 'ruby_smb/smb2/commands'
|
data/lib/ruby_smb/version.rb
CHANGED
data/lib/ruby_smb.rb
CHANGED
@@ -8,8 +8,8 @@ require 'windows_error'
|
|
8
8
|
require 'windows_error/nt_status'
|
9
9
|
# A packet parsing and manipulation library for the SMB1 and SMB2 protocols
|
10
10
|
#
|
11
|
-
# [[MS-SMB] Server
|
12
|
-
# [[MS-SMB2] Server
|
11
|
+
# [[MS-SMB] Server Message Block (SMB) Protocol Version 1](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
|
12
|
+
# [[MS-SMB2] Server Message Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
|
13
13
|
module RubySMB
|
14
14
|
require 'ruby_smb/error'
|
15
15
|
require 'ruby_smb/dispositions'
|
@@ -21,9 +21,11 @@ module RubySMB
|
|
21
21
|
require 'ruby_smb/generic_packet'
|
22
22
|
require 'ruby_smb/dispatcher'
|
23
23
|
require 'ruby_smb/version'
|
24
|
-
require 'ruby_smb/version'
|
25
24
|
require 'ruby_smb/smb2'
|
26
25
|
require 'ruby_smb/smb1'
|
27
26
|
require 'ruby_smb/client'
|
28
27
|
require 'ruby_smb/crypto'
|
28
|
+
require 'ruby_smb/compression'
|
29
|
+
require 'ruby_smb/server'
|
30
|
+
require 'ruby_smb/dialect'
|
29
31
|
end
|
@@ -209,6 +209,8 @@ RSpec.describe RubySMB::Client do
|
|
209
209
|
|
210
210
|
context 'when signing' do
|
211
211
|
it 'calls #smb1_sign if it is an SMB1 packet' do
|
212
|
+
allow(client).to receive(:signing_required).and_return(true)
|
213
|
+
allow(client).to receive(:session_key).and_return(Random.new.bytes(16))
|
212
214
|
expect(client).to receive(:smb1_sign).with(smb1_request).and_call_original
|
213
215
|
client.send_recv(smb1_request)
|
214
216
|
end
|
@@ -223,15 +225,11 @@ RSpec.describe RubySMB::Client do
|
|
223
225
|
|
224
226
|
it 'calls #smb2_sign if it is an SMB2 client' do
|
225
227
|
allow(smb2_client).to receive(:is_status_pending?).and_return(false)
|
228
|
+
allow(smb2_client).to receive(:signing_required).and_return(true)
|
229
|
+
allow(smb2_client).to receive(:session_key).and_return(Random.new.bytes(16))
|
226
230
|
expect(smb2_client).to receive(:smb2_sign).with(smb2_request).and_call_original
|
227
231
|
smb2_client.send_recv(smb2_request)
|
228
232
|
end
|
229
|
-
|
230
|
-
it 'calls #smb3_sign if it is an SMB3 client' do
|
231
|
-
allow(smb3_client).to receive(:is_status_pending?).and_return(false)
|
232
|
-
expect(smb3_client).to receive(:smb3_sign).with(smb2_request).and_call_original
|
233
|
-
smb3_client.send_recv(smb2_request)
|
234
|
-
end
|
235
233
|
end
|
236
234
|
end
|
237
235
|
|
@@ -809,6 +807,18 @@ RSpec.describe RubySMB::Client do
|
|
809
807
|
smb1_extended_response.to_binary_s
|
810
808
|
}
|
811
809
|
|
810
|
+
let(:smb1_response) {
|
811
|
+
packet = RubySMB::SMB1::Packet::NegotiateResponse.new
|
812
|
+
smb1_capabilities_dup = smb1_capabilities.dup
|
813
|
+
smb1_capabilities_dup[:extended_security] = 0
|
814
|
+
|
815
|
+
packet.parameter_block.capabilities = smb1_capabilities_dup
|
816
|
+
packet
|
817
|
+
}
|
818
|
+
let(:smb1_response_raw) {
|
819
|
+
smb1_response.to_binary_s
|
820
|
+
}
|
821
|
+
|
812
822
|
let(:smb2_response) { RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x200) }
|
813
823
|
let(:smb3_response) { RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x300) }
|
814
824
|
|
@@ -997,10 +1007,14 @@ RSpec.describe RubySMB::Client do
|
|
997
1007
|
|
998
1008
|
describe '#negotiate_response' do
|
999
1009
|
context 'with only SMB1' do
|
1000
|
-
it 'returns a properly formed packet' do
|
1010
|
+
it 'returns a properly formed NegotiateResponseExtended packet if extended_security is set as 1' do
|
1001
1011
|
expect(smb1_client.negotiate_response(smb1_extended_response_raw)).to eq smb1_extended_response
|
1002
1012
|
end
|
1003
1013
|
|
1014
|
+
it 'returns a properly formed NegotiateResponse packet if extended_security is set as 0' do
|
1015
|
+
expect(smb1_client.negotiate_response(smb1_response_raw)).to eq smb1_response
|
1016
|
+
end
|
1017
|
+
|
1004
1018
|
it 'raises an exception if the response is not a SMB packet' do
|
1005
1019
|
expect { smb1_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
|
1006
1020
|
end
|
@@ -1015,12 +1029,6 @@ RSpec.describe RubySMB::Client do
|
|
1015
1029
|
bogus_response.smb_header.command = 0xff
|
1016
1030
|
expect { smb1_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
1017
1031
|
end
|
1018
|
-
|
1019
|
-
it 'considers the response invalid if Extended Security is not enabled' do
|
1020
|
-
bogus_response = smb1_extended_response
|
1021
|
-
bogus_response.parameter_block.capabilities.extended_security = 0
|
1022
|
-
expect { smb1_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
1023
|
-
end
|
1024
1032
|
end
|
1025
1033
|
|
1026
1034
|
context 'with only SMB2' do
|
@@ -2077,7 +2085,7 @@ RSpec.describe RubySMB::Client do
|
|
2077
2085
|
it 'generates the HMAC based on the packet and the NTLM session key and signs the packet with it' do
|
2078
2086
|
smb2_client.session_key = 'foo'
|
2079
2087
|
smb2_client.signing_required = true
|
2080
|
-
expect(OpenSSL::HMAC).to receive(:digest).with(instance_of(OpenSSL::Digest
|
2088
|
+
expect(OpenSSL::HMAC).to receive(:digest).with(instance_of(OpenSSL::Digest), smb2_client.session_key, request1.to_binary_s).and_return(fake_hmac)
|
2081
2089
|
expect(smb2_client.smb2_sign(request1).smb2_header.signature).to eq fake_hmac
|
2082
2090
|
end
|
2083
2091
|
end
|
@@ -2177,7 +2185,7 @@ RSpec.describe RubySMB::Client do
|
|
2177
2185
|
smb3_client.dialect = '0x0202'
|
2178
2186
|
expect { smb3_client.smb3_sign(request) }.to raise_error(
|
2179
2187
|
RubySMB::Error::SigningError,
|
2180
|
-
'Dialect is incompatible with SMBv3 signing'
|
2188
|
+
'Dialect "0x0202" is incompatible with SMBv3 signing'
|
2181
2189
|
)
|
2182
2190
|
end
|
2183
2191
|
end
|
@@ -2227,7 +2235,7 @@ RSpec.describe RubySMB::Client do
|
|
2227
2235
|
smb3_client.dialect = '0x0202'
|
2228
2236
|
expect { smb3_client.smb3_sign(request) }.to raise_error(
|
2229
2237
|
RubySMB::Error::SigningError,
|
2230
|
-
'Dialect is incompatible with SMBv3 signing'
|
2238
|
+
'Dialect "0x0202" is incompatible with SMBv3 signing'
|
2231
2239
|
)
|
2232
2240
|
end
|
2233
2241
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RubySMB::Compression::LZNT1 do
|
4
|
+
describe '.compress' do
|
5
|
+
it 'generates an empty blob when provided an empty blob' do
|
6
|
+
expected = "".b
|
7
|
+
expect(described_class.compress('')).to eq(expected)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'generates a compressed blob when provided a string with non-reoccurring characters' do
|
11
|
+
expect(described_class.compress('RubySMB')).to eq("\x060RubySMB".b)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'generates a compressed blob when provided a string of reoccurring characters' do
|
15
|
+
expect(described_class.compress("\x01" * 0x200)).to eq("\x03\xB0\x02\x01\xFC\x01".b)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.decompress' do
|
20
|
+
it 'generates a decompressed blob for a string with non-reoccurring characters' do
|
21
|
+
expect(described_class.decompress("\x060RubySMB".b)).to eq('RubySMB')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'generates a decompressed blob for a string of reoccurring characters' do
|
25
|
+
expect(described_class.decompress("\x03\xB0\x02\x01\xFC\x01".b)).to eq("\x01" * 0x200)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'raises an EncodingError when the length is invalid' do
|
29
|
+
expect { described_class.decompress("\x010".b) }.to raise_error(EncodingError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|