ruby_smb 3.0.5 → 3.1.1
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 +2 -3
- data/.github/workflows/verify.yml +1 -1
- data/.simplecov +1 -1
- data/CONTRIBUTING.md +28 -3
- data/README.md +8 -0
- data/examples/pwsh_service.rb +112 -0
- data/lib/ruby_smb/client/encryption.rb +16 -4
- data/lib/ruby_smb/client/negotiation.rb +10 -8
- data/lib/ruby_smb/dcerpc/request.rb +2 -0
- data/lib/ruby_smb/dcerpc/svcctl/create_service_w_request.rb +35 -0
- data/lib/ruby_smb/dcerpc/svcctl/create_service_w_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/svcctl/delete_service_request.rb +21 -0
- data/lib/ruby_smb/dcerpc/svcctl/delete_service_response.rb +21 -0
- data/lib/ruby_smb/dcerpc/svcctl.rb +66 -5
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/regsam.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg.rb +1 -1
- data/lib/ruby_smb/fscc/file_information.rb +4 -0
- data/lib/ruby_smb/gss/provider/ntlm.rb +4 -0
- data/lib/ruby_smb/server/server_client/encryption.rb +66 -0
- data/lib/ruby_smb/server/server_client/negotiation.rb +14 -3
- data/lib/ruby_smb/server/server_client/session_setup.rb +18 -3
- data/lib/ruby_smb/server/server_client/share_io.rb +17 -0
- data/lib/ruby_smb/server/server_client/tree_connect.rb +40 -3
- data/lib/ruby_smb/server/server_client.rb +147 -37
- data/lib/ruby_smb/server/share/provider/disk/file_system.rb +28 -0
- data/lib/ruby_smb/server/share/provider/disk/processor/close.rb +42 -0
- data/lib/ruby_smb/server/share/provider/disk/processor/create.rb +143 -0
- data/lib/ruby_smb/server/share/provider/disk/processor/query.rb +359 -0
- data/lib/ruby_smb/server/share/provider/disk/processor/read.rb +69 -0
- data/lib/ruby_smb/server/share/provider/disk/processor.rb +159 -0
- data/lib/ruby_smb/server/share/provider/disk.rb +4 -416
- data/lib/ruby_smb/server/share/provider/pipe.rb +2 -2
- data/lib/ruby_smb/server/share/provider/processor.rb +16 -0
- data/lib/ruby_smb/signing.rb +18 -4
- data/lib/ruby_smb/smb1/bit_field/directory_access_mask.rb +1 -1
- data/lib/ruby_smb/smb1/bit_field/file_access_mask.rb +1 -1
- data/lib/ruby_smb/smb1/commands.rb +1 -0
- data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +11 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/read_andx_response.rb +5 -4
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +12 -4
- data/lib/ruby_smb/smb1/packet/trans2/data_block.rb +9 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +52 -51
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +37 -37
- data/lib/ruby_smb/smb1/packet/trans2/find_information_level/find_file_both_directory_info.rb +48 -0
- data/lib/ruby_smb/smb1/packet/trans2/find_information_level.rb +28 -15
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +51 -51
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +36 -36
- data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +40 -39
- data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +40 -40
- data/lib/ruby_smb/smb1/packet/trans2/query_file_information_request.rb +60 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_file_information_response.rb +59 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_level/query_fs_attribute_info.rb +31 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_level.rb +40 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_request.rb +46 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_response.rb +59 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_information_level/query_file_basic_info.rb +23 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_information_level/query_file_standard_info.rb +22 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_information_level.rb +62 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_path_information_request.rb +65 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_path_information_response.rb +59 -0
- data/lib/ruby_smb/smb1/packet/trans2/request.rb +24 -8
- data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +4 -4
- data/lib/ruby_smb/smb1/packet/trans2/response.rb +29 -20
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +42 -42
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +23 -23
- data/lib/ruby_smb/smb1/packet/trans2/subcommands.rb +23 -5
- data/lib/ruby_smb/smb1/packet/trans2.rb +4 -0
- data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +4 -1
- data/lib/ruby_smb/smb2/bit_field/directory_access_mask.rb +1 -1
- data/lib/ruby_smb/smb2/bit_field/file_access_mask.rb +1 -1
- data/lib/ruby_smb/smb2/negotiate_context.rb +10 -1
- data/lib/ruby_smb/smb2/packet/transform_header.rb +7 -7
- data/lib/ruby_smb/smb2.rb +1 -0
- data/lib/ruby_smb/version.rb +1 -1
- data/ruby_smb.gemspec +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +31 -8
- data/spec/lib/ruby_smb/dcerpc/svcctl/create_service_w_request_spec.rb +143 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/create_service_w_response_spec.rb +45 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/delete_service_request_spec.rb +29 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/delete_service_response_spec.rb +29 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +8 -8
- data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +1 -1
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +1 -1
- data/spec/lib/ruby_smb/smb1/bit_field/directory_access_mask_spec.rb +4 -4
- data/spec/lib/ruby_smb/smb1/bit_field/file_access_mask_spec.rb +4 -4
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +36 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +35 -1
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_file_information_request_spec.rb +74 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_file_information_response_spec.rb +96 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_fs_information_request_spec.rb +62 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_fs_information_response_spec.rb +88 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_path_information_request_spec.rb +79 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_path_information_response_spec.rb +96 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/response_spec.rb +3 -3
- data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_request_spec.rb +3 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +7 -2
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +3 -3
- data/spec/lib/ruby_smb/smb2/bit_field/directory_access_mask_spec.rb +4 -4
- data/spec/lib/ruby_smb/smb2/bit_field/file_access_mask_spec.rb +4 -4
- data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +2 -2
- data/spec/spec_helper.rb +2 -3
- data.tar.gz.sig +0 -0
- metadata +48 -4
- metadata.gz.sig +1 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5567054608b80da9be641c717d1d8b69e71452fb2fc67c2e390d9a6a34fb19d3
|
4
|
+
data.tar.gz: 0b439033b20bd578414e51d56d98ee4ccc4d44c60e6248bf0b8ad37a33b0fd6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19ad76e3ed2e1902141aac772cd7908de06b402414b787e7f2371584e5cee6fc1cf82e9b80dce10ae0599082ee65a4dbd4f6c62a3c32bd3b154d47de25944b20
|
7
|
+
data.tar.gz: dd970aad2f91aec70671b7364438ee7df79908c552a6826aad99318e10fb63b6ebcad5929f131fa4284ff5531c189061a83f499203188ac919dc74e6007d422f
|
checksums.yaml.gz.sig
CHANGED
@@ -1,3 +1,2 @@
|
|
1
|
-
����
|
2
|
-
|
3
|
-
u��_A7��*�L)�X��N��c
|
1
|
+
��Gӹ������=�|)�і��c�澫W^P��&d����Lx�<����L��A 8���y(�Չ���2+�\O,�S�o�Gɲ�)����5ו�����L�(@���Ea���ܴo��f� d!��4zn�2t�_��%9��[�?Y!�*Bﳯ���Wa���nO�c�Oz �`x�Z|n"V��d,���H���@�����V��
|
2
|
+
�|�A�C���\�~ۀ.�T)? ~\G��kG��z��� ����}'>
|
data/.simplecov
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# controlled by running with coverage, so don't explicitly start coverage (and
|
3
3
|
# therefore generate a report) when in Rubymine. This _will_ generate a report
|
4
4
|
# whenever `rake spec` is run.
|
5
|
-
unless ENV['RM_INFO']
|
5
|
+
unless ENV['RM_INFO'] || SimpleCov.running
|
6
6
|
SimpleCov.start
|
7
7
|
end
|
8
8
|
|
data/CONTRIBUTING.md
CHANGED
@@ -1,8 +1,33 @@
|
|
1
1
|
# Contributing
|
2
2
|
|
3
|
+
## Versioning
|
4
|
+
|
5
|
+
RubySMB attempts to follow [the Semantic Versioning 2.0.0](https://semver.org/) standard, however due to certain key architectural qualities, some reservations are made regarding what will trigger major and minor version bumps. A large component of RubySMB are the data definitions written using [BinData](https://github.com/dmendel/bindata). These definitions are implemented in an incremental fashion, causing some to be missing while others are only partially implemented (particularly in cases where fields are dependent on other types that may or may not be defined). Because of this, the RubySMB project reserves the right to update these definitions without incrementing the major version. In most cases, the definitions and their fields do not need to be used to perform standard operations such as connecting to, authenticating to and reading a file from a remote SMB server. The API provided by non-BinData objects will remain stable across minor versions, with backwards incompatible changes triggering a major version bump. Backwards incompatible changes to data structure definitions in BinData will cause the RubySMB maintianers to perform a minor version bump. These changes include but are not limited to:
|
6
|
+
|
7
|
+
* Changes to the class name due to how BinData's internal registration works
|
8
|
+
* Their field names which may be updated to avoid conflicts
|
9
|
+
* Their field types which may be extended with more functionality or switched from integers to full-flag based definitions
|
10
|
+
|
11
|
+
### Usage Examples
|
12
|
+
The following two examples show dangerous operations that may not be stable across minor version bumps, and safe operations.
|
13
|
+
|
14
|
+
```
|
15
|
+
# dangerous operation, the BinData object name (TransformHeader) may change in the future
|
16
|
+
header = RubySMB::SMB2::Packet::TransformHeader.read(raw_request)
|
17
|
+
# also dangerous operation, the field name may change in the future
|
18
|
+
header.flags = 0
|
19
|
+
```
|
20
|
+
|
21
|
+
```
|
22
|
+
# safe operation, the API exposed on the Client won't change without a major version bump
|
23
|
+
client = RubySMB::Client.new(dispatcher, smb1: options[:smbv1], smb2: options[:smbv2], smb3: options[:smbv3], username: options[:username], password: options[:password], domain: options[:domain])
|
24
|
+
# safe operation, methods on non-BinData objects won't change without a major version bump
|
25
|
+
protocol = client.negotiate
|
26
|
+
```
|
27
|
+
|
3
28
|
## Forking
|
4
29
|
|
5
|
-
[Fork this repository](https://github.com/rapid7/
|
30
|
+
[Fork this repository](https://github.com/rapid7/ruby_smb/fork)
|
6
31
|
|
7
32
|
## Branching
|
8
33
|
|
@@ -14,7 +39,7 @@ Branch names follow the format `TYPE/ISSUE/SUMMARY`. You can create it with `gi
|
|
14
39
|
|
15
40
|
### `ISSUE`
|
16
41
|
|
17
|
-
`ISSUE` is either a [Github issue](https://github.com/rapid7/
|
42
|
+
`ISSUE` is either a [Github issue](https://github.com/rapid7/ruby_smb/issues) or an issue from some other
|
18
43
|
issue tracking software.
|
19
44
|
|
20
45
|
### `SUMMARY`
|
@@ -49,7 +74,7 @@ Push your branch to your fork on gitub: `git push TYPE/ISSUE/SUMMARY`
|
|
49
74
|
|
50
75
|
### Pull Request
|
51
76
|
|
52
|
-
* [Create new Pull Request](https://github.com/rapid7/
|
77
|
+
* [Create new Pull Request](https://github.com/rapid7/ruby_smb/compare/)
|
53
78
|
* Add a Verification Steps to the description comment
|
54
79
|
|
55
80
|
```
|
data/README.md
CHANGED
@@ -12,6 +12,12 @@ The RubySMB library provides client-level and packet-level support for the proto
|
|
12
12
|
|
13
13
|
See the Wiki for more information on this project's long-term goals, style guide, and developer tips.
|
14
14
|
|
15
|
+
|
16
|
+
## Versioning
|
17
|
+
RubySMB attempts to follow [the Semantic Versioning 2.0.0](https://semver.org/) standard, however due to certain key architectural qualities, some reservations are made regarding what will trigger major and minor version bumps. Upstream consumers utilizing higher-level SMB operations can lock to minor (`~= 3.0`) versions however consumers utilizing raw SMB packet manipulation are recommended to set restrictions for compatibility to allow update resolution for patch versions (`~= 3.0.0`).
|
18
|
+
|
19
|
+
|
20
|
+
|
15
21
|
## Installation
|
16
22
|
|
17
23
|
Add this line to your application's Gemfile:
|
@@ -34,6 +40,8 @@ Or install it yourself as:
|
|
34
40
|
|
35
41
|
All packets are implemented in a declarative style with BinData. Nested data structures are used where appropriate to give users an easy method of manipulating individual fields inside of a packet.
|
36
42
|
|
43
|
+
Note that these packets are defined using [BinData](https://github.com/dmendel/bindata) and that their struction is subject to change across minor version bumps. See [CONTRIBUTING.md](https://github.com/rapid7/ruby_smb/blob/master/CONTRIBUTING.md#versioning) for more information.
|
44
|
+
|
37
45
|
#### SMB1
|
38
46
|
|
39
47
|
SMB1 Packets are made up of three basic components:
|
@@ -0,0 +1,112 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# This example script is used for launching a powershell command as a service on a host.
|
4
|
+
# It will attempt to connect to a host and create a new service to launch the command using powershell.exe.
|
5
|
+
# Example usage: ruby pwsh_service.rb --username msfadmin --password msfadmin 192.168.172.138 "echo test > C:\\Users\\User\\Desktop\\test.txt"
|
6
|
+
# This will try to connect to \\192.168.172.138 with the msfadmin:msfadmin credentials and create a new service to run the powershell command.
|
7
|
+
|
8
|
+
def random_string(length)
|
9
|
+
return (1..length).map { (('a'..'z').to_a + ('A'..'Z').to_a)[rand(26*2)] }.join
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'bundler/setup'
|
13
|
+
require 'optparse'
|
14
|
+
require 'ruby_smb'
|
15
|
+
|
16
|
+
args = ARGV.dup
|
17
|
+
options = {
|
18
|
+
domain: '.',
|
19
|
+
username: '',
|
20
|
+
password: '',
|
21
|
+
command: nil,
|
22
|
+
smbv1: true,
|
23
|
+
smbv2: true,
|
24
|
+
smbv3: true,
|
25
|
+
target: nil
|
26
|
+
}
|
27
|
+
options[:command] = args.pop
|
28
|
+
options[:target] = args.pop
|
29
|
+
optparser = OptionParser.new do |opts|
|
30
|
+
opts.banner = "Usage: #{File.basename(__FILE__)} [options] target command"
|
31
|
+
opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
|
32
|
+
options[:smbv1] = smbv1
|
33
|
+
end
|
34
|
+
opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
|
35
|
+
options[:smbv2] = smbv2
|
36
|
+
end
|
37
|
+
opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
|
38
|
+
options[:smbv3] = smbv3
|
39
|
+
end
|
40
|
+
opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
|
41
|
+
if username.include?('\\')
|
42
|
+
options[:domain], options[:username] = username.split('\\', 2)
|
43
|
+
else
|
44
|
+
options[:username] = username
|
45
|
+
end
|
46
|
+
end
|
47
|
+
opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
|
48
|
+
options[:password] = password
|
49
|
+
end
|
50
|
+
end
|
51
|
+
optparser.parse!(args)
|
52
|
+
|
53
|
+
if options[:target].nil? || options[:command].nil?
|
54
|
+
abort(optparser.help)
|
55
|
+
end
|
56
|
+
|
57
|
+
sock = TCPSocket.new options[:target], 445
|
58
|
+
dispatcher = RubySMB::Dispatcher::Socket.new(sock, read_timeout: 60)
|
59
|
+
|
60
|
+
client = RubySMB::Client.new(dispatcher, smb1: options[:smbv1], smb2: options[:smbv2], smb3: options[:smbv3], username: options[:username], password: options[:password], domain: options[:domain])
|
61
|
+
protocol = client.negotiate
|
62
|
+
status = client.authenticate
|
63
|
+
|
64
|
+
puts "#{protocol} : #{status}"
|
65
|
+
|
66
|
+
tree = client.tree_connect("\\\\#{options[:target]}\\IPC$")
|
67
|
+
svcctl = tree.open_file(filename: 'svcctl', write: true, read: true)
|
68
|
+
|
69
|
+
puts('Binding to \\svcctl...')
|
70
|
+
svcctl.bind(endpoint: RubySMB::Dcerpc::Svcctl)
|
71
|
+
puts('Bound to \\svcctl')
|
72
|
+
|
73
|
+
puts('Opening Service Control Manager')
|
74
|
+
scm_handle = svcctl.open_sc_manager_w(options[:target])
|
75
|
+
|
76
|
+
service_name = random_string(8)
|
77
|
+
display_name = random_string(8)
|
78
|
+
binary_path_name = "%COMSPEC% /c start /b /min powershell.exe -nop -win hid -noni -en #{[options[:command].encode("UTF-16LE")].pack("m0")}"
|
79
|
+
puts "Full Command: #{binary_path_name}"
|
80
|
+
svc_handle = svcctl.create_service_w(scm_handle, service_name, display_name, binary_path_name)
|
81
|
+
|
82
|
+
puts('Created new service')
|
83
|
+
|
84
|
+
svcctl.close_service_handle(svc_handle)
|
85
|
+
|
86
|
+
puts('Opening the service')
|
87
|
+
|
88
|
+
svc_handle = svcctl.open_service_w(scm_handle, service_name)
|
89
|
+
|
90
|
+
puts('Starting the service')
|
91
|
+
|
92
|
+
begin
|
93
|
+
svcctl.start_service_w(svc_handle)
|
94
|
+
rescue RubySMB::Dcerpc::Error::SvcctlError => e
|
95
|
+
if e.message.include?('ERROR_SERVICE_REQUEST_TIMEOUT')
|
96
|
+
puts "Service start timed out, OK if running a command or non-service executable..."
|
97
|
+
else
|
98
|
+
puts "Service start error: #{e}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
puts('Deleting the service')
|
103
|
+
|
104
|
+
svcctl.delete_service(svc_handle)
|
105
|
+
|
106
|
+
puts('Closing Service Control Manager')
|
107
|
+
|
108
|
+
svcctl.close_service_handle(scm_handle)
|
109
|
+
|
110
|
+
puts('Done')
|
111
|
+
|
112
|
+
client.disconnect!
|
@@ -4,18 +4,24 @@ module RubySMB
|
|
4
4
|
module Encryption
|
5
5
|
def smb3_encrypt(data)
|
6
6
|
unless @client_encryption_key
|
7
|
+
raise RubySMB::Error::EncryptionError.new('The encryption algorithm has not been set') if @encryption_algorithm.nil?
|
8
|
+
|
9
|
+
key_bit_len = OpenSSL::Cipher.new(@encryption_algorithm).key_len * 8
|
10
|
+
|
7
11
|
case @dialect
|
8
12
|
when '0x0300', '0x0302'
|
9
13
|
@client_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
10
14
|
@session_key,
|
11
15
|
"SMB2AESCCM\x00",
|
12
|
-
"ServerIn \x00"
|
16
|
+
"ServerIn \x00",
|
17
|
+
length: key_bit_len
|
13
18
|
)
|
14
19
|
when '0x0311'
|
15
20
|
@client_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
16
21
|
@session_key,
|
17
22
|
"SMBC2SCipherKey\x00",
|
18
|
-
@preauth_integrity_hash_value
|
23
|
+
@preauth_integrity_hash_value,
|
24
|
+
length: key_bit_len
|
19
25
|
)
|
20
26
|
else
|
21
27
|
raise RubySMB::Error::EncryptionError.new('Dialect is incompatible with SMBv3 encryption')
|
@@ -33,18 +39,24 @@ module RubySMB
|
|
33
39
|
|
34
40
|
def smb3_decrypt(th)
|
35
41
|
unless @server_encryption_key
|
42
|
+
raise RubySMB::Error::EncryptionError.new('The encryption algorithm has not been set') if @encryption_algorithm.nil?
|
43
|
+
|
44
|
+
key_bit_len = OpenSSL::Cipher.new(@encryption_algorithm).key_len * 8
|
45
|
+
|
36
46
|
case @dialect
|
37
47
|
when '0x0300', '0x0302'
|
38
48
|
@server_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
39
49
|
@session_key,
|
40
50
|
"SMB2AESCCM\x00",
|
41
|
-
"ServerOut\x00"
|
51
|
+
"ServerOut\x00",
|
52
|
+
length: key_bit_len
|
42
53
|
)
|
43
54
|
when '0x0311'
|
44
55
|
@server_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
45
56
|
@session_key,
|
46
57
|
"SMBS2CCipherKey\x00",
|
47
|
-
@preauth_integrity_hash_value
|
58
|
+
@preauth_integrity_hash_value,
|
59
|
+
length: key_bit_len
|
48
60
|
)
|
49
61
|
else
|
50
62
|
raise RubySMB::Error::EncryptionError.new('Dialect is incompatible with SMBv3 decryption')
|
@@ -5,7 +5,7 @@ module RubySMB
|
|
5
5
|
module Negotiation
|
6
6
|
# Handles the entire SMB Multi-Protocol Negotiation from the
|
7
7
|
# Client to the Server. It sets state on the client appropriate
|
8
|
-
# to the protocol and
|
8
|
+
# to the protocol and capabilities negotiated during the exchange.
|
9
9
|
# It also keeps track of the negotiated dialect.
|
10
10
|
#
|
11
11
|
# @return [void]
|
@@ -23,13 +23,13 @@ module RubySMB
|
|
23
23
|
update_preauth_hash(response_packet)
|
24
24
|
end
|
25
25
|
|
26
|
-
# If the response contains
|
27
|
-
# it indicates that the server
|
28
|
-
#
|
29
|
-
# request to negotiate the actual SMB 2 Protocol revision to
|
30
|
-
# The wildcard revision number is sent only in response to a
|
26
|
+
# If the response contains an SMB2 dialect and the request was SMB1;
|
27
|
+
# it indicates that the server supports SMB2 and wants to upgrade the
|
28
|
+
# connection. The server expects the client to send a subsequent SMB2
|
29
|
+
# Negotiate request to negotiate the actual SMB 2 Protocol revision to
|
30
|
+
# be used. The wildcard revision number is sent only in response to a
|
31
31
|
# multi-protocol negotiate request with the "SMB 2.???" dialect string.
|
32
|
-
if @dialect ==
|
32
|
+
if request_packet.packet_smb_version == 'SMB1' && RubySMB::Dialect[@dialect]&.order == RubySMB::Dialect::ORDER_SMB2
|
33
33
|
self.smb2_message_id += 1
|
34
34
|
version = negotiate
|
35
35
|
end
|
@@ -265,8 +265,10 @@ module RubySMB
|
|
265
265
|
nc = RubySMB::SMB2::NegotiateContext.new(
|
266
266
|
context_type: RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
|
267
267
|
)
|
268
|
-
nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::
|
268
|
+
nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_256_GCM
|
269
|
+
nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_256_CCM
|
269
270
|
nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM
|
271
|
+
nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM
|
270
272
|
packet.add_negotiate_context(nc)
|
271
273
|
|
272
274
|
nc = RubySMB::SMB2::NegotiateContext.new(
|
@@ -48,6 +48,7 @@ module RubySMB
|
|
48
48
|
end
|
49
49
|
choice 'Svcctl', selection: -> { opnum } do
|
50
50
|
open_sc_manager_w_request Svcctl::OPEN_SC_MANAGER_W
|
51
|
+
create_service_w_request Svcctl::CREATE_SERVICE_W
|
51
52
|
open_service_w_request Svcctl::OPEN_SERVICE_W
|
52
53
|
query_service_status_request Svcctl::QUERY_SERVICE_STATUS
|
53
54
|
query_service_config_w_request Svcctl::QUERY_SERVICE_CONFIG_W
|
@@ -55,6 +56,7 @@ module RubySMB
|
|
55
56
|
start_service_w_request Svcctl::START_SERVICE_W
|
56
57
|
control_service_request Svcctl::CONTROL_SERVICE
|
57
58
|
close_service_handle_request Svcctl::CLOSE_SERVICE_HANDLE
|
59
|
+
delete_service_request Svcctl::DELETE_SERVICE
|
58
60
|
string :default
|
59
61
|
end
|
60
62
|
choice 'Samr', selection: -> { opnum } do
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Svcctl
|
4
|
+
|
5
|
+
# [3.1.4.12 RCreateServiceW (Opnum 12)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/6a8ca926-9477-4dd4-b766-692fab07227e)
|
6
|
+
class CreateServiceWRequest < BinData::Record
|
7
|
+
attr_reader :opnum
|
8
|
+
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
sc_rpc_handle :h_sc_object
|
12
|
+
ndr_conf_var_wide_stringz :lp_service_name
|
13
|
+
ndr_wide_stringz_ptr :lp_display_name
|
14
|
+
ndr_uint32 :dw_desired_access
|
15
|
+
ndr_uint32 :dw_service_type
|
16
|
+
ndr_uint32 :dw_start_type
|
17
|
+
ndr_uint32 :dw_error_control
|
18
|
+
ndr_conf_var_wide_stringz :lp_binary_path_name
|
19
|
+
ndr_wide_stringz_ptr :lp_load_order_group
|
20
|
+
ndr_uint32_ptr :lp_dw_tag_id
|
21
|
+
svcctl_byte_array_ptr :lp_dependencies, type: :ndr_uint8
|
22
|
+
ndr_uint32 :dw_depend_size, initial_value: -> { lp_dependencies.size }
|
23
|
+
ndr_wide_stringz_ptr :lp_service_start_name
|
24
|
+
svcctl_byte_array_ptr :lp_password, type: :ndr_uint8
|
25
|
+
ndr_uint32 :dw_pw_size, initial_value: -> { lp_password.size }
|
26
|
+
|
27
|
+
def initialize_instance
|
28
|
+
super
|
29
|
+
@opnum = CREATE_SERVICE_W
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Svcctl
|
4
|
+
|
5
|
+
# [3.1.4.12 RCreateServiceW (Opnum 12)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/6a8ca926-9477-4dd4-b766-692fab07227e)
|
6
|
+
class CreateServiceWResponse < BinData::Record
|
7
|
+
attr_reader :opnum
|
8
|
+
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
ndr_uint32_ptr :lp_dw_tag_id
|
12
|
+
sc_rpc_handle :lp_sc_handle
|
13
|
+
ndr_uint32 :error_status
|
14
|
+
|
15
|
+
def initialize_instance
|
16
|
+
super
|
17
|
+
@opnum = CREATE_SERVICE_W
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Svcctl
|
4
|
+
|
5
|
+
# [3.1.4.2 RDeleteService (Opnum 2)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/6744cdb8-f162-4be0-bb31-98996b6495be)
|
6
|
+
class DeleteServiceRequest < BinData::Record
|
7
|
+
attr_reader :opnum
|
8
|
+
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
sc_rpc_handle :lp_sc_handle
|
12
|
+
|
13
|
+
def initialize_instance
|
14
|
+
super
|
15
|
+
@opnum = DELETE_SERVICE
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Svcctl
|
4
|
+
|
5
|
+
# [3.1.4.2 RDeleteService (Opnum 2)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/6744cdb8-f162-4be0-bb31-98996b6495be)
|
6
|
+
class DeleteServiceResponse < BinData::Record
|
7
|
+
attr_reader :opnum
|
8
|
+
|
9
|
+
endian :little
|
10
|
+
|
11
|
+
ndr_uint32 :error_status
|
12
|
+
|
13
|
+
def initialize_instance
|
14
|
+
super
|
15
|
+
@opnum = DELETE_SERVICE
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -9,8 +9,10 @@ module RubySMB
|
|
9
9
|
# Operation numbers
|
10
10
|
CLOSE_SERVICE_HANDLE = 0x0000
|
11
11
|
CONTROL_SERVICE = 0x0001
|
12
|
+
DELETE_SERVICE = 0x0002
|
12
13
|
QUERY_SERVICE_STATUS = 0x0006
|
13
14
|
CHANGE_SERVICE_CONFIG_W = 0x000B
|
15
|
+
CREATE_SERVICE_W = 0x000C
|
14
16
|
OPEN_SC_MANAGER_W = 0x000F
|
15
17
|
OPEN_SERVICE_W = 0x0010
|
16
18
|
QUERY_SERVICE_CONFIG_W = 0x0011
|
@@ -19,6 +21,10 @@ module RubySMB
|
|
19
21
|
|
20
22
|
class ScRpcHandle < Ndr::NdrContextHandle; end
|
21
23
|
|
24
|
+
class SvcctlByteArrayPtr < Ndr::NdrConfArray
|
25
|
+
default_parameters type: :ndr_uint8
|
26
|
+
extend Ndr::PointerClassPlugin
|
27
|
+
end
|
22
28
|
|
23
29
|
#################################
|
24
30
|
# Constants #
|
@@ -254,10 +260,14 @@ module RubySMB
|
|
254
260
|
require 'ruby_smb/dcerpc/svcctl/open_service_w_response'
|
255
261
|
require 'ruby_smb/dcerpc/svcctl/query_service_status_request'
|
256
262
|
require 'ruby_smb/dcerpc/svcctl/query_service_status_response'
|
263
|
+
require 'ruby_smb/dcerpc/svcctl/delete_service_request'
|
264
|
+
require 'ruby_smb/dcerpc/svcctl/delete_service_response'
|
257
265
|
require 'ruby_smb/dcerpc/svcctl/query_service_config_w_request'
|
258
266
|
require 'ruby_smb/dcerpc/svcctl/query_service_config_w_response'
|
259
267
|
require 'ruby_smb/dcerpc/svcctl/change_service_config_w_request'
|
260
268
|
require 'ruby_smb/dcerpc/svcctl/change_service_config_w_response'
|
269
|
+
require 'ruby_smb/dcerpc/svcctl/create_service_w_request'
|
270
|
+
require 'ruby_smb/dcerpc/svcctl/create_service_w_response'
|
261
271
|
require 'ruby_smb/dcerpc/svcctl/start_service_w_request'
|
262
272
|
require 'ruby_smb/dcerpc/svcctl/start_service_w_response'
|
263
273
|
require 'ruby_smb/dcerpc/svcctl/control_service_request'
|
@@ -289,10 +299,41 @@ module RubySMB
|
|
289
299
|
open_sc_manager_w_response.lp_sc_handle
|
290
300
|
end
|
291
301
|
|
302
|
+
# Creates an RPC context handle to a new service record.
|
303
|
+
#
|
304
|
+
# @param scm_handle [RubySMB::Dcerpc::Svcctl::ScRpcHandle] handle to the SCM database
|
305
|
+
# @param service_name [String] the ServiceName of the service record
|
306
|
+
# @param display_name [String] the DisplayName of the service record
|
307
|
+
# @param binary_path_name [String] the BinaryPathName of the service record
|
308
|
+
# @return [RubySMB::Dcerpc::Svcctl::ScRpcHandle] handle to the created service record
|
309
|
+
# @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a CreateServiceWResponse packet
|
310
|
+
# @raise [RubySMB::Dcerpc::Error::SvcctlError] if the response error status is not ERROR_SUCCESS
|
311
|
+
def create_service_w(scm_handle, service_name, display_name, binary_path_name)
|
312
|
+
create_service_w_request = CreateServiceWRequest.new(dw_desired_access: SERVICE_ALL_ACCESS)
|
313
|
+
create_service_w_request.dw_service_type = SERVICE_INTERACTIVE_PROCESS | SERVICE_WIN32_OWN_PROCESS
|
314
|
+
create_service_w_request.dw_start_type = SERVICE_DEMAND_START
|
315
|
+
create_service_w_request.h_sc_object = scm_handle
|
316
|
+
create_service_w_request.lp_service_name = service_name
|
317
|
+
create_service_w_request.lp_display_name = display_name
|
318
|
+
create_service_w_request.lp_binary_path_name = binary_path_name
|
319
|
+
response = dcerpc_request(create_service_w_request)
|
320
|
+
begin
|
321
|
+
create_service_w_response = CreateServiceWResponse.read(response)
|
322
|
+
rescue IOError
|
323
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading CreateServiceWResponse'
|
324
|
+
end
|
325
|
+
unless create_service_w_response.error_status == WindowsError::Win32::ERROR_SUCCESS
|
326
|
+
raise RubySMB::Dcerpc::Error::SvcctlError,
|
327
|
+
"Error returned when creating #{service_name} service: "\
|
328
|
+
"#{WindowsError::Win32.find_by_retval(create_service_w_response.error_status.value).join(',')}"
|
329
|
+
end
|
330
|
+
create_service_w_response.lp_sc_handle
|
331
|
+
end
|
332
|
+
|
292
333
|
# Creates an RPC context handle to an existing service record.
|
293
334
|
#
|
294
335
|
# @param scm_handle [RubySMB::Dcerpc::Svcctl::ScRpcHandle] handle to the SCM database
|
295
|
-
# @param service_name [
|
336
|
+
# @param service_name [String] the ServiceName of the service record
|
296
337
|
# @param access [Integer] access right
|
297
338
|
# @return [RubySMB::Dcerpc::Svcctl::ScRpcHandle] handle to the found service record
|
298
339
|
# @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a OpenServiceWResponse packet
|
@@ -303,16 +344,16 @@ module RubySMB
|
|
303
344
|
open_service_w_request.lp_service_name = service_name
|
304
345
|
response = dcerpc_request(open_service_w_request)
|
305
346
|
begin
|
306
|
-
|
347
|
+
open_service_w_response = OpenServiceWResponse.read(response)
|
307
348
|
rescue IOError
|
308
349
|
raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading OpenServiceWResponse'
|
309
350
|
end
|
310
|
-
unless
|
351
|
+
unless open_service_w_response.error_status == WindowsError::Win32::ERROR_SUCCESS
|
311
352
|
raise RubySMB::Dcerpc::Error::SvcctlError,
|
312
353
|
"Error returned when opening #{service_name} service: "\
|
313
|
-
"#{WindowsError::Win32.find_by_retval(
|
354
|
+
"#{WindowsError::Win32.find_by_retval(open_service_w_response.error_status.value).join(',')}"
|
314
355
|
end
|
315
|
-
|
356
|
+
open_service_w_response.lp_sc_handle
|
316
357
|
end
|
317
358
|
|
318
359
|
# Returns the current status of the specified service
|
@@ -472,6 +513,26 @@ module RubySMB
|
|
472
513
|
"#{WindowsError::Win32.find_by_retval(csh_response.error_status.value).join(',')}"
|
473
514
|
end
|
474
515
|
end
|
516
|
+
|
517
|
+
# Deletes the specified service
|
518
|
+
#
|
519
|
+
# @param scm_handle [RubySMB::Dcerpc::Svcctl::ScRpcHandle] handle to the service record
|
520
|
+
# @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a DeleteServiceResponse packet
|
521
|
+
# @raise [RubySMB::Dcerpc::Error::SvcctlError] if the response error status is not ERROR_SUCCESS
|
522
|
+
def delete_service(svc_handle)
|
523
|
+
ds_request = DeleteServiceRequest.new(lp_sc_handle: svc_handle)
|
524
|
+
response = dcerpc_request(ds_request)
|
525
|
+
begin
|
526
|
+
ds_response = DeleteServiceResponse.read(response)
|
527
|
+
rescue IOError
|
528
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading DeleteServiceResponse'
|
529
|
+
end
|
530
|
+
unless ds_response.error_status == WindowsError::Win32::ERROR_SUCCESS
|
531
|
+
raise RubySMB::Dcerpc::Error::SvcctlError,
|
532
|
+
"Error returned when deleting the service: "\
|
533
|
+
"#{WindowsError::Win32.find_by_retval(ds_response.error_status.value).join(',')}"
|
534
|
+
end
|
535
|
+
end
|
475
536
|
end
|
476
537
|
end
|
477
538
|
end
|
@@ -33,7 +33,7 @@ module RubySMB
|
|
33
33
|
super
|
34
34
|
@opnum = get_parameter(:opnum) if has_parameter?(:opnum)
|
35
35
|
self.server_name = :null
|
36
|
-
self.sam_desired.
|
36
|
+
self.sam_desired.maximum_allowed = 1 unless [OPEN_HKPD, OPEN_HKPT, OPEN_HKPN].include?(@opnum)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -33,7 +33,7 @@ module RubySMB
|
|
33
33
|
bit1 :generic_execute, label: 'Generic Execute'
|
34
34
|
bit1 :generic_all, label: 'Generic All'
|
35
35
|
bit2 :reserved4, label: 'Reserved Space'
|
36
|
-
bit1 :
|
36
|
+
bit1 :maximum_allowed, label: 'Maximum Allowed'
|
37
37
|
bit1 :system_security, label: 'System Security'
|
38
38
|
end
|
39
39
|
|
@@ -274,7 +274,7 @@ module RubySMB
|
|
274
274
|
lp_sub_key: sub_key,
|
275
275
|
lp_class: opts[:lp_class] || :null,
|
276
276
|
dw_options: opts[:dw_options] || RubySMB::Dcerpc::Winreg::CreateKeyRequest::REG_KEY_TYPE_VOLATILE,
|
277
|
-
sam_desired: opts[:sam_desired] || RubySMB::Dcerpc::Winreg::Regsam.new(
|
277
|
+
sam_desired: opts[:sam_desired] || RubySMB::Dcerpc::Winreg::Regsam.new(maximum_allowed: 1),
|
278
278
|
lp_security_attributes: opts[:lp_security_attributes] || RubySMB::Dcerpc::RpcSecurityAttributes.new,
|
279
279
|
lpdw_disposition: opts[:lpdw_disposition] || RubySMB::Dcerpc::Winreg::CreateKeyRequest::REG_CREATED_NEW_KEY,
|
280
280
|
}
|
@@ -65,6 +65,10 @@ module RubySMB
|
|
65
65
|
# [2.2.2.3.5 Pass-through Information Level Codes](https://msdn.microsoft.com/en-us/library/ff470158.aspx)
|
66
66
|
SMB_INFO_PASSTHROUGH = 0x03e8
|
67
67
|
|
68
|
+
def self.name(value)
|
69
|
+
constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
|
70
|
+
end
|
71
|
+
|
68
72
|
# The FILE_NAME_INFORMATION type as defined in
|
69
73
|
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/20406fb1-605f-4629-ba9a-c67ee25f23d2
|
70
74
|
class FileNameInformation < BinData::Record
|
@@ -78,6 +78,10 @@ module RubySMB
|
|
78
78
|
msg.flag |= NTLM::NEGOTIATE_FLAGS.fetch(flag)
|
79
79
|
end
|
80
80
|
|
81
|
+
if type1_msg.flag & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] == NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY]
|
82
|
+
msg.flag |= NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY]
|
83
|
+
end
|
84
|
+
|
81
85
|
@server_challenge = @provider.generate_server_challenge
|
82
86
|
msg.challenge = @server_challenge.unpack1('Q<') # 64-bit unsigned, little endian (uint64_t)
|
83
87
|
target_info = Net::NTLM::TargetInfo.new('')
|