ruby_smb 1.0.5 → 1.1.0
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.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
|
|