ruby_smb 3.1.2 → 3.1.5
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/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
|