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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4749ae5185070d44e447593cd863ee5ac17a87468b16b14a251f68e0166b7663
|
4
|
+
data.tar.gz: b63378e4367136e0c6dffc35aa02b5c5ce1df450d5b64300eaee060e7938e9ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f09557d4ba6148796bb6adf2c1169ebad237f021527975fabb179e1eb2099cfb2c17e45d3b57629c7fc61106092317076e41c77d39f8cb3d4335d280a75e144c
|
7
|
+
data.tar.gz: e969cc9f474e64e4cf7ba9bcf39e1dfc30b9e365bf6c758abd1e7ff905a6e9a3a8f3c3b33fc88f3104b7998201406d8b78bca34e746c58cfa4c03353bd94f789
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/examples/file_server.rb
CHANGED
@@ -3,81 +3,19 @@
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'optparse'
|
5
5
|
require 'ruby_smb'
|
6
|
-
require 'ruby_smb/gss/provider/ntlm'
|
7
6
|
|
8
7
|
# we just need *a* default encoding to handle the strings from the NTLM messages
|
9
8
|
Encoding.default_internal = 'UTF-8' if Encoding.default_internal.nil?
|
10
9
|
|
11
|
-
options = {
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
username: 'RubySMB',
|
16
|
-
password: 'password',
|
17
|
-
share_name: 'home',
|
18
|
-
share_path: '.',
|
19
|
-
smbv1: true,
|
20
|
-
smbv2: true,
|
21
|
-
smbv3: true
|
22
|
-
}
|
23
|
-
OptionParser.new do |opts|
|
24
|
-
opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
|
25
|
-
opts.on("--path PATH", "The path to share (default: #{options[:share_path]})") do |path|
|
10
|
+
options = RubySMB::Server::Cli.parse(defaults: { share_path: '.' }) do |options, parser|
|
11
|
+
parser.banner = "Usage: #{File.basename(__FILE__)} [options]"
|
12
|
+
|
13
|
+
parser.on("--share-path SHARE_PATH", "The path to share (default: #{options[:share_path]})") do |path|
|
26
14
|
options[:share_path] = path
|
27
15
|
end
|
28
|
-
opts.on("--share SHARE", "The share name (default: #{options[:share_name]})") do |share|
|
29
|
-
options[:share_name] = share
|
30
|
-
end
|
31
|
-
opts.on("--[no-]anonymous", "Allow anonymous access (default: #{options[:allow_anonymous]})") do |allow_anonymous|
|
32
|
-
options[:allow_anonymous] = allow_anonymous
|
33
|
-
end
|
34
|
-
opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
|
35
|
-
options[:smbv1] = smbv1
|
36
|
-
end
|
37
|
-
opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
|
38
|
-
options[:smbv2] = smbv2
|
39
|
-
end
|
40
|
-
opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
|
41
|
-
options[:smbv3] = smbv3
|
42
|
-
end
|
43
|
-
opts.on("--[no-]guests", "Allow guest accounts (default: #{options[:allow_guests]})") do |allow_guests|
|
44
|
-
options[:allow_guests] = allow_guests
|
45
|
-
end
|
46
|
-
opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
|
47
|
-
if username.include?('\\')
|
48
|
-
options[:domain], options[:username] = username.split('\\', 2)
|
49
|
-
else
|
50
|
-
options[:username] = username
|
51
|
-
end
|
52
|
-
end
|
53
|
-
opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
|
54
|
-
options[:password] = password
|
55
|
-
end
|
56
|
-
end.parse!
|
57
|
-
|
58
|
-
ntlm_provider = RubySMB::Gss::Provider::NTLM.new(
|
59
|
-
allow_anonymous: options[:allow_anonymous],
|
60
|
-
allow_guests: options[:allow_guests]
|
61
|
-
)
|
62
|
-
ntlm_provider.put_account(options[:username], options[:password], domain: options[:domain]) # password can also be an NTLM hash
|
63
|
-
|
64
|
-
server = RubySMB::Server.new(
|
65
|
-
gss_provider: ntlm_provider,
|
66
|
-
logger: :stdout
|
67
|
-
)
|
68
|
-
server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB1 } unless options[:smbv1]
|
69
|
-
server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB2 } unless options[:smbv2]
|
70
|
-
server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB3 } unless options[:smbv3]
|
71
|
-
|
72
|
-
if server.dialects.empty?
|
73
|
-
puts "at least one version must be enabled"
|
74
|
-
exit false
|
75
16
|
end
|
76
17
|
|
18
|
+
server = RubySMB::Server::Cli.build(options)
|
77
19
|
server.add_share(RubySMB::Server::Share::Provider::Disk.new(options[:share_name], options[:share_path]))
|
78
|
-
puts "server is running"
|
79
|
-
server.run do
|
80
|
-
puts "received connection"
|
81
|
-
true
|
82
|
-
end
|
83
20
|
|
21
|
+
RubySMB::Server::Cli.run(server)
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'optparse'
|
5
5
|
require 'ruby_smb'
|
6
|
-
require 'ruby_smb/gss/provider/ntlm'
|
7
6
|
|
8
7
|
# we just need *a* default encoding to handle the strings from the NTLM messages
|
9
8
|
Encoding.default_internal = 'UTF-8' if Encoding.default_internal.nil?
|
@@ -32,70 +31,23 @@ MAGIC_8_BALL_ANSWERS = [
|
|
32
31
|
'Very doubtful'
|
33
32
|
]
|
34
33
|
|
35
|
-
options =
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
password: 'password',
|
40
|
-
share_name: 'home',
|
41
|
-
smbv1: true,
|
42
|
-
smbv2: true,
|
43
|
-
smbv3: true
|
44
|
-
}
|
45
|
-
OptionParser.new do |opts|
|
46
|
-
opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
|
47
|
-
opts.on("--share SHARE", "The share name (default: #{options[:share_name]})") do |share|
|
48
|
-
options[:share_name] = share
|
49
|
-
end
|
50
|
-
opts.on("--[no-]anonymous", "Allow anonymous access (default: #{options[:allow_anonymous]})") do |allow_anonymous|
|
51
|
-
options[:allow_anonymous] = allow_anonymous
|
52
|
-
end
|
53
|
-
opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
|
54
|
-
options[:smbv1] = smbv1
|
55
|
-
end
|
56
|
-
opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
|
57
|
-
options[:smbv2] = smbv2
|
58
|
-
end
|
59
|
-
opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
|
60
|
-
options[:smbv3] = smbv3
|
61
|
-
end
|
62
|
-
opts.on("--username USERNAME", "The account's username (default: #{options[: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
|
-
opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
|
70
|
-
options[:password] = password
|
71
|
-
end
|
72
|
-
opts.on("--virtual-content CONTENT", "The virtual share contents") do |virtual_content|
|
34
|
+
options = RubySMB::Server::Cli.parse do |options, parser|
|
35
|
+
parser.banner = "Usage: #{File.basename(__FILE__)} [options]"
|
36
|
+
|
37
|
+
parser.on("--virtual-content CONTENT", "The virtual share contents") do |virtual_content|
|
73
38
|
options[:virtual_content] = virtual_content
|
74
39
|
end
|
75
|
-
|
40
|
+
|
41
|
+
parser.on("--virtual-name NAME", "The virtual share file name") do |virtual_name|
|
76
42
|
options[:virtual_name] = virtual_name
|
77
43
|
end
|
78
|
-
|
44
|
+
|
45
|
+
parser.on("--virtual-type TYPE", "The virtual share type") do |virtual_type|
|
79
46
|
options[:virtual_type] = virtual_type
|
80
47
|
end
|
81
|
-
end.parse!
|
82
|
-
|
83
|
-
ntlm_provider = RubySMB::Gss::Provider::NTLM.new(allow_anonymous: options[:allow_anonymous])
|
84
|
-
ntlm_provider.put_account(options[:username], options[:password], domain: options[:domain]) # password can also be an NTLM hash
|
85
|
-
|
86
|
-
server = RubySMB::Server.new(
|
87
|
-
gss_provider: ntlm_provider,
|
88
|
-
logger: :stdout
|
89
|
-
)
|
90
|
-
server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB1 } unless options[:smbv1]
|
91
|
-
server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB2 } unless options[:smbv2]
|
92
|
-
server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB3 } unless options[:smbv3]
|
93
|
-
|
94
|
-
if server.dialects.empty?
|
95
|
-
puts "at least one version must be enabled"
|
96
|
-
exit false
|
97
48
|
end
|
98
49
|
|
50
|
+
server = RubySMB::Server::Cli.build(options)
|
99
51
|
virtual_disk = RubySMB::Server::Share::Provider::VirtualDisk.new(options[:share_name])
|
100
52
|
|
101
53
|
# greeting is a static text file
|
@@ -135,9 +87,5 @@ elsif options[:virtual_content] || options[:virtual_name] || options[:virtual_ty
|
|
135
87
|
end
|
136
88
|
|
137
89
|
server.add_share(virtual_disk)
|
138
|
-
puts "server is running"
|
139
|
-
server.run do |server_client|
|
140
|
-
puts "received connection"
|
141
|
-
true
|
142
|
-
end
|
143
90
|
|
91
|
+
RubySMB::Server::Cli.run(server)
|
@@ -80,7 +80,7 @@ module RubySMB
|
|
80
80
|
type2_b64_message = smb1_type2_message(challenge_packet)
|
81
81
|
type3_message = @ntlm_client.init_context(type2_b64_message)
|
82
82
|
|
83
|
-
@session_key = @ntlm_client.session_key
|
83
|
+
@application_key = @session_key = @ntlm_client.session_key
|
84
84
|
challenge_message = @ntlm_client.session.challenge_message
|
85
85
|
store_target_info(challenge_message.target_info) if challenge_message.has_flag?(:TARGET_INFO)
|
86
86
|
@os_version = extract_os_version(challenge_message.os_version.to_s) unless challenge_message.os_version.empty?
|
@@ -146,7 +146,7 @@ module RubySMB
|
|
146
146
|
end
|
147
147
|
|
148
148
|
# Takes the raw binary string and returns a {RubySMB::SMB1::Packet::SessionSetupResponse}
|
149
|
-
def
|
149
|
+
def smb1_session_setup_response(raw_response)
|
150
150
|
packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
|
151
151
|
|
152
152
|
unless packet.valid?
|
@@ -159,6 +159,11 @@ module RubySMB
|
|
159
159
|
packet
|
160
160
|
end
|
161
161
|
|
162
|
+
# Takes the raw binary string and returns a {RubySMB::SMB1::Packet::SessionSetupResponse}
|
163
|
+
def smb1_ntlmssp_final_packet(raw_response)
|
164
|
+
smb1_session_setup_response(raw_response)
|
165
|
+
end
|
166
|
+
|
162
167
|
# Takes the raw binary string and returns a {RubySMB::SMB1::Packet::SessionSetupResponse}
|
163
168
|
def smb1_ntlmssp_challenge_packet(raw_response)
|
164
169
|
packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
|
@@ -205,7 +210,7 @@ module RubySMB
|
|
205
210
|
type2_b64_message = smb2_type2_message(challenge_packet)
|
206
211
|
type3_message = @ntlm_client.init_context(type2_b64_message)
|
207
212
|
|
208
|
-
@session_key = @ntlm_client.session_key
|
213
|
+
@application_key = @session_key = @ntlm_client.session_key
|
209
214
|
challenge_message = ntlm_client.session.challenge_message
|
210
215
|
store_target_info(challenge_message.target_info) if challenge_message.has_flag?(:TARGET_INFO)
|
211
216
|
@os_version = extract_os_version(challenge_message.os_version.to_s) unless challenge_message.os_version.empty?
|
@@ -222,6 +227,21 @@ module RubySMB
|
|
222
227
|
# disable encryption when necessary
|
223
228
|
@session_encrypt_data = false
|
224
229
|
end
|
230
|
+
|
231
|
+
# see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/7fd079ca-17e6-4f02-8449-46b606ea289c
|
232
|
+
if @dialect == '0x0300' || @dialect == '0x0302'
|
233
|
+
@application_key = RubySMB::Crypto::KDF.counter_mode(
|
234
|
+
@session_key,
|
235
|
+
"SMB2APP\x00",
|
236
|
+
"SmbRpc\x00"
|
237
|
+
)
|
238
|
+
else
|
239
|
+
@application_key = RubySMB::Crypto::KDF.counter_mode(
|
240
|
+
@session_key,
|
241
|
+
"SMBAppKey\x00",
|
242
|
+
@preauth_integrity_hash_value
|
243
|
+
)
|
244
|
+
end
|
225
245
|
# otherwise, leave encryption to the default value that it was initialized to
|
226
246
|
end
|
227
247
|
######
|
@@ -235,7 +255,7 @@ module RubySMB
|
|
235
255
|
end
|
236
256
|
|
237
257
|
# Takes the raw binary string and returns a {RubySMB::SMB2::Packet::SessionSetupResponse}
|
238
|
-
def
|
258
|
+
def smb2_session_setup_response(raw_response)
|
239
259
|
packet = RubySMB::SMB2::Packet::SessionSetupResponse.read(raw_response)
|
240
260
|
unless packet.valid?
|
241
261
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -248,6 +268,11 @@ module RubySMB
|
|
248
268
|
packet
|
249
269
|
end
|
250
270
|
|
271
|
+
# Takes the raw binary string and returns a {RubySMB::SMB2::Packet::SessionSetupResponse}
|
272
|
+
def smb2_ntlmssp_final_packet(raw_response)
|
273
|
+
smb2_session_setup_response(raw_response)
|
274
|
+
end
|
275
|
+
|
251
276
|
# Takes the raw binary string and returns a {RubySMB::SMB2::Packet::SessionSetupResponse}
|
252
277
|
def smb2_ntlmssp_challenge_packet(raw_response)
|
253
278
|
packet = RubySMB::SMB2::Packet::SessionSetupResponse.read(raw_response)
|
@@ -118,6 +118,7 @@ module RubySMB
|
|
118
118
|
self.server_max_buffer_size = packet.parameter_block.max_buffer_size - 260
|
119
119
|
self.negotiated_smb_version = 1
|
120
120
|
self.session_encrypt_data = false
|
121
|
+
self.negotiation_security_buffer = packet.data_block.security_blob
|
121
122
|
'SMB1'
|
122
123
|
when RubySMB::SMB2::Packet::NegotiateResponse
|
123
124
|
self.smb1 = false
|
@@ -137,6 +138,7 @@ module RubySMB
|
|
137
138
|
self.server_start_time = packet.server_start_time.to_time if packet.server_start_time != 0
|
138
139
|
self.server_system_time = packet.system_time.to_time if packet.system_time != 0
|
139
140
|
self.server_supports_multi_credit = self.dialect != '0x0202' && packet&.capabilities&.large_mtu == 1
|
141
|
+
self.negotiation_security_buffer = packet.security_buffer
|
140
142
|
case self.dialect
|
141
143
|
when '0x02ff'
|
142
144
|
when '0x0300', '0x0302'
|
data/lib/ruby_smb/client.rb
CHANGED
@@ -54,6 +54,13 @@ module RubySMB
|
|
54
54
|
# The default maximum size of a SMB message that the Server accepts (in bytes)
|
55
55
|
SERVER_MAX_BUFFER_SIZE = 4356
|
56
56
|
|
57
|
+
# The application key. After authenticating to the remote server, this value is the session key for dialects less
|
58
|
+
# than version 3 and a unique value for v3 dialects. See:
|
59
|
+
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/901ae284-31d3-4ea1-ae8a-766fc8bfe00e
|
60
|
+
# @!attribute [rw] application_key
|
61
|
+
# @return [String]
|
62
|
+
attr_accessor :application_key
|
63
|
+
|
57
64
|
# The dispatcher responsible for sending packets
|
58
65
|
# @!attribute [rw] dispatcher
|
59
66
|
# @return [RubySMB::Dispatcher::Socket]
|
@@ -288,13 +295,19 @@ module RubySMB
|
|
288
295
|
attr_accessor :negotiated_smb_version
|
289
296
|
|
290
297
|
# Whether or not the server supports multi-credit operations. It is
|
291
|
-
# reported by the LARGE_MTU
|
298
|
+
# reported by the LARGE_MTU capability as part of the negotiation process
|
292
299
|
# (SMB 2.x and 3.x).
|
293
300
|
# @!attribute [rw] server_supports_multi_credit
|
294
301
|
# @return [Boolean] true if the server supports multi-credit operations,
|
295
302
|
# false otherwise
|
296
303
|
attr_accessor :server_supports_multi_credit
|
297
304
|
|
305
|
+
# The negotiated security buffer. This is nil until the negotiation process
|
306
|
+
# has finished.
|
307
|
+
# @!attribute [rw] negotiation_security_buffer
|
308
|
+
# @return [String] The raw security buffer bytes
|
309
|
+
attr_accessor :negotiation_security_buffer
|
310
|
+
|
298
311
|
# @param dispatcher [RubySMB::Dispatcher::Socket] the packet dispatcher to use
|
299
312
|
# @param smb1 [Boolean] whether or not to enable SMB1 support
|
300
313
|
# @param smb2 [Boolean] whether or not to enable SMB2 support
|
@@ -313,6 +326,7 @@ module RubySMB
|
|
313
326
|
@sequence_counter = 0
|
314
327
|
@session_id = 0x00
|
315
328
|
@session_key = ''
|
329
|
+
@application_key = ''
|
316
330
|
@session_is_guest = false
|
317
331
|
@signing_required = false
|
318
332
|
@smb1 = smb1
|
@@ -332,7 +346,7 @@ module RubySMB
|
|
332
346
|
# session setup response is received
|
333
347
|
@session_encrypt_data = always_encrypt
|
334
348
|
|
335
|
-
@ntlm_client =
|
349
|
+
@ntlm_client = RubySMB::NTLM::Client.new(
|
336
350
|
@username,
|
337
351
|
@password,
|
338
352
|
workstation: @local_workstation,
|
@@ -404,7 +418,7 @@ module RubySMB
|
|
404
418
|
@password = pass.encode('utf-8') || ''.encode('utf-8')
|
405
419
|
@username = user.encode('utf-8') || ''.encode('utf-8')
|
406
420
|
|
407
|
-
@ntlm_client =
|
421
|
+
@ntlm_client = RubySMB::NTLM::Client.new(
|
408
422
|
@username,
|
409
423
|
@password,
|
410
424
|
workstation: @local_workstation,
|
@@ -617,6 +631,7 @@ module RubySMB
|
|
617
631
|
def wipe_state!
|
618
632
|
self.session_id = 0x00
|
619
633
|
self.user_id = 0x00
|
634
|
+
self.application_key = ''
|
620
635
|
self.session_key = ''
|
621
636
|
self.session_is_guest = false
|
622
637
|
self.sequence_counter = 0
|
@@ -13,6 +13,19 @@ module RubySMB
|
|
13
13
|
# Raised when an invalid packet is received
|
14
14
|
class InvalidPacket < DcerpcError; end
|
15
15
|
|
16
|
+
# Raised when a fault response is received
|
17
|
+
class FaultError < InvalidPacket
|
18
|
+
attr_reader :status_code
|
19
|
+
def initialize(message=nil, status:)
|
20
|
+
@status_code = status
|
21
|
+
super(message)
|
22
|
+
end
|
23
|
+
|
24
|
+
def status_name
|
25
|
+
RubySMB::Dcerpc::Fault::Status.name(@status_code)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
16
29
|
# Raised when an error is returned during a Winreg operation
|
17
30
|
class WinregError < DcerpcError; end
|
18
31
|
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module RubySMB::Dcerpc::Fault
|
2
|
+
module Status
|
3
|
+
# DCERPC
|
4
|
+
NCA_S_FAULT_OTHER = 0x00000001
|
5
|
+
NCA_S_FAULT_ACCESS_DENIED = 0x00000005
|
6
|
+
NCA_S_FAULT_NDR = 0x000006F7
|
7
|
+
NCA_S_FAULT_CANT_PERFORM = 0x000006D8
|
8
|
+
NCA_S_FAULT_INT_DIV_BY_ZERO = 0x1C000001
|
9
|
+
NCA_S_FAULT_ADDR_ERROR = 0x1C000002
|
10
|
+
NCA_S_FAULT_FP_DIV_ZERO = 0x1C000003
|
11
|
+
NCA_S_FAULT_FP_UNDERFLOW = 0x1C000004
|
12
|
+
NCA_S_FAULT_FP_OVERFLOW = 0x1C000005
|
13
|
+
NCA_S_FAULT_INVALID_TAG = 0x1C000006
|
14
|
+
NCA_S_FAULT_INVALID_BOUND = 0x1C000007
|
15
|
+
NCA_RPC_VERSION_MISMATCH = 0x1C000008
|
16
|
+
NCA_UNSPEC_REJECT = 0x1C000009
|
17
|
+
NCA_S_BAD_ACTID = 0x1C00000A
|
18
|
+
NCA_WHO_ARE_YOU_FAILED = 0x1C00000B
|
19
|
+
NCA_MANAGER_NOT_ENTERED = 0x1C00000C
|
20
|
+
NCA_S_FAULT_CANCEL = 0x1C00000D
|
21
|
+
NCA_S_FAULT_ILL_INST = 0x1C00000E
|
22
|
+
NCA_S_FAULT_FP_ERROR = 0x1C00000F
|
23
|
+
NCA_S_FAULT_INT_OVERFLOW = 0x1C000010
|
24
|
+
NCA_S_FAULT_PIPE_EMPTY = 0x1C000014
|
25
|
+
NCA_S_FAULT_PIPE_CLOSED = 0x1C000015
|
26
|
+
NCA_S_FAULT_PIPE_ORDER = 0x1C000016
|
27
|
+
NCA_S_FAULT_PIPE_DISCIPLINE = 0x1C000017
|
28
|
+
NCA_S_FAULT_PIPE_COMM_ERROR = 0x1C000018
|
29
|
+
NCA_S_FAULT_PIPE_MEMORY = 0x1C000019
|
30
|
+
NCA_S_FAULT_CONTEXT_MISMATCH = 0x1C00001A
|
31
|
+
NCA_S_FAULT_REMOTE_NO_MEMORY = 0x1C00001B
|
32
|
+
NCA_INVALID_PRES_CONTEXT_ID = 0x1C00001C
|
33
|
+
NCA_UNSUPPORTED_AUTHN_LEVEL = 0x1C00001D
|
34
|
+
NCA_INVALID_CHECKSUM = 0x1C00001F
|
35
|
+
NCA_INVALID_CRC = 0x1C000020
|
36
|
+
NCS_S_FAULT_USER_DEFINED = 0x1C000021
|
37
|
+
NCA_S_FAULT_TX_OPEN_FAILED = 0x1C000022
|
38
|
+
NCA_S_FAULT_CODESET_CONV_ERROR = 0x1C000023
|
39
|
+
NCA_S_FAULT_OBJECT_NOT_FOUND = 0x1C000024
|
40
|
+
NCA_S_FAULT_NO_CLIENT_STUB = 0x1C000025
|
41
|
+
NCA_OP_RNG_ERROR = 0x1C010002
|
42
|
+
NCA_UNK_IF = 0x1C010003
|
43
|
+
NCA_WRONG_BOOT_TIME = 0x1C010006
|
44
|
+
NCA_S_YOU_CRASHED = 0x1C010009
|
45
|
+
NCA_PROTO_ERROR = 0x1C01000B
|
46
|
+
NCA_OUT_ARGS_TOO_BIG = 0x1C010013
|
47
|
+
NCA_SERVER_TOO_BUSY = 0x1C010014
|
48
|
+
NCA_UNSUPPORTED_TYPE = 0x1C010017
|
49
|
+
# Microsoft specific codes
|
50
|
+
E_NOTIMPL = 0x80004001
|
51
|
+
E_POINTER = 0x80004003
|
52
|
+
E_AOBRT = 0x80004004
|
53
|
+
E_UNEXPECTED = 0x8000FFFF
|
54
|
+
RPC_E_SERVERFAULT = 0x80010105
|
55
|
+
RPC_E_DISCONNECTED = 0x80010108
|
56
|
+
RPC_E_INVALID_IPID = 0x80010113
|
57
|
+
RPC_E_TIMEOUT = 0x8001011F
|
58
|
+
DISP_E_MEMBERNOTFOUND = 0x80020003
|
59
|
+
DISP_E_UNKNOWNNAME = 0x80020006
|
60
|
+
DISP_E_BADPARAMCOUNT = 0x8002000E
|
61
|
+
CBA_E_MALFORMED = 0x8004CB00
|
62
|
+
CBA_E_UNKNOWNOBJECT = 0x8004CB01
|
63
|
+
CBA_E_INVALIDID = 0x8004CB05
|
64
|
+
CBA_E_INVALIDCOOKIE = 0x8004CB09
|
65
|
+
CBA_E_QOSTYPEUNSUPPORTED = 0x8004CB0B
|
66
|
+
CBA_E_QOSVALUEUNSUPPORTED = 0x8004CB0C
|
67
|
+
CBA_E_NOTAPPLICABLE = 0x8004CB0F
|
68
|
+
CBA_E_LIMITVIOLATION = 0x8004CB12
|
69
|
+
CBA_E_QOSTYPENOTAPPLICABLE = 0x8004CB13
|
70
|
+
CBA_E_OUTOFPARTNERACCOS = 0x8004CB18
|
71
|
+
CBA_E_FLAGUNSUPPORTED = 0x8004CB1C
|
72
|
+
CBA_E_FRAMECOUNTUNSUPPORTED = 0x8004CB23
|
73
|
+
CBA_E_MODECHANGE = 0x8004CB25
|
74
|
+
E_OUTOFMEMORY = 0x8007000E
|
75
|
+
E_INVALIDARG = 0x80070057
|
76
|
+
RPC_S_PROCNUM_OUT_OF_RANGE = 0x800706D1
|
77
|
+
OR_INVALID_OXID = 0x80070776
|
78
|
+
|
79
|
+
def self.name(value)
|
80
|
+
constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/ruby_smb/dcerpc/ndr.rb
CHANGED
@@ -312,29 +312,31 @@ module RubySMB::Dcerpc::Ndr
|
|
312
312
|
def initialize_instance
|
313
313
|
@read_until_index = 0
|
314
314
|
@max_count = 0
|
315
|
+
@max_count_set = false
|
315
316
|
super
|
316
317
|
end
|
317
318
|
|
318
319
|
def insert(index, *objs)
|
319
320
|
obj = super
|
320
|
-
@max_count = length
|
321
|
+
@max_count = length unless @max_count_set
|
321
322
|
obj
|
322
323
|
end
|
323
324
|
|
324
325
|
def slice_index(index)
|
325
326
|
obj = super
|
326
|
-
@max_count = length
|
327
|
+
@max_count = length unless @max_count_set
|
327
328
|
obj
|
328
329
|
end
|
329
330
|
|
330
331
|
def []=(index, value)
|
331
332
|
obj = super
|
332
|
-
@max_count = length
|
333
|
+
@max_count = length unless @max_count_set
|
333
334
|
obj
|
334
335
|
end
|
335
336
|
|
336
337
|
def set_max_count(val)
|
337
338
|
@max_count = @read_until_index = val
|
339
|
+
@max_count_set = true
|
338
340
|
end
|
339
341
|
end
|
340
342
|
|
@@ -650,7 +652,7 @@ module RubySMB::Dcerpc::Ndr
|
|
650
652
|
include ConstructedTypePlugin
|
651
653
|
|
652
654
|
def should_process_max_count?
|
653
|
-
# According to the NDR
|
655
|
+
# According to the NDR definition for Structures Containing a Conformant
|
654
656
|
# Array:
|
655
657
|
#
|
656
658
|
# "In the NDR representation of a structure that contains a
|
@@ -761,13 +763,13 @@ module RubySMB::Dcerpc::Ndr
|
|
761
763
|
return (4 - (rel_offset % 4)) % 4
|
762
764
|
end
|
763
765
|
if obj.is_a?(ConfPlugin)
|
764
|
-
# `max_count` should have been handled at the
|
766
|
+
# `max_count` should have been handled at the beginning of the structure
|
765
767
|
# already. We need to fix `rel_offset` since it includes the
|
766
768
|
# `max_count` 4 bytes, plus the possible padding bytes needed to align
|
767
769
|
# the structure. This is required because BinData Struct is not
|
768
|
-
# aware of `max_count` and
|
770
|
+
# aware of `max_count` and consider the first field to be the beginning
|
769
771
|
# of the structure instead. We have to make sure the alignment is
|
770
|
-
# calculated from the
|
772
|
+
# calculated from the beginning of the structure.
|
771
773
|
align = eval_parameter(:byte_align)
|
772
774
|
pad_length = (align - (4 % align)) % align
|
773
775
|
rel_offset += (4 + pad_length)
|
@@ -776,7 +778,7 @@ module RubySMB::Dcerpc::Ndr
|
|
776
778
|
# (not Varying). The size information (max_count) has been place in
|
777
779
|
# from of the structure and no other size information is present before
|
778
780
|
# the actual elements of the array. Therefore, the alignment must be
|
779
|
-
# done
|
781
|
+
# done according to the rules of the elements. Since a NdrArray has its
|
780
782
|
# default :byte_align value set to 4 (:max_count size), we have to make
|
781
783
|
# sure the element size is used instead.
|
782
784
|
unless obj.is_a?(VarPlugin)
|
@@ -838,6 +840,10 @@ module RubySMB::Dcerpc::Ndr
|
|
838
840
|
end
|
839
841
|
end
|
840
842
|
|
843
|
+
#
|
844
|
+
# [Unions](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_08)
|
845
|
+
#
|
846
|
+
|
841
847
|
# TODO: Unions
|
842
848
|
# TODO: Pipes
|
843
849
|
|
@@ -1207,6 +1213,11 @@ module RubySMB::Dcerpc::Ndr
|
|
1207
1213
|
extend PointerClassPlugin
|
1208
1214
|
end
|
1209
1215
|
|
1216
|
+
class NdrUint16ArrayPtr < NdrConfVarArray
|
1217
|
+
default_parameters type: :ndr_uint16
|
1218
|
+
extend PointerClassPlugin
|
1219
|
+
end
|
1220
|
+
|
1210
1221
|
class NdrFileTimePtr < NdrFileTime
|
1211
1222
|
extend PointerClassPlugin
|
1212
1223
|
end
|
@@ -60,16 +60,21 @@ module RubySMB
|
|
60
60
|
string :default
|
61
61
|
end
|
62
62
|
choice 'Samr', selection: -> { opnum } do
|
63
|
-
samr_connect_request
|
64
|
-
samr_lookup_domain_in_sam_server_request
|
65
|
-
samr_open_domain_request
|
66
|
-
samr_enumerate_users_in_domain_request
|
67
|
-
samr_rid_to_sid_request
|
68
|
-
samr_close_handle_request
|
69
|
-
samr_get_alias_membership_request
|
70
|
-
samr_open_user_request
|
71
|
-
samr_get_groups_for_user_request
|
72
|
-
|
63
|
+
samr_connect_request Samr::SAMR_CONNECT
|
64
|
+
samr_lookup_domain_in_sam_server_request Samr::SAMR_LOOKUP_DOMAIN_IN_SAM_SERVER
|
65
|
+
samr_open_domain_request Samr::SAMR_OPEN_DOMAIN
|
66
|
+
samr_enumerate_users_in_domain_request Samr::SAMR_ENUMERATE_USERS_IN_DOMAIN
|
67
|
+
samr_rid_to_sid_request Samr::SAMR_RID_TO_SID
|
68
|
+
samr_close_handle_request Samr::SAMR_CLOSE_HANDLE
|
69
|
+
samr_get_alias_membership_request Samr::SAMR_GET_ALIAS_MEMBERSHIP
|
70
|
+
samr_open_user_request Samr::SAMR_OPEN_USER
|
71
|
+
samr_get_groups_for_user_request Samr::SAMR_GET_GROUPS_FOR_USER
|
72
|
+
samr_enumerate_domains_in_sam_server_request Samr::SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER
|
73
|
+
samr_lookup_names_in_domain_request Samr::SAMR_LOOKUP_NAMES_IN_DOMAIN
|
74
|
+
samr_create_user2_in_domain_request Samr::SAMR_CREATE_USER2_IN_DOMAIN
|
75
|
+
samr_set_information_user2_request Samr::SAMR_SET_INFORMATION_USER2
|
76
|
+
samr_delete_user_request Samr::SAMR_DELETE_USER
|
77
|
+
string :default
|
73
78
|
end
|
74
79
|
choice 'Wkssvc', selection: -> { opnum } do
|
75
80
|
netr_wksta_get_info_request Wkssvc::NETR_WKSTA_GET_INFO
|
@@ -109,6 +109,11 @@ module RubySMB
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
+
class RpcUnicodeStringConfVarArray < Ndr::NdrConfVarArray
|
113
|
+
extend Ndr::ArrayClassPlugin
|
114
|
+
default_parameters type: :rpc_unicode_string
|
115
|
+
end
|
116
|
+
|
112
117
|
# A pointer to a RPC_UNICODE_STRING structure
|
113
118
|
class PrpcUnicodeString < RpcUnicodeString
|
114
119
|
extend Ndr::PointerClassPlugin
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Samr
|
4
|
+
|
5
|
+
# [3.1.5.4.4 SamrCreateUser2InDomain (Opnum 50)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/a98d7fbb-1735-4fbf-b41a-ef363c899002)
|
6
|
+
class SamrCreateUser2InDomainRequest < BinData::Record
|
7
|
+
attr_reader :opnum
|
8
|
+
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
sampr_handle :domain_handle
|
12
|
+
rpc_unicode_string :name
|
13
|
+
ndr_uint32 :account_type
|
14
|
+
ndr_uint32 :desired_access
|
15
|
+
|
16
|
+
def initialize_instance
|
17
|
+
super
|
18
|
+
@opnum = SAMR_CREATE_USER2_IN_DOMAIN
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Samr
|
4
|
+
|
5
|
+
# [3.1.5.4.4 SamrCreateUser2InDomain (Opnum 50)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/a98d7fbb-1735-4fbf-b41a-ef363c899002)
|
6
|
+
class SamrCreateUser2InDomainResponse < BinData::Record
|
7
|
+
attr_reader :opnum
|
8
|
+
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
sampr_handle :user_handle
|
12
|
+
ndr_uint32 :granted_access
|
13
|
+
ndr_uint32 :relative_id
|
14
|
+
ndr_uint32 :error_status
|
15
|
+
|
16
|
+
def initialize_instance
|
17
|
+
super
|
18
|
+
@opnum = SAMR_CREATE_USER2_IN_DOMAIN
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|