ruby_smb 1.0.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.travis.yml +4 -1
- data/README.md +35 -47
- data/examples/enum_registry_key.rb +28 -0
- data/examples/enum_registry_values.rb +30 -0
- data/examples/pipes.rb +2 -1
- data/examples/read_registry_key_value.rb +32 -0
- data/lib/ruby_smb.rb +0 -1
- data/lib/ruby_smb/client.rb +2 -0
- data/lib/ruby_smb/client/winreg.rb +46 -0
- data/lib/ruby_smb/dcerpc.rb +38 -0
- data/lib/ruby_smb/dcerpc/bind.rb +2 -2
- data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
- data/lib/ruby_smb/dcerpc/error.rb +3 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
- data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
- data/lib/ruby_smb/dcerpc/request.rb +28 -9
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
- data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
- data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
- data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
- data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
- data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
- data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
- data/lib/ruby_smb/smb1/file.rb +2 -0
- data/lib/ruby_smb/smb1/pipe.rb +78 -2
- data/lib/ruby_smb/smb2/packet/error_packet.rb +2 -4
- data/lib/ruby_smb/smb2/pipe.rb +89 -2
- data/lib/ruby_smb/version.rb +1 -1
- data/ruby_smb.gemspec +3 -3
- data/spec/lib/ruby_smb/client_spec.rb +148 -0
- data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
- data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
- data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
- data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +210 -148
- data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +3 -24
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +256 -145
- metadata +66 -9
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
- data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -0,0 +1,57 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Winreg
|
4
|
+
|
5
|
+
# This class represents a BaseRegQueryValue Response Packet as defined in
|
6
|
+
# [3.1.5.17 BaseRegQueryValue (Opnum 17)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/8bc10aa3-2f91-44e8-aa33-b3263c49ab9d)
|
7
|
+
class QueryValueResponse < BinData::Record
|
8
|
+
attr_reader :opnum
|
9
|
+
|
10
|
+
endian :little
|
11
|
+
|
12
|
+
ndr_lp_dword :lp_type
|
13
|
+
ndr_lp_byte :lp_data
|
14
|
+
string :pad, length: -> { pad_length }
|
15
|
+
ndr_lp_dword :lpcb_data
|
16
|
+
ndr_lp_dword :lpcb_len
|
17
|
+
uint32 :error_status
|
18
|
+
|
19
|
+
def initialize_instance
|
20
|
+
super
|
21
|
+
@opnum = REG_QUERY_VALUE
|
22
|
+
end
|
23
|
+
|
24
|
+
# Determines the correct length for the padding in front of
|
25
|
+
# #lpcb_data. It should always force a 4-byte alignment.
|
26
|
+
def pad_length
|
27
|
+
offset = (lp_data.abs_offset + lp_data.to_binary_s.length) % 4
|
28
|
+
(4 - offset) % 4
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the data portion of the registry value formatted according to its type:
|
32
|
+
# [3.1.1.5 Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/3d64dbea-f016-4373-8cac-e43bf343837d)
|
33
|
+
def data
|
34
|
+
bytes = lp_data.bytes.to_a.pack('C*')
|
35
|
+
case lp_type
|
36
|
+
when 1,2
|
37
|
+
bytes.force_encoding('utf-16le').strip
|
38
|
+
when 3
|
39
|
+
bytes
|
40
|
+
when 4
|
41
|
+
bytes.unpack('V').first
|
42
|
+
when 5
|
43
|
+
bytes.unpack('N').first
|
44
|
+
when 7
|
45
|
+
str = bytes.force_encoding('utf-16le')
|
46
|
+
str.split("\0".encode('utf-16le'))
|
47
|
+
when 11
|
48
|
+
bytes.unpack('Q<').first
|
49
|
+
else
|
50
|
+
""
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
module Winreg
|
4
|
+
|
5
|
+
# This class represents a REGSAM structure as defined in
|
6
|
+
# [2.2.3 REGSAM](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/fefbc801-b141-4bb1-9dcb-bf366da3ae7e)
|
7
|
+
# [2.4.3 ACCESS_MASK](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/7a53f60e-e730-4dfe-bbe9-b21b62eb790b)
|
8
|
+
class Regsam < BinData::Record
|
9
|
+
endian :little
|
10
|
+
bit2 :reserved, label: 'Reserved Space'
|
11
|
+
bit1 :key_create_link, label: 'Key Create Link'
|
12
|
+
bit1 :key_notify, label: 'Key Notify'
|
13
|
+
bit1 :key_enumerate_sub_keys, label: 'Key Enumerate Sub Keys'
|
14
|
+
bit1 :key_create_sub_key, label: 'Key Create Sub Key'
|
15
|
+
bit1 :key_set_value, label: 'Key Set Value'
|
16
|
+
bit1 :key_query_value, label: 'Key Query Value'
|
17
|
+
# byte boundary
|
18
|
+
bit6 :reserved2, label: 'Reserved Space'
|
19
|
+
bit1 :key_wow64_32key, label: 'Key Wow64 32key'
|
20
|
+
bit1 :key_wow64_64key, label: 'Key Wow64 64key'
|
21
|
+
# byte boundary
|
22
|
+
bit3 :reserved3, label: 'Reserved Space'
|
23
|
+
bit1 :synchronize, label: 'Synchronize'
|
24
|
+
bit1 :write_owner, label: 'Write Owner'
|
25
|
+
bit1 :write_dac, label: 'Write DAC'
|
26
|
+
bit1 :read_control, label: 'Read Control'
|
27
|
+
bit1 :delete_access, label: 'Delete'
|
28
|
+
# byte boundary
|
29
|
+
bit1 :generic_read, label: 'Generic Read'
|
30
|
+
bit1 :generic_write, label: 'Generic Write'
|
31
|
+
bit1 :generic_execute, label: 'Generic Execute'
|
32
|
+
bit1 :generic_all, label: 'Generic All'
|
33
|
+
bit2 :reserved4, label: 'Reserved Space'
|
34
|
+
bit1 :maximum, label: 'Maximum Allowed'
|
35
|
+
bit1 :system_security, label: 'System Security'
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/ruby_smb/smb1/file.rb
CHANGED
@@ -156,6 +156,8 @@ module RubySMB
|
|
156
156
|
def read_packet(read_length: 0, offset: 0)
|
157
157
|
read_request = set_header_fields(RubySMB::SMB1::Packet::ReadAndxRequest.new)
|
158
158
|
read_request.parameter_block.max_count_of_bytes_to_return = read_length
|
159
|
+
read_request.parameter_block.min_count_of_bytes_to_return = read_length
|
160
|
+
read_request.parameter_block.remaining = read_length
|
159
161
|
read_request.parameter_block.offset = offset
|
160
162
|
read_request
|
161
163
|
end
|
data/lib/ruby_smb/smb1/pipe.rb
CHANGED
@@ -3,9 +3,9 @@ module RubySMB
|
|
3
3
|
# Represents a pipe on the Remote server that we can perform
|
4
4
|
# various I/O operations on.
|
5
5
|
class Pipe < File
|
6
|
-
require 'ruby_smb/
|
6
|
+
require 'ruby_smb/dcerpc'
|
7
7
|
|
8
|
-
include RubySMB::
|
8
|
+
include RubySMB::Dcerpc
|
9
9
|
|
10
10
|
# Reference: https://msdn.microsoft.com/en-us/library/ee441883.aspx
|
11
11
|
STATUS_DISCONNECTED = 0x0001
|
@@ -13,6 +13,17 @@ module RubySMB
|
|
13
13
|
STATUS_OK = 0x0003
|
14
14
|
STATUS_CLOSED = 0x0004
|
15
15
|
|
16
|
+
def initialize(tree:, response:, name:)
|
17
|
+
raise ArgumentError, 'No Name Provided' if name.nil?
|
18
|
+
case name
|
19
|
+
when 'srvsvc'
|
20
|
+
extend RubySMB::Dcerpc::Srvsvc
|
21
|
+
when 'winreg'
|
22
|
+
extend RubySMB::Dcerpc::Winreg
|
23
|
+
end
|
24
|
+
super(tree: tree, response: response, name: name)
|
25
|
+
end
|
26
|
+
|
16
27
|
# Performs a peek operation on the named pipe
|
17
28
|
#
|
18
29
|
# @param peek_size [Integer] Amount of data to peek
|
@@ -68,6 +79,71 @@ module RubySMB
|
|
68
79
|
state == STATUS_OK
|
69
80
|
end
|
70
81
|
|
82
|
+
# Send a DCERPC request with the provided stub packet.
|
83
|
+
#
|
84
|
+
# @params stub_packet [#opnum] the stub packet to add to the DCERPC request
|
85
|
+
# @return [String] the raw DCERPC response stub
|
86
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not valid
|
87
|
+
# @raise [RubySMB::Error::UnexpectedStatusCode] if the response status code is different than STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW
|
88
|
+
def dcerpc_request(stub_packet, options={})
|
89
|
+
options.merge!(endpoint: stub_packet.class.name.split('::').at(-2))
|
90
|
+
dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: stub_packet.opnum }, options)
|
91
|
+
dcerpc_request.stub.read(stub_packet.to_binary_s)
|
92
|
+
trans_nmpipe_request = RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest.new(options)
|
93
|
+
@tree.set_header_fields(trans_nmpipe_request)
|
94
|
+
trans_nmpipe_request.set_fid(@fid)
|
95
|
+
trans_nmpipe_request.data_block.trans_data.write_data = dcerpc_request.to_binary_s
|
96
|
+
|
97
|
+
trans_nmpipe_raw_response = @tree.client.send_recv(trans_nmpipe_request)
|
98
|
+
trans_nmpipe_response = RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse.read(trans_nmpipe_raw_response)
|
99
|
+
unless trans_nmpipe_response.valid?
|
100
|
+
raise RubySMB::Error::InvalidPacket.new(
|
101
|
+
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
102
|
+
expected_cmd: RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse::COMMAND,
|
103
|
+
received_proto: trans_nmpipe_response.smb_header.protocol,
|
104
|
+
received_cmd: trans_nmpipe_response.smb_header.command
|
105
|
+
)
|
106
|
+
end
|
107
|
+
unless [WindowsError::NTStatus::STATUS_SUCCESS,
|
108
|
+
WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW].include?(trans_nmpipe_response.status_code)
|
109
|
+
raise RubySMB::Error::UnexpectedStatusCode, trans_nmpipe_response.status_code.name
|
110
|
+
end
|
111
|
+
|
112
|
+
raw_data = trans_nmpipe_response.data_block.trans_data.read_data.to_binary_s
|
113
|
+
if trans_nmpipe_response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW
|
114
|
+
raw_data << read(bytes: @tree.client.max_buffer_size - trans_nmpipe_response.parameter_block.data_count)
|
115
|
+
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
116
|
+
unless dcerpc_response.pdu_header.pfc_flags.first_frag == 1
|
117
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, "Not the first fragment"
|
118
|
+
end
|
119
|
+
stub_data = dcerpc_response.stub.to_s
|
120
|
+
|
121
|
+
loop do
|
122
|
+
break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
|
123
|
+
raw_data = read(bytes: @tree.client.max_buffer_size)
|
124
|
+
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
125
|
+
stub_data << dcerpc_response.stub.to_s
|
126
|
+
end
|
127
|
+
stub_data
|
128
|
+
else
|
129
|
+
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
130
|
+
dcerpc_response.stub.to_s
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def dcerpc_response_from_raw_response(raw_data)
|
138
|
+
dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
|
139
|
+
unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::RESPONSE
|
140
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
|
141
|
+
end
|
142
|
+
dcerpc_response
|
143
|
+
rescue IOError
|
144
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the DCERPC response"
|
145
|
+
end
|
146
|
+
|
71
147
|
end
|
72
148
|
end
|
73
149
|
end
|
@@ -11,10 +11,8 @@ module RubySMB
|
|
11
11
|
uint16 :structure_size, label: 'Structure Size', initial_value: 9
|
12
12
|
uint8 :error_context_count, label: 'ErrorContextCount'
|
13
13
|
uint8 :reserved
|
14
|
-
uint32 :byte_count, label: 'Byte Count of ErrorData'
|
15
|
-
|
16
|
-
string :error_data, label: 'Error Data', initial_value: "\x00",
|
17
|
-
read_length: -> { byte_count == 0 ? 1 : byte_count }
|
14
|
+
uint32 :byte_count, label: 'Byte Count of ErrorData'
|
15
|
+
string :error_data, label: 'Error Data', read_length: -> { byte_count }
|
18
16
|
|
19
17
|
def valid?
|
20
18
|
return smb2_header.protocol == RubySMB::SMB2::SMB2_PROTOCOL_ID &&
|
data/lib/ruby_smb/smb2/pipe.rb
CHANGED
@@ -3,13 +3,24 @@ module RubySMB
|
|
3
3
|
# Represents a pipe on the Remote server that we can perform
|
4
4
|
# various I/O operations on.
|
5
5
|
class Pipe < File
|
6
|
-
require 'ruby_smb/
|
6
|
+
require 'ruby_smb/dcerpc'
|
7
7
|
|
8
|
-
include RubySMB::
|
8
|
+
include RubySMB::Dcerpc
|
9
9
|
|
10
10
|
STATUS_CONNECTED = 0x00000003
|
11
11
|
STATUS_CLOSING = 0x00000004
|
12
12
|
|
13
|
+
def initialize(tree:, response:, name:)
|
14
|
+
raise ArgumentError, 'No Name Provided' if name.nil?
|
15
|
+
case name
|
16
|
+
when 'srvsvc'
|
17
|
+
extend RubySMB::Dcerpc::Srvsvc
|
18
|
+
when 'winreg'
|
19
|
+
extend RubySMB::Dcerpc::Winreg
|
20
|
+
end
|
21
|
+
super(tree: tree, response: response, name: name)
|
22
|
+
end
|
23
|
+
|
13
24
|
# Performs a peek operation on the named pipe
|
14
25
|
#
|
15
26
|
# @param peek_size [Integer] Amount of data to peek
|
@@ -67,6 +78,82 @@ module RubySMB
|
|
67
78
|
state == STATUS_CONNECTED
|
68
79
|
end
|
69
80
|
|
81
|
+
def dcerpc_request(stub_packet, options={})
|
82
|
+
options.merge!(endpoint: stub_packet.class.name.split('::').at(-2))
|
83
|
+
dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: stub_packet.opnum }, options)
|
84
|
+
dcerpc_request.stub.read(stub_packet.to_binary_s)
|
85
|
+
ioctl_send_recv(dcerpc_request, options)
|
86
|
+
end
|
87
|
+
|
88
|
+
def ioctl_send_recv(action, options={})
|
89
|
+
request = set_header_fields(RubySMB::SMB2::Packet::IoctlRequest.new(options))
|
90
|
+
request.ctl_code = 0x0011C017
|
91
|
+
request.flags.is_fsctl = 0x00000001
|
92
|
+
request.buffer = action.to_binary_s
|
93
|
+
|
94
|
+
ioctl_raw_response = @tree.client.send_recv(request)
|
95
|
+
ioctl_response = RubySMB::SMB2::Packet::IoctlResponse.read(ioctl_raw_response)
|
96
|
+
unless ioctl_response.valid?
|
97
|
+
raise RubySMB::Error::InvalidPacket.new(
|
98
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
99
|
+
expected_cmd: RubySMB::SMB2::Packet::IoctlRequest::COMMAND,
|
100
|
+
received_proto: ioctl_response.smb2_header.protocol,
|
101
|
+
received_cmd: ioctl_response.smb2_header.command
|
102
|
+
)
|
103
|
+
end
|
104
|
+
# TODO: improve the handling of STATUS_PENDING responses
|
105
|
+
if ioctl_response.status_code == WindowsError::NTStatus::STATUS_PENDING
|
106
|
+
sleep 1
|
107
|
+
ioctl_raw_response = @tree.client.dispatcher.recv_packet
|
108
|
+
ioctl_response = RubySMB::SMB2::Packet::IoctlResponse.read(ioctl_raw_response)
|
109
|
+
unless ioctl_response.valid?
|
110
|
+
raise RubySMB::Error::InvalidPacket.new(
|
111
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
112
|
+
expected_cmd: RubySMB::SMB2::Packet::IoctlRequest::COMMAND,
|
113
|
+
received_proto: ioctl_response.smb2_header.protocol,
|
114
|
+
received_cmd: ioctl_response.smb2_header.command
|
115
|
+
)
|
116
|
+
end
|
117
|
+
elsif ![WindowsError::NTStatus::STATUS_SUCCESS,
|
118
|
+
WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW].include?(ioctl_response.status_code)
|
119
|
+
raise RubySMB::Error::UnexpectedStatusCode, ioctl_response.status_code.name
|
120
|
+
end
|
121
|
+
|
122
|
+
raw_data = ioctl_response.output_data
|
123
|
+
if ioctl_response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW
|
124
|
+
raw_data << read(bytes: @tree.client.max_buffer_size - ioctl_response.output_count)
|
125
|
+
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
126
|
+
unless dcerpc_response.pdu_header.pfc_flags.first_frag == 1
|
127
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, "Not the first fragment"
|
128
|
+
end
|
129
|
+
stub_data = dcerpc_response.stub.to_s
|
130
|
+
|
131
|
+
loop do
|
132
|
+
break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
|
133
|
+
raw_data = read(bytes: @tree.client.max_buffer_size)
|
134
|
+
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
135
|
+
stub_data << dcerpc_response.stub.to_s
|
136
|
+
end
|
137
|
+
stub_data
|
138
|
+
else
|
139
|
+
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
140
|
+
dcerpc_response.stub.to_s
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def dcerpc_response_from_raw_response(raw_data)
|
148
|
+
dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
|
149
|
+
unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::RESPONSE
|
150
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
|
151
|
+
end
|
152
|
+
dcerpc_response
|
153
|
+
rescue IOError
|
154
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the DCERPC response"
|
155
|
+
end
|
156
|
+
|
70
157
|
end
|
71
158
|
end
|
72
159
|
end
|
data/lib/ruby_smb/version.rb
CHANGED
data/ruby_smb.gemspec
CHANGED
@@ -6,8 +6,8 @@ require 'ruby_smb/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'ruby_smb'
|
8
8
|
spec.version = RubySMB::VERSION
|
9
|
-
spec.authors = ['David Maloney', 'James Lee', 'Dev Mohanty', 'Christophe De La Fuente']
|
10
|
-
spec.email = ['
|
9
|
+
spec.authors = ['Metasploit Hackers', 'David Maloney', 'James Lee', 'Dev Mohanty', 'Christophe De La Fuente']
|
10
|
+
spec.email = ['msfdev@metasploit.com']
|
11
11
|
spec.summary = 'A pure Ruby implementation of the SMB Protocol Family'
|
12
12
|
spec.description = ''
|
13
13
|
spec.homepage = 'https://github.com/rapid7/ruby_smb'
|
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.platform = Gem::Platform::RUBY
|
27
27
|
end
|
28
28
|
|
29
|
-
spec.required_ruby_version = '>= 2.
|
29
|
+
spec.required_ruby_version = '>= 2.3.0'
|
30
30
|
|
31
31
|
spec.add_development_dependency 'bundler'
|
32
32
|
spec.add_development_dependency 'fivemat'
|
@@ -1325,5 +1325,153 @@ RSpec.describe RubySMB::Client do
|
|
1325
1325
|
end
|
1326
1326
|
end
|
1327
1327
|
|
1328
|
+
context 'Winreg' do
|
1329
|
+
describe '#connect_to_winreg' do
|
1330
|
+
let(:host) { '1.2.3.4' }
|
1331
|
+
let(:share) { "\\\\#{host}\\IPC$" }
|
1332
|
+
let(:ipc_tree) { double('IPC$ tree') }
|
1333
|
+
let(:named_pipe) { double('Named pipe') }
|
1334
|
+
before :example do
|
1335
|
+
allow(ipc_tree).to receive_messages(
|
1336
|
+
:share => share,
|
1337
|
+
:open_file => named_pipe
|
1338
|
+
)
|
1339
|
+
allow(client).to receive(:tree_connect).and_return(ipc_tree)
|
1340
|
+
end
|
1341
|
+
|
1342
|
+
context 'when the client is already connected to the IPC$ share' do
|
1343
|
+
before :example do
|
1344
|
+
client.tree_connects << ipc_tree
|
1345
|
+
allow(ipc_tree).to receive(:share).and_return(share)
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
it 'does not connect to the already connected tree' do
|
1349
|
+
client.connect_to_winreg(host)
|
1350
|
+
expect(client).to_not have_received(:tree_connect)
|
1351
|
+
end
|
1352
|
+
end
|
1353
|
+
|
1354
|
+
it 'calls #tree_connect' do
|
1355
|
+
client.connect_to_winreg(host)
|
1356
|
+
expect(client).to have_received(:tree_connect).with(share)
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
it 'open \'winreg\' file on the IPC$ Tree' do
|
1360
|
+
client.connect_to_winreg(host)
|
1361
|
+
expect(ipc_tree).to have_received(:open_file).with(filename: "winreg", write: true, read: true)
|
1362
|
+
end
|
1363
|
+
|
1364
|
+
it 'returns the expected opened named pipe' do
|
1365
|
+
expect(client.connect_to_winreg(host)).to eq(named_pipe)
|
1366
|
+
end
|
1367
|
+
|
1368
|
+
context 'when a block is given' do
|
1369
|
+
before :example do
|
1370
|
+
allow(named_pipe).to receive(:close)
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
it 'yields the expected named_pipe' do
|
1374
|
+
client.connect_to_winreg(host) do |np|
|
1375
|
+
expect(np).to eq(named_pipe)
|
1376
|
+
end
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
it 'closes the named pipe' do
|
1380
|
+
client.connect_to_winreg(host) { |np| }
|
1381
|
+
expect(named_pipe).to have_received(:close)
|
1382
|
+
end
|
1383
|
+
|
1384
|
+
it 'returns the block return value' do
|
1385
|
+
result = double('Result')
|
1386
|
+
expect(client.connect_to_winreg(host) { |np| result }).to eq(result)
|
1387
|
+
end
|
1388
|
+
end
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
describe '#has_registry_key?' do
|
1392
|
+
let(:host) { '1.2.3.4' }
|
1393
|
+
let(:key) { 'HKLM\\Registry\\Key' }
|
1394
|
+
let(:named_pipe) { double('Named pipe') }
|
1395
|
+
let(:result) { double('Result') }
|
1396
|
+
before :example do
|
1397
|
+
allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
|
1398
|
+
allow(named_pipe).to receive(:has_registry_key?).and_return(result)
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
it 'calls #connect_to_winreg to wrap the main logic around' do
|
1402
|
+
client.has_registry_key?(host, key)
|
1403
|
+
expect(client).to have_received(:connect_to_winreg).with(host)
|
1404
|
+
end
|
1405
|
+
|
1406
|
+
it 'calls Pipe #has_registry_key?' do
|
1407
|
+
client.has_registry_key?(host, key)
|
1408
|
+
expect(named_pipe).to have_received(:has_registry_key?).with(key)
|
1409
|
+
end
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
describe '#read_registry_key_value' do
|
1413
|
+
let(:host) { '1.2.3.4' }
|
1414
|
+
let(:key) { 'HKLM\\Registry\\Key' }
|
1415
|
+
let(:value_name) { 'Value' }
|
1416
|
+
let(:named_pipe) { double('Named pipe') }
|
1417
|
+
let(:result) { double('Result') }
|
1418
|
+
before :example do
|
1419
|
+
allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
|
1420
|
+
allow(named_pipe).to receive(:read_registry_key_value).and_return(result)
|
1421
|
+
end
|
1422
|
+
|
1423
|
+
it 'calls #connect_to_winreg to wrap the main logic around' do
|
1424
|
+
client.read_registry_key_value(host, key, value_name)
|
1425
|
+
expect(client).to have_received(:connect_to_winreg).with(host)
|
1426
|
+
end
|
1427
|
+
|
1428
|
+
it 'calls Pipe #read_registry_key_value' do
|
1429
|
+
client.read_registry_key_value(host, key, value_name)
|
1430
|
+
expect(named_pipe).to have_received(:read_registry_key_value).with(key, value_name)
|
1431
|
+
end
|
1432
|
+
end
|
1433
|
+
|
1434
|
+
describe '#enum_registry_key' do
|
1435
|
+
let(:host) { '1.2.3.4' }
|
1436
|
+
let(:key) { 'HKLM\\Registry\\Key' }
|
1437
|
+
let(:named_pipe) { double('Named pipe') }
|
1438
|
+
let(:result) { double('Result') }
|
1439
|
+
before :example do
|
1440
|
+
allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
|
1441
|
+
allow(named_pipe).to receive(:enum_registry_key).and_return(result)
|
1442
|
+
end
|
1443
|
+
|
1444
|
+
it 'calls #connect_to_winreg to wrap the main logic around' do
|
1445
|
+
client.enum_registry_key(host, key)
|
1446
|
+
expect(client).to have_received(:connect_to_winreg).with(host)
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
it 'calls Pipe #enum_registry_key' do
|
1450
|
+
client.enum_registry_key(host, key)
|
1451
|
+
expect(named_pipe).to have_received(:enum_registry_key).with(key)
|
1452
|
+
end
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
describe '#enum_registry_values' do
|
1456
|
+
let(:host) { '1.2.3.4' }
|
1457
|
+
let(:key) { 'HKLM\\Registry\\Key' }
|
1458
|
+
let(:named_pipe) { double('Named pipe') }
|
1459
|
+
let(:result) { double('Result') }
|
1460
|
+
before :example do
|
1461
|
+
allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
|
1462
|
+
allow(named_pipe).to receive(:enum_registry_values).and_return(result)
|
1463
|
+
end
|
1464
|
+
|
1465
|
+
it 'calls #connect_to_winreg to wrap the main logic around' do
|
1466
|
+
client.enum_registry_values(host, key)
|
1467
|
+
expect(client).to have_received(:connect_to_winreg).with(host)
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
it 'calls Pipe #enum_registry_values' do
|
1471
|
+
client.enum_registry_values(host, key)
|
1472
|
+
expect(named_pipe).to have_received(:enum_registry_values).with(key)
|
1473
|
+
end
|
1474
|
+
end
|
1475
|
+
end
|
1328
1476
|
end
|
1329
1477
|
|