ruby_smb 3.1.2 → 3.1.5
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/examples/file_server.rb +6 -68
- data/examples/virtual_file_server.rb +10 -62
- data/lib/ruby_smb/client/authentication.rb +29 -4
- data/lib/ruby_smb/client/negotiation.rb +2 -0
- data/lib/ruby_smb/client.rb +18 -3
- data/lib/ruby_smb/dcerpc/error.rb +13 -0
- data/lib/ruby_smb/dcerpc/fault.rb +83 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +19 -8
- data/lib/ruby_smb/dcerpc/request.rb +15 -10
- data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +5 -0
- data/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr/samr_delete_user_request.rb +21 -0
- data/lib/ruby_smb/dcerpc/samr/samr_delete_user_response.rb +22 -0
- data/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response.rb +0 -31
- data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_response.rb +1 -14
- data/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_response.rb +21 -0
- data/lib/ruby_smb/dcerpc/samr.rb +453 -83
- data/lib/ruby_smb/dcerpc.rb +1 -0
- data/lib/ruby_smb/error.rb +4 -0
- data/lib/ruby_smb/gss.rb +1 -0
- data/lib/ruby_smb/ntlm/client.rb +74 -0
- data/lib/ruby_smb/ntlm.rb +1 -0
- data/lib/ruby_smb/server/cli.rb +121 -0
- data/lib/ruby_smb/server.rb +1 -0
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +11 -0
- data/lib/ruby_smb/smb1/pipe.rb +4 -1
- data/lib/ruby_smb/smb2/pipe.rb +4 -2
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +1 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_request_spec.rb +69 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_response_spec.rb +69 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_delete_user_request_spec.rb +42 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_delete_user_response_spec.rb +51 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_request_spec.rb +60 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_response_spec.rb +75 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response_spec.rb +0 -195
- data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_request_spec.rb +62 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_response_spec.rb +54 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_request_spec.rb +67 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_response_spec.rb +35 -0
- data/spec/lib/ruby_smb/dcerpc/samr_spec.rb +194 -0
- data/spec/lib/ruby_smb/ntlm/client/session_spec.rb +114 -0
- data/spec/lib/ruby_smb/ntlm/client_spec.rb +36 -0
- data.tar.gz.sig +0 -0
- metadata +39 -2
- metadata.gz.sig +0 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
module RubySMB::NTLM
|
2
|
+
module Message
|
3
|
+
def deflag
|
4
|
+
security_buffers.inject(head_size) do |cur, a|
|
5
|
+
a[1].offset = cur
|
6
|
+
cur += a[1].data_size
|
7
|
+
has_flag?(:UNICODE) ? cur + cur % 2 : cur
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def serialize
|
12
|
+
deflag
|
13
|
+
@alist.map { |n, f| f.serialize }.join + security_buffers.map { |n, f| f.value + (has_flag?(:UNICODE) ? "\x00".b * (f.value.length % 2) : '') }.join
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Client < Net::NTLM::Client
|
18
|
+
class Session < Net::NTLM::Client::Session
|
19
|
+
def authenticate!
|
20
|
+
calculate_user_session_key!
|
21
|
+
type3_opts = {
|
22
|
+
:lm_response => is_anonymous? ? "\x00".b : lmv2_resp,
|
23
|
+
:ntlm_response => is_anonymous? ? '' : ntlmv2_resp,
|
24
|
+
:domain => domain,
|
25
|
+
:user => username,
|
26
|
+
:workstation => workstation,
|
27
|
+
:flag => (challenge_message.flag & client.flags)
|
28
|
+
}
|
29
|
+
t3 = Net::NTLM::Message::Type3.create type3_opts
|
30
|
+
t3.extend(Message)
|
31
|
+
if negotiate_key_exchange?
|
32
|
+
t3.enable(:session_key)
|
33
|
+
rc4 = OpenSSL::Cipher.new("rc4")
|
34
|
+
rc4.encrypt
|
35
|
+
rc4.key = user_session_key
|
36
|
+
sk = rc4.update exported_session_key
|
37
|
+
sk << rc4.final
|
38
|
+
t3.session_key = sk
|
39
|
+
end
|
40
|
+
t3
|
41
|
+
end
|
42
|
+
|
43
|
+
def is_anonymous?
|
44
|
+
username == '' && password == ''
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def use_oem_strings?
|
50
|
+
# @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832
|
51
|
+
!challenge_message.has_flag?(:UNICODE) && challenge_message.has_flag?(:OEM)
|
52
|
+
end
|
53
|
+
|
54
|
+
def calculate_user_session_key!
|
55
|
+
if is_anonymous?
|
56
|
+
# see MS-NLMP section 3.4
|
57
|
+
@user_session_key = "\x00".b * 16
|
58
|
+
else
|
59
|
+
@user_session_key = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, nt_proof_str)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def init_context(resp = nil, channel_binding = nil)
|
65
|
+
if resp.nil?
|
66
|
+
@session = nil
|
67
|
+
type1_message
|
68
|
+
else
|
69
|
+
@session = Client::Session.new(self, Net::NTLM::Message.decode64(resp), channel_binding)
|
70
|
+
@session.authenticate!
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/ruby_smb/ntlm.rb
CHANGED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module RubySMB
|
4
|
+
class Server
|
5
|
+
module Cli
|
6
|
+
DEFAULT_OPTIONS = {
|
7
|
+
allow_anonymous: true,
|
8
|
+
allow_guests: false,
|
9
|
+
domain: nil,
|
10
|
+
username: 'RubySMB',
|
11
|
+
password: 'password',
|
12
|
+
share_name: 'home',
|
13
|
+
smbv1: true,
|
14
|
+
smbv2: true,
|
15
|
+
smbv3: true
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
# Parse options from the command line. The resulting option hash is suitable for passing to {build}.
|
19
|
+
#
|
20
|
+
# @yield [options, parser] A block that can be used to update the built option parser.
|
21
|
+
# @yieldparam [Hash<Symbol => >] options The options hash that should be assigned to.
|
22
|
+
# @yieldparam [OptionParser] parser The built option parser.
|
23
|
+
# @param [Hash] defaults Default option values to use.
|
24
|
+
# @return [Hash<Symbol => >] The options hash.
|
25
|
+
# * :allow_anonymous [Boolean] Whether or not to allow anonymous authentication.
|
26
|
+
# * :allow_guest [Boolean] Whether or not to allow guest authentication.
|
27
|
+
# * :username [String] The username of the account to add for authentication.
|
28
|
+
# * :password [String] The password of the account to add for authentication.
|
29
|
+
# * :domain [String] The domain of the account to add for authentication.
|
30
|
+
# * :smbv1 [Boolean] Whether or not to enable SMBv1 dialects.
|
31
|
+
# * :smbv2 [Boolean] Whether or not to enable SMBv2 dialects.
|
32
|
+
# * :smbv3 [Boolean] Whether or not to enable SMBv3 dialects.
|
33
|
+
# * :share_name [String] The name of the share to add.
|
34
|
+
def self.parse(defaults: {}, &block)
|
35
|
+
defaults = DEFAULT_OPTIONS.merge(defaults)
|
36
|
+
options = defaults.clone
|
37
|
+
OptionParser.new do |parser|
|
38
|
+
parser.on("--share-name SHARE_NAME", "The share name (default: #{defaults[:share_name]})") do |share|
|
39
|
+
options[:share_name] = share
|
40
|
+
end
|
41
|
+
|
42
|
+
parser.on("--[no-]anonymous", "Allow anonymous access (default: #{defaults[:allow_anonymous]})") do |allow_anonymous|
|
43
|
+
options[:allow_anonymous] = allow_anonymous
|
44
|
+
end
|
45
|
+
|
46
|
+
parser.on("--[no-]guests", "Allow guest accounts (default: #{defaults[:allow_guests]})") do |allow_guests|
|
47
|
+
options[:allow_guests] = allow_guests
|
48
|
+
end
|
49
|
+
|
50
|
+
parser.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{defaults[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
|
51
|
+
options[:smbv1] = smbv1
|
52
|
+
end
|
53
|
+
|
54
|
+
parser.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{defaults[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
|
55
|
+
options[:smbv2] = smbv2
|
56
|
+
end
|
57
|
+
|
58
|
+
parser.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{defaults[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
|
59
|
+
options[:smbv3] = smbv3
|
60
|
+
end
|
61
|
+
|
62
|
+
parser.on("--username USERNAME", "The account's username (default: #{defaults[:username]})") do |username|
|
63
|
+
if username.include?('\\')
|
64
|
+
options[:domain], options[:username] = username.split('\\', 2)
|
65
|
+
else
|
66
|
+
options[:username] = username
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
parser.on("--password PASSWORD", "The account's password (default: #{defaults[:password]})") do |password|
|
71
|
+
options[:password] = password
|
72
|
+
end
|
73
|
+
|
74
|
+
block.call(options, parser) if block_given?
|
75
|
+
end.parse!
|
76
|
+
|
77
|
+
options
|
78
|
+
end
|
79
|
+
|
80
|
+
# Build a server instance from the specified options. The NTLM provider will be used for authentication.
|
81
|
+
#
|
82
|
+
# @param [Hash] options the options to use while building the server. See the return value of {parse} for a
|
83
|
+
# comprehensive list of keys.
|
84
|
+
def self.build(options)
|
85
|
+
ntlm_provider = RubySMB::Gss::Provider::NTLM.new(
|
86
|
+
allow_anonymous: options[:allow_anonymous],
|
87
|
+
allow_guests: options[:allow_guests]
|
88
|
+
)
|
89
|
+
ntlm_provider.put_account(options[:username], options[:password], domain: options[:domain]) # password can also be an NTLM hash
|
90
|
+
|
91
|
+
server = RubySMB::Server.new(
|
92
|
+
gss_provider: ntlm_provider,
|
93
|
+
logger: :stdout
|
94
|
+
)
|
95
|
+
server.dialects.filter! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB1 } unless options[:smbv1]
|
96
|
+
server.dialects.filter! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB2 } unless options[:smbv2]
|
97
|
+
server.dialects.filter! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB3 } unless options[:smbv3]
|
98
|
+
|
99
|
+
server
|
100
|
+
end
|
101
|
+
|
102
|
+
# Run the server forever. At least 1 SMB dialect must be enabled.
|
103
|
+
#
|
104
|
+
# @param [RubySMB::Server] server The server instance to run.
|
105
|
+
# @param [#puts] out The stream to write standard output messages to.
|
106
|
+
# @param [#puts] err The stream to write error messages to.
|
107
|
+
def self.run(server, out: $stdout, err: $stderr)
|
108
|
+
if server.dialects.empty?
|
109
|
+
err.puts 'at least one version must be enabled'
|
110
|
+
return
|
111
|
+
end
|
112
|
+
|
113
|
+
out.puts 'server is running'
|
114
|
+
server.run do |server_client|
|
115
|
+
out.puts 'received connection'
|
116
|
+
true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/lib/ruby_smb/server.rb
CHANGED
@@ -6,6 +6,7 @@ module RubySMB
|
|
6
6
|
# Currently, the server only supports negotiating and authenticating requests. No other server functionality is
|
7
7
|
# available at this time. The negotiating and authentication is supported for SMB versions 1 through 3.1.1.
|
8
8
|
class Server
|
9
|
+
require 'ruby_smb/server/cli'
|
9
10
|
require 'ruby_smb/server/server_client'
|
10
11
|
require 'ruby_smb/server/session'
|
11
12
|
require 'ruby_smb/server/share'
|
@@ -40,6 +40,17 @@ module RubySMB
|
|
40
40
|
parameter_block :parameter_block
|
41
41
|
data_block :data_block
|
42
42
|
|
43
|
+
# Takes the specified security buffer string and sets it in the {RubySMB::SMB1::Packet::SessionSetupRequest::DataBlock#security_blob}
|
44
|
+
# field. It also automatically sets the length in
|
45
|
+
# {RubySMB::SMB1::Packet::SessionSetupRequest::ParameterBlock#security_blob_length}
|
46
|
+
#
|
47
|
+
# @param buffer [String] the security buffer
|
48
|
+
# @return [void]
|
49
|
+
def set_security_buffer(buffer)
|
50
|
+
parameter_block.security_blob_length = buffer.length
|
51
|
+
data_block.security_blob = buffer
|
52
|
+
end
|
53
|
+
|
43
54
|
# Takes an NTLM Type 1 Message and creates the GSS Security Blob
|
44
55
|
# for it and sets it in the {RubySMB::SMB1::Packet::SessionSetupRequest::DataBlock#security_blob}
|
45
56
|
# field. It also automatically sets the length in
|
data/lib/ruby_smb/smb1/pipe.rb
CHANGED
@@ -142,7 +142,10 @@ module RubySMB
|
|
142
142
|
|
143
143
|
def dcerpc_response_from_raw_response(raw_data)
|
144
144
|
dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
|
145
|
-
|
145
|
+
if dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::FAULT
|
146
|
+
status = dcerpc_response.stub.unpack('V').first
|
147
|
+
raise RubySMB::Dcerpc::Error::FaultError.new('A fault occurred', status: status)
|
148
|
+
elsif dcerpc_response.pdu_header.ptype != RubySMB::Dcerpc::PTypes::RESPONSE
|
146
149
|
raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
|
147
150
|
end
|
148
151
|
dcerpc_response
|
data/lib/ruby_smb/smb2/pipe.rb
CHANGED
@@ -135,12 +135,14 @@ module RubySMB
|
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
138
|
-
|
139
138
|
private
|
140
139
|
|
141
140
|
def dcerpc_response_from_raw_response(raw_data)
|
142
141
|
dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
|
143
|
-
|
142
|
+
if dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::FAULT
|
143
|
+
status = dcerpc_response.stub.unpack('V').first
|
144
|
+
raise RubySMB::Dcerpc::Error::FaultError.new('A fault occurred', status: status)
|
145
|
+
elsif dcerpc_response.pdu_header.ptype != RubySMB::Dcerpc::PTypes::RESPONSE
|
144
146
|
raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
|
145
147
|
end
|
146
148
|
dcerpc_response
|
data/lib/ruby_smb/version.rb
CHANGED
@@ -1881,6 +1881,7 @@ RSpec.describe RubySMB::Client do
|
|
1881
1881
|
before :example do
|
1882
1882
|
smb2_client.smb3 = true
|
1883
1883
|
smb2_client.session_encrypt_data = false
|
1884
|
+
smb2_client.preauth_integrity_hash_value = ''
|
1884
1885
|
end
|
1885
1886
|
|
1886
1887
|
it 'sets the session_encrypt_data parameter to true if the server requires encryption' do
|
@@ -0,0 +1,69 @@
|
|
1
|
+
RSpec.describe RubySMB::Dcerpc::Samr::SamrCreateUser2InDomainRequest do
|
2
|
+
subject(:packet) { described_class.new }
|
3
|
+
|
4
|
+
it { is_expected.to respond_to :domain_handle }
|
5
|
+
it { is_expected.to respond_to :name }
|
6
|
+
it { is_expected.to respond_to :account_type }
|
7
|
+
it { is_expected.to respond_to :desired_access }
|
8
|
+
it { is_expected.to respond_to :opnum }
|
9
|
+
|
10
|
+
it 'is little endian' do
|
11
|
+
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'is a BinData::Record' do
|
15
|
+
expect(packet).to be_a(BinData::Record)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#domain_handle' do
|
19
|
+
it 'is a SamprHandle structure' do
|
20
|
+
expect(packet.domain_handle).to be_a RubySMB::Dcerpc::Samr::SamprHandle
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#name' do
|
25
|
+
it 'is a RpcUnicodeString' do
|
26
|
+
expect(packet.name).to be_a RubySMB::Dcerpc::RpcUnicodeString
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#account_type' do
|
31
|
+
it 'is a NdrUint32 structure' do
|
32
|
+
expect(packet.account_type).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#desired_access' do
|
37
|
+
it 'is a NdrUint32 structure' do
|
38
|
+
expect(packet.desired_access).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#initialize_instance' do
|
43
|
+
it 'sets #opnum to SAMR_CREATE_USER2_IN_DOMAIN constant' do
|
44
|
+
expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_CREATE_USER2_IN_DOMAIN)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'reads itself' do
|
49
|
+
new_packet = described_class.new({
|
50
|
+
domain_handle: {
|
51
|
+
context_handle_attributes: 0,
|
52
|
+
context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
|
53
|
+
},
|
54
|
+
name: 'test',
|
55
|
+
account_type: 1,
|
56
|
+
desired_access: 2
|
57
|
+
})
|
58
|
+
expected_output = {
|
59
|
+
domain_handle: {
|
60
|
+
context_handle_attributes: 0,
|
61
|
+
context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
|
62
|
+
},
|
63
|
+
name: {:buffer=>"test".encode('utf-16le'), :buffer_length=>8, :maximum_length=>8},
|
64
|
+
account_type: 1,
|
65
|
+
desired_access: 2
|
66
|
+
}
|
67
|
+
expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
RSpec.describe RubySMB::Dcerpc::Samr::SamrCreateUser2InDomainResponse do
|
2
|
+
subject(:packet) { described_class.new }
|
3
|
+
|
4
|
+
it { is_expected.to respond_to :user_handle }
|
5
|
+
it { is_expected.to respond_to :granted_access }
|
6
|
+
it { is_expected.to respond_to :relative_id }
|
7
|
+
it { is_expected.to respond_to :error_status }
|
8
|
+
it { is_expected.to respond_to :opnum }
|
9
|
+
|
10
|
+
it 'is little endian' do
|
11
|
+
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'is a BinData::Record' do
|
15
|
+
expect(packet).to be_a(BinData::Record)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#user_handle' do
|
19
|
+
it 'is a SamprHandle structure' do
|
20
|
+
expect(packet.user_handle).to be_a RubySMB::Dcerpc::Samr::SamprHandle
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#granted_access' do
|
25
|
+
it 'is a NdrUint32' do
|
26
|
+
expect(packet.granted_access).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#relative_id' do
|
31
|
+
it 'is a NdrUint32 structure' do
|
32
|
+
expect(packet.relative_id).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#error_status' do
|
37
|
+
it 'is a NdrUint32 structure' do
|
38
|
+
expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#initialize_instance' do
|
43
|
+
it 'sets #opnum to SAMR_CREATE_USER2_IN_DOMAIN constant' do
|
44
|
+
expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_CREATE_USER2_IN_DOMAIN)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'reads itself' do
|
49
|
+
new_packet = described_class.new({
|
50
|
+
user_handle: {
|
51
|
+
context_handle_attributes: 0,
|
52
|
+
context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
|
53
|
+
},
|
54
|
+
granted_access: 1,
|
55
|
+
relative_id: 2,
|
56
|
+
error_status: 0x11223344
|
57
|
+
})
|
58
|
+
expected_output = {
|
59
|
+
user_handle: {
|
60
|
+
context_handle_attributes: 0,
|
61
|
+
context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
|
62
|
+
},
|
63
|
+
granted_access: 1,
|
64
|
+
relative_id: 2,
|
65
|
+
error_status: 0x11223344
|
66
|
+
}
|
67
|
+
expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
RSpec.describe RubySMB::Dcerpc::Samr::SamrDeleteUserRequest do
|
2
|
+
subject(:packet) { described_class.new }
|
3
|
+
|
4
|
+
it { is_expected.to respond_to :user_handle }
|
5
|
+
it { is_expected.to respond_to :opnum }
|
6
|
+
|
7
|
+
it 'is little endian' do
|
8
|
+
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'is a BinData::Record' do
|
12
|
+
expect(packet).to be_a(BinData::Record)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#user_handle' do
|
16
|
+
it 'is a SamprHandle structure' do
|
17
|
+
expect(packet.user_handle).to be_a RubySMB::Dcerpc::Samr::SamprHandle
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#initialize_instance' do
|
22
|
+
it 'sets #opnum to SAMR_DELETE_USER constant' do
|
23
|
+
expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_DELETE_USER)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'reads itself' do
|
28
|
+
new_packet = described_class.new({
|
29
|
+
user_handle: {
|
30
|
+
context_handle_attributes: 0,
|
31
|
+
context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
|
32
|
+
}
|
33
|
+
})
|
34
|
+
expected_output = {
|
35
|
+
user_handle: {
|
36
|
+
context_handle_attributes: 0,
|
37
|
+
context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
|
38
|
+
}
|
39
|
+
}
|
40
|
+
expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
RSpec.describe RubySMB::Dcerpc::Samr::SamrDeleteUserResponse do
|
2
|
+
subject(:packet) { described_class.new }
|
3
|
+
|
4
|
+
it { is_expected.to respond_to :user_handle }
|
5
|
+
it { is_expected.to respond_to :error_status }
|
6
|
+
it { is_expected.to respond_to :opnum }
|
7
|
+
|
8
|
+
it 'is little endian' do
|
9
|
+
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'is a BinData::Record' do
|
13
|
+
expect(packet).to be_a(BinData::Record)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#user_handle' do
|
17
|
+
it 'is a SamprHandle structure' do
|
18
|
+
expect(packet.user_handle).to be_a RubySMB::Dcerpc::Samr::SamprHandle
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#error_status' do
|
23
|
+
it 'is a NdrUint32 structure' do
|
24
|
+
expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#initialize_instance' do
|
29
|
+
it 'sets #opnum to SAMR_DELETE_USER constant' do
|
30
|
+
expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_DELETE_USER)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'reads itself' do
|
35
|
+
new_packet = described_class.new({
|
36
|
+
user_handle: {
|
37
|
+
context_handle_attributes: 0,
|
38
|
+
context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
|
39
|
+
},
|
40
|
+
error_status: 0x11223344
|
41
|
+
})
|
42
|
+
expected_output = {
|
43
|
+
user_handle: {
|
44
|
+
context_handle_attributes: 0,
|
45
|
+
context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
|
46
|
+
},
|
47
|
+
error_status: 0x11223344
|
48
|
+
}
|
49
|
+
expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
RSpec.describe RubySMB::Dcerpc::Samr::SamrEnumerateDomainsInSamServerRequest do
|
2
|
+
subject(:packet) { described_class.new }
|
3
|
+
|
4
|
+
it { is_expected.to respond_to :server_handle }
|
5
|
+
it { is_expected.to respond_to :enumeration_context }
|
6
|
+
it { is_expected.to respond_to :prefered_maximum_length }
|
7
|
+
it { is_expected.to respond_to :opnum }
|
8
|
+
|
9
|
+
it 'is little endian' do
|
10
|
+
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'is a BinData::Record' do
|
14
|
+
expect(packet).to be_a(BinData::Record)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#server_handle' do
|
18
|
+
it 'is a SamprHandle structure' do
|
19
|
+
expect(packet.server_handle).to be_a RubySMB::Dcerpc::Samr::SamprHandle
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#enumeration_context' do
|
24
|
+
it 'is a NdrUint32 structure' do
|
25
|
+
expect(packet.enumeration_context).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#prefered_maximum_length' do
|
30
|
+
it 'is a NdrUint32 structure' do
|
31
|
+
expect(packet.prefered_maximum_length).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#initialize_instance' do
|
36
|
+
it 'sets #opnum to SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER constant' do
|
37
|
+
expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'reads itself' do
|
42
|
+
new_packet = described_class.new({
|
43
|
+
server_handle: {
|
44
|
+
context_handle_attributes: 0,
|
45
|
+
context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
|
46
|
+
},
|
47
|
+
enumeration_context: 1,
|
48
|
+
prefered_maximum_length: 2
|
49
|
+
})
|
50
|
+
expected_output = {
|
51
|
+
server_handle: {
|
52
|
+
context_handle_attributes: 0,
|
53
|
+
context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
|
54
|
+
},
|
55
|
+
enumeration_context: 1,
|
56
|
+
prefered_maximum_length: 2
|
57
|
+
}
|
58
|
+
expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
RSpec.describe RubySMB::Dcerpc::Samr::SamrEnumerateDomainsInSamServerResponse do
|
2
|
+
subject(:packet) { described_class.new }
|
3
|
+
|
4
|
+
it { is_expected.to respond_to :enumeration_context }
|
5
|
+
it { is_expected.to respond_to :buffer }
|
6
|
+
it { is_expected.to respond_to :count_returned }
|
7
|
+
it { is_expected.to respond_to :error_status }
|
8
|
+
it { is_expected.to respond_to :opnum }
|
9
|
+
|
10
|
+
it 'is little endian' do
|
11
|
+
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'is a BinData::Record' do
|
15
|
+
expect(packet).to be_a(BinData::Record)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#enumeration_context' do
|
19
|
+
it 'is a NdrUint32 structure' do
|
20
|
+
expect(packet.enumeration_context).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#buffer' do
|
25
|
+
it 'is a PsamprEnumerationBuffer structure' do
|
26
|
+
expect(packet.buffer).to be_a RubySMB::Dcerpc::Samr::PsamprEnumerationBuffer
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#count_returned' do
|
31
|
+
it 'is a NdrUint32 structure' do
|
32
|
+
expect(packet.count_returned).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#error_status' do
|
37
|
+
it 'is a NdrUint32 structure' do
|
38
|
+
expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#initialize_instance' do
|
43
|
+
it 'sets #opnum to SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER constant' do
|
44
|
+
expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'reads itself' do
|
49
|
+
new_packet = described_class.new({
|
50
|
+
enumeration_context: 1,
|
51
|
+
buffer: {
|
52
|
+
entries_read: 2,
|
53
|
+
buffer: [
|
54
|
+
{relative_id: 500, name: { buffer_length: 26, maximum_length: 26, buffer: "Builtin".encode('utf-16le') }},
|
55
|
+
{relative_id: 501, name: { buffer_length: 10, maximum_length: 10, buffer: "RUBYSMB".encode('utf-16le') }},
|
56
|
+
]
|
57
|
+
},
|
58
|
+
count_returned: 2,
|
59
|
+
error_status: 0x11223344
|
60
|
+
})
|
61
|
+
expected_output = {
|
62
|
+
enumeration_context: 1,
|
63
|
+
buffer: {
|
64
|
+
entries_read: 2,
|
65
|
+
buffer: [
|
66
|
+
{relative_id: 500, name: { buffer_length: 26, maximum_length: 26, buffer: "Builtin".encode('utf-16le') }},
|
67
|
+
{relative_id: 501, name: { buffer_length: 10, maximum_length: 10, buffer: "RUBYSMB".encode('utf-16le') }},
|
68
|
+
]
|
69
|
+
},
|
70
|
+
count_returned: 2,
|
71
|
+
error_status: 0x11223344
|
72
|
+
}
|
73
|
+
expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
|
74
|
+
end
|
75
|
+
end
|