From 9354f9ea03a492edd95881f41da206ec3c879084 Mon Sep 17 00:00:00 2001 From: NtAlexio2 Date: Sun, 1 Sep 2024 20:34:31 -0400 Subject: [PATCH 1/6] move WkssvcIdentifyHandle struct to generic file --- lib/ruby_smb/dcerpc/wkssvc.rb | 2 ++ lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/ruby_smb/dcerpc/wkssvc.rb b/lib/ruby_smb/dcerpc/wkssvc.rb index 278a4820b..1f33cecdd 100644 --- a/lib/ruby_smb/dcerpc/wkssvc.rb +++ b/lib/ruby_smb/dcerpc/wkssvc.rb @@ -23,6 +23,8 @@ module Wkssvc WKSTA_INFO_102 = 0x00000066 #TODO: WKSTA_INFO_502 = 0x000001F6 + class WkssvcIdentifyHandle < Ndr::NdrWideStringPtr; end + require 'ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request' require 'ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response' diff --git a/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb index b9b1eb5fe..9de032b9c 100644 --- a/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb +++ b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb @@ -2,9 +2,6 @@ module RubySMB module Dcerpc module Wkssvc - # [2.2.2.1 WKSSVC_IDENTIFY_HANDLE](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/9ef94a11-0e5c-49d7-9ac7-68d6f03565de) - class WkssvcIdentifyHandle < Ndr::NdrWideStringPtr; end - # [3.2.4.1 NetrWkstaGetInfo (Opnum 0)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/4af41d6f-b800-4de1-af5b-0b15a85f8e04) class NetrWkstaGetInfoRequest < BinData::Record attr_reader :opnum From c827862f8b60be11a1ff3be92d30ba85e4d0f7b2 Mon Sep 17 00:00:00 2001 From: NtAlexio2 Date: Sun, 1 Sep 2024 20:35:20 -0400 Subject: [PATCH 2/6] fix typo in comments --- lib/ruby_smb/dcerpc/wkssvc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ruby_smb/dcerpc/wkssvc.rb b/lib/ruby_smb/dcerpc/wkssvc.rb index 1f33cecdd..9e104767d 100644 --- a/lib/ruby_smb/dcerpc/wkssvc.rb +++ b/lib/ruby_smb/dcerpc/wkssvc.rb @@ -35,7 +35,7 @@ class WkssvcIdentifyHandle < Ndr::NdrWideStringPtr; end # # @param server_name [optional, String] String that identifies the server (optional # since it is ignored by the server) - # @param server_name [optional, Integer] The information level of the data (default: WKSTA_INFO_100) + # @param level [optional, Integer] The information level of the data (default: WKSTA_INFO_100) # @return [RubySMB::Dcerpc::Wkssvc::WkstaInfo100, RubySMB::Dcerpc::Wkssvc::WkstaInfo101, # RubySMB::Dcerpc::Wkssvc::WkstaInfo102] The structure containing the requested information # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a From fc4de9a83b6fc56613dbecf951020ac8fd0bb0aa Mon Sep 17 00:00:00 2001 From: NtAlexio2 Date: Sun, 1 Sep 2024 20:35:57 -0400 Subject: [PATCH 3/6] add new NetrWkstaUserEnum request --- lib/ruby_smb/dcerpc/request.rb | 3 +- lib/ruby_smb/dcerpc/wkssvc.rb | 121 +++++++++++++++++- .../wkssvc/netr_wksta_user_enum_request.rb | 25 ++++ .../wkssvc/netr_wksta_user_enum_response.rb | 25 ++++ 4 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb create mode 100644 lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb diff --git a/lib/ruby_smb/dcerpc/request.rb b/lib/ruby_smb/dcerpc/request.rb index 1267c33ee..7931ff397 100644 --- a/lib/ruby_smb/dcerpc/request.rb +++ b/lib/ruby_smb/dcerpc/request.rb @@ -80,7 +80,8 @@ class Request < BinData::Record string :default end choice 'Wkssvc', selection: -> { opnum } do - netr_wksta_get_info_request Wkssvc::NETR_WKSTA_GET_INFO + netr_wksta_get_info_request Wkssvc::NETR_WKSTA_GET_INFO + netr_wksta_user_enum_request Wkssvc::NETR_WKSTA_USER_ENUM string :default end choice 'Epm', selection: -> { opnum } do diff --git a/lib/ruby_smb/dcerpc/wkssvc.rb b/lib/ruby_smb/dcerpc/wkssvc.rb index 9e104767d..eddfd95bf 100644 --- a/lib/ruby_smb/dcerpc/wkssvc.rb +++ b/lib/ruby_smb/dcerpc/wkssvc.rb @@ -7,7 +7,8 @@ module Wkssvc VER_MINOR = 0 # Operation numbers - NETR_WKSTA_GET_INFO = 0x0000 + NETR_WKSTA_GET_INFO = 0x0000 + NETR_WKSTA_USER_ENUM = 0x0002 PLATFORM_ID = { 0x0000012C => "DOS", @@ -23,11 +24,91 @@ module Wkssvc WKSTA_INFO_102 = 0x00000066 #TODO: WKSTA_INFO_502 = 0x000001F6 + # User Enum Information Level + WKSTA_USER_INFO_0 = 0x00000000 + WKSTA_USER_INFO_1 = 0x00000001 + + # [2.2.2.1 WKSSVC_IDENTIFY_HANDLE](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/9ef94a11-0e5c-49d7-9ac7-68d6f03565de) class WkssvcIdentifyHandle < Ndr::NdrWideStringPtr; end + # [2.2.5.9 WKSTA_USER_INFO_0](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/b7c53c6f-8b92-4e5d-9a2e-6462cb4ef1ac) + class UserInfo0 < Ndr::NdrStruct + default_parameter byte_align: 4 + endian :little + + ndr_wide_stringz_ptr :wkui0_username + end + + class WkstaUserInfo0 < Ndr::NdrConfArray + default_parameter type: :user_info0 + end + + class PwkstaUserInfo0 < WkstaUserInfo0 + extend Ndr::PointerClassPlugin + end + + # [2.2.5.12 WKSTA_USER_INFO_0_CONTAINER](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/0b0cff8f-09bc-43a8-b0d3-88f0bf7e3664) + class WkstaUserInfo0Container < Ndr::NdrStruct + default_parameter byte_align: 4 + endian :little + + ndr_uint32 :wkui0_entries_read + pwksta_user_info0 :wkui0_buffer + end + + class PwkstaUserInfo0Container < WkstaUserInfo0Container + extend Ndr::PointerClassPlugin + end + + # [2.2.5.10 WKSTA_USER_INFO_1](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/c37b9606-866f-40ac-9490-57b8334968e2) + class UserInfo1 < Ndr::NdrStruct + default_parameter byte_align: 4 + endian :little + + ndr_wide_stringz_ptr :wkui1_username + ndr_wide_stringz_ptr :wkui1_logon_domain + ndr_wide_stringz_ptr :wkui1_oth_domains + ndr_wide_stringz_ptr :wkui1_logon_server + end + + class WkstaUserInfo1 < Ndr::NdrConfArray + default_parameter type: :user_info1 + end + + class PwkstaUserInfo1 < WkstaUserInfo1 + extend Ndr::PointerClassPlugin + end + + # [2.2.5.13 WKSTA_USER_INFO_1_CONTAINER](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/22a813e4-fc7d-4fe3-a6d6-78debfd2c0c9) + class WkstaUserInfo1Container < Ndr::NdrStruct + default_parameter byte_align: 4 + endian :little + + ndr_uint32 :wkui1_entries_read + pwksta_user_info1 :wkui1_buffer + end + + class PwkstaUserInfo1Container < WkstaUserInfo1Container + extend Ndr::PointerClassPlugin + end + + # [2.2.5.14 WKSTA_USER_ENUM_STRUCT](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/4041455a-52be-4389-a4fc-82fea3cb3160) + class LpwkssvcUserEnumStructure < Ndr::NdrStruct + default_parameter byte_align: 4 + endian :little + + ndr_uint32 :level + ndr_uint32 :tag, value: -> { self.level } + choice :info, selection: :level, byte_align: 4 do + pwksta_user_info0_container WKSTA_USER_INFO_0 + pwksta_user_info1_container WKSTA_USER_INFO_1 + end + end require 'ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request' require 'ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response' + require 'ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request' + require 'ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response' # Returns details about a computer environment, including # platform-specific information, the names of the domain and local @@ -61,6 +142,44 @@ def netr_wksta_get_info(server_name: "\x00", level: WKSTA_INFO_100) wkst_netr_wksta_get_info_response.wksta_info.info end + # Returns details about users who are currently active on a remote computer. + # + # @param server_name [optional, String] String that identifies the server (optional + # since it is ignored by the server) + # @param level [optional, Integer] The information level of the data (default: WKSTA_USER_INFO_0) + # @return [RubySMB::Dcerpc::Wkssvc::WkstaUserInfo0, RubySMB::Dcerpc::Wkssvc::WkstaUserInfo1] + # The structure containing the requested information + # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a + # NetrWkstaGetInfoResponse packet + # @raise [RubySMB::Dcerpc::Error::WkssvcError] if the response error status + # is not STATUS_SUCCESS + def netr_wksta_user_enum(server_name: "\x00", level: WKSTA_USER_INFO_0) + wkst_netr_wksta_enum_user_request = NetrWkstaUserEnumRequest.new( + server_name: server_name, + user_info: { + level: level, + tag: level, + info: { + wkui0_entries_read: 0, + }, + }, + preferred_max_length: 0xFFFFFFFF, + result_handle: 0 + ) + response = dcerpc_request(wkst_netr_wksta_enum_user_request) + begin + wkst_netr_wksta_enum_user_response = NetrWkstaUserEnumResponse.read(response) + rescue IOError + raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading WkstNetrWkstaUserEnumResponse' + end + unless wkst_netr_wksta_enum_user_response.error_status == WindowsError::NTStatus::STATUS_SUCCESS + raise RubySMB::Dcerpc::Error::WkssvcError, + "Error returned with netr_wksta_enum_user: #{wkst_netr_wksta_enum_user_response.error_status.value} - "\ + "#{WindowsError::NTStatus.find_by_retval(wkst_netr_wksta_enum_user_response.error_status.value).join(',')}" + end + wkst_netr_wksta_enum_user_response.user_info.info + end + end end end diff --git a/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb new file mode 100644 index 000000000..6c206c021 --- /dev/null +++ b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb @@ -0,0 +1,25 @@ +module RubySMB + module Dcerpc + module Wkssvc + + # [3.2.4.3 NetrWkstaUserEnum (Opnum 2)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/4af41d6f-b800-4de1-af5b-0b15a85f8e04) + class NetrWkstaUserEnumRequest < BinData::Record + attr_reader :opnum + + endian :little + + wkssvc_identify_handle :server_name + lpwkssvc_user_enum_structure :user_info + ndr_uint32 :preferred_max_length, initial_value: 0xFFFFFFFF + ndr_uint32_ptr :result_handle, initial_value: 0 + + def initialize_instance + super + @opnum = NETR_WKSTA_USER_ENUM + end + end + + end + end +end + diff --git a/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb new file mode 100644 index 000000000..f172ef769 --- /dev/null +++ b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb @@ -0,0 +1,25 @@ +module RubySMB + module Dcerpc + module Wkssvc + + # [3.2.4.3 NetrWkstaUserEnum (Opnum 2)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/4af41d6f-b800-4de1-af5b-0b15a85f8e04) + class NetrWkstaUserEnumResponse < BinData::Record + attr_reader :opnum + + endian :little + + lpwkssvc_user_enum_structure :user_info + ndr_uint32_ptr :total_entries + ndr_uint32_ptr :result_handle + ndr_uint32 :error_status + + def initialize_instance + super + @opnum = NETR_WKSTA_USER_ENUM + end + end + + end + end +end + From 686fbcb62601a45c337c6431fe3c31990218ef98 Mon Sep 17 00:00:00 2001 From: NtAlexio2 Date: Sun, 1 Sep 2024 21:00:42 -0400 Subject: [PATCH 4/6] add NetrWkstaUserEnum test cases --- .../netr_wksta_get_info_request_spec.rb | 8 --- .../netr_wksta_get_info_response_spec.rb | 2 +- .../wkssvc/netr_wksta_identity_handle.rb | 7 ++ .../netr_wksta_user_enum_request_spec.rb | 71 +++++++++++++++++++ .../netr_wksta_user_enum_response_spec.rb | 65 +++++++++++++++++ spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb | 57 +++++++++++++++ 6 files changed, 201 insertions(+), 9 deletions(-) create mode 100644 spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_identity_handle.rb create mode 100644 spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb create mode 100644 spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb diff --git a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb index 3cfd38010..90ec26b0f 100644 --- a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb +++ b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb @@ -1,11 +1,3 @@ -RSpec.describe RubySMB::Dcerpc::Wkssvc::WkssvcIdentifyHandle do - subject(:packet) { described_class.new } - - it 'is a Ndr::NdrWideStringPtr' do - expect(packet).to be_a(RubySMB::Dcerpc::Ndr::NdrWideStringPtr) - end -end - RSpec.describe RubySMB::Dcerpc::Wkssvc::NetrWkstaGetInfoRequest do subject(:packet) { described_class.new } diff --git a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb index 4e8e251cb..c29c67e25 100644 --- a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb +++ b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb @@ -305,7 +305,7 @@ it 'is little endian' do expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little end - it 'it is a Ndr::NdrStruct' do + it 'is a Ndr::NdrStruct' do expect(described_class).to be < RubySMB::Dcerpc::Ndr::NdrStruct end describe '#level' do diff --git a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_identity_handle.rb b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_identity_handle.rb new file mode 100644 index 000000000..6fa4c8feb --- /dev/null +++ b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_identity_handle.rb @@ -0,0 +1,7 @@ +RSpec.describe RubySMB::Dcerpc::Wkssvc::WkssvcIdentifyHandle do + subject(:packet) { described_class.new } + + it 'is a Ndr::NdrWideStringPtr' do + expect(packet).to be_a(RubySMB::Dcerpc::Ndr::NdrWideStringPtr) + end +end diff --git a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb new file mode 100644 index 000000000..8c07268e8 --- /dev/null +++ b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb @@ -0,0 +1,71 @@ +RSpec.describe RubySMB::Dcerpc::Wkssvc::NetrWkstaUserEnumRequest do + subject(:packet) { described_class.new } + + def random_str(nb = 8) + nb.times.map { rand('a'.ord..'z'.ord).chr }.join + end + + it { is_expected.to respond_to :server_name } + it { is_expected.to respond_to :user_info } + it { is_expected.to respond_to :preferred_max_length } + it { is_expected.to respond_to :result_handle } + it { is_expected.to respond_to :opnum } + + it 'is little endian' do + expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little + end + it 'is a BinData::Record' do + expect(packet).to be_a(BinData::Record) + end + describe '#server_name' do + it 'is a WkssvcIdentifyHandle structure' do + expect(packet.server_name).to be_a RubySMB::Dcerpc::Wkssvc::WkssvcIdentifyHandle + end + end + describe '#user_info' do + it 'is a LpwkssvcUserEnumStructure structure' do + expect(packet.user_info).to be_a RubySMB::Dcerpc::Wkssvc::LpwkssvcUserEnumStructure + end + end + describe '#preferred_max_length' do + it 'is a NdrUint32 structure' do + expect(packet.preferred_max_length).to be_a RubySMB::Dcerpc::Ndr::NdrUint32 + end + + it 'has a default value of 0xFFFFFFFF' do + expect(packet.preferred_max_length).to eq(0xFFFFFFFF) + end + end + describe '#result_handle' do + it 'is a NdrUint32Ptr structure' do + expect(packet.result_handle).to be_a RubySMB::Dcerpc::Ndr::NdrUint32Ptr + end + + it 'has a default value of 0' do + expect(packet.result_handle).to eq(0) + end + end + describe '#initialize_instance' do + it 'sets #opnum to NETR_WKSTA_USER_ENUM constant' do + expect(packet.opnum).to eq(RubySMB::Dcerpc::Wkssvc::NETR_WKSTA_USER_ENUM) + end + end + it 'reads itself' do + packet = described_class.new( + server_name: 'TestServer', + user_info: { + level: RubySMB::Dcerpc::Wkssvc::WKSTA_USER_INFO_0, + info: { + wkui0_entries_read: 1, + wkui0_buffer: [{ + wkui0_username: random_str + }], + }, + }, + preferred_max_length: 0xFFFFFFFF, + result_handle: 0 + ) + binary = packet.to_binary_s + expect(described_class.read(binary)).to eq(packet) + end +end diff --git a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb new file mode 100644 index 000000000..bfae967d3 --- /dev/null +++ b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb @@ -0,0 +1,65 @@ +RSpec.describe RubySMB::Dcerpc::Wkssvc::NetrWkstaUserEnumResponse do + subject(:packet) { described_class.new } + + def random_str(nb = 8) + nb.times.map { rand('a'.ord..'z'.ord).chr }.join + end + + it { is_expected.to respond_to :user_info } + it { is_expected.to respond_to :total_entries } + it { is_expected.to respond_to :result_handle } + it { is_expected.to respond_to :error_status } + + it 'is little endian' do + expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little + end + it 'is a BinData::Record' do + expect(packet).to be_a(BinData::Record) + end + describe '#user_info' do + it 'is a LpwkssvcUserEnumStructure structure' do + expect(packet.user_info).to be_a RubySMB::Dcerpc::Wkssvc::LpwkssvcUserEnumStructure + end + end + describe '#total_entries' do + it 'is a NdrUint32Ptr structure' do + expect(packet.total_entries).to be_a RubySMB::Dcerpc::Ndr::NdrUint32Ptr + end + end + describe '#result_handle' do + it 'is a NdrUint32Ptr structure' do + expect(packet.result_handle).to be_a RubySMB::Dcerpc::Ndr::NdrUint32Ptr + end + end + describe '#error_status' do + it 'is a NdrUint32 structure' do + expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32 + end + end + describe '#initialize_instance' do + it 'sets #opnum to NETR_WKSTA_USER_ENUM constant' do + expect(packet.opnum).to eq(RubySMB::Dcerpc::Wkssvc::NETR_WKSTA_USER_ENUM) + end + end + it 'reads itself' do + packet = described_class.new( + user_info: { + level: RubySMB::Dcerpc::Wkssvc::WKSTA_USER_INFO_1, + info: { + wkui1_entries_read: 1, + wkui1_buffer: [{ + wkui1_username: random_str, + wkui1_logon_domain: random_str, + wkui1_oth_domains: random_str, + wkui1_logon_server: random_str + }], + }, + }, + total_entries: 1, + result_handle: 0, + error_status: 0 + ) + binary = packet.to_binary_s + expect(described_class.read(binary)).to eq(packet) + end +end diff --git a/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb b/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb index fb5020d8e..6a8469fe8 100644 --- a/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +++ b/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb @@ -1,3 +1,11 @@ +RSpec.describe RubySMB::Dcerpc::Wkssvc::WkssvcIdentifyHandle do + subject(:packet) { described_class.new } + + it 'is a Ndr::NdrWideStringPtr' do + expect(packet).to be_a(RubySMB::Dcerpc::Ndr::NdrWideStringPtr) + end +end + RSpec.describe RubySMB::Dcerpc::Wkssvc do let(:wkssvc) do RubySMB::SMB1::Pipe.new( @@ -67,4 +75,53 @@ end end end + + describe '#netr_wksta_user_enum' do + let(:wkst_netr_wksta_user_enum_request) { double('NetrWkstaUserEnumRequest') } + let(:response) { double('Response') } + let(:wkst_netr_wksta_user_enum_response) { double('NetrWkstaUserEnumResponse') } + let(:info) { double('info') } + before :example do + allow(described_class::NetrWkstaUserEnumRequest).to receive(:new).and_return(wkst_netr_wksta_user_enum_request) + allow(wkssvc).to receive(:dcerpc_request).and_return(response) + allow(described_class::NetrWkstaUserEnumResponse).to receive(:read).and_return(wkst_netr_wksta_user_enum_response) + allow(wkst_netr_wksta_user_enum_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS) + allow(wkst_netr_wksta_user_enum_response).to receive_message_chain(:user_info, :info => info) + end + + it 'sets the request with the expected values' do + wkssvc.netr_wksta_user_enum + expect(described_class::NetrWkstaUserEnumRequest).to have_received(:new).with( + server_name: "\x00", + user_info: { + level: described_class::WKSTA_USER_INFO_0, + tag: described_class::WKSTA_USER_INFO_0, + info: { + wkui0_entries_read: 0, + }, + }, + preferred_max_length: 0xFFFFFFFF, + result_handle: 0 + ) + end + it 'send the expected request structure' do + wkssvc.netr_wksta_user_enum + expect(wkssvc).to have_received(:dcerpc_request).with(wkst_netr_wksta_user_enum_request) + end + context 'when an IOError occurs while parsing the response' do + it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do + allow(described_class::NetrWkstaUserEnumResponse).to receive(:read).and_raise(IOError) + expect { wkssvc.netr_wksta_user_enum }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket) + end + end + context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do + it 'raises a RubySMB::Dcerpc::Error::WinregError' do + allow(wkst_netr_wksta_user_enum_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA) + expect { wkssvc.netr_wksta_user_enum }.to raise_error(RubySMB::Dcerpc::Error::WkssvcError) + end + end + it 'returns the expected handler' do + expect(wkssvc.netr_wksta_user_enum).to eq(info) + end + end end From 563542e102db063e4218e74614a78409919eab71 Mon Sep 17 00:00:00 2001 From: NtAlexio2 Date: Sun, 8 Sep 2024 03:00:47 -0400 Subject: [PATCH 5/6] minor changes in NetrWkstaUserEnum --- lib/ruby_smb/dcerpc/wkssvc.rb | 30 ++++++++----------- .../wkssvc/netr_wksta_user_enum_request.rb | 2 +- .../wkssvc/netr_wksta_user_enum_response.rb | 2 +- spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb | 4 +-- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/lib/ruby_smb/dcerpc/wkssvc.rb b/lib/ruby_smb/dcerpc/wkssvc.rb index eddfd95bf..e582e75e0 100644 --- a/lib/ruby_smb/dcerpc/wkssvc.rb +++ b/lib/ruby_smb/dcerpc/wkssvc.rb @@ -29,21 +29,18 @@ module Wkssvc WKSTA_USER_INFO_1 = 0x00000001 # [2.2.2.1 WKSSVC_IDENTIFY_HANDLE](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/9ef94a11-0e5c-49d7-9ac7-68d6f03565de) - class WkssvcIdentifyHandle < Ndr::NdrWideStringPtr; end + class WkssvcIdentifyHandle < Ndr::NdrWideStringzPtr; end # [2.2.5.9 WKSTA_USER_INFO_0](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/b7c53c6f-8b92-4e5d-9a2e-6462cb4ef1ac) - class UserInfo0 < Ndr::NdrStruct + class WkstaUserInfo0 < Ndr::NdrStruct default_parameter byte_align: 4 endian :little ndr_wide_stringz_ptr :wkui0_username end - class WkstaUserInfo0 < Ndr::NdrConfArray - default_parameter type: :user_info0 - end - - class PwkstaUserInfo0 < WkstaUserInfo0 + class WkstaUserInfo0ArrayPtr < Ndr::NdrConfArray + default_parameter type: :wksta_user_info0 extend Ndr::PointerClassPlugin end @@ -52,8 +49,8 @@ class WkstaUserInfo0Container < Ndr::NdrStruct default_parameter byte_align: 4 endian :little - ndr_uint32 :wkui0_entries_read - pwksta_user_info0 :wkui0_buffer + ndr_uint32 :wkui0_entries_read + wksta_user_info0_array_ptr :wkui0_buffer end class PwkstaUserInfo0Container < WkstaUserInfo0Container @@ -61,7 +58,7 @@ class PwkstaUserInfo0Container < WkstaUserInfo0Container end # [2.2.5.10 WKSTA_USER_INFO_1](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/c37b9606-866f-40ac-9490-57b8334968e2) - class UserInfo1 < Ndr::NdrStruct + class WkstaUserInfo1 < Ndr::NdrStruct default_parameter byte_align: 4 endian :little @@ -70,12 +67,9 @@ class UserInfo1 < Ndr::NdrStruct ndr_wide_stringz_ptr :wkui1_oth_domains ndr_wide_stringz_ptr :wkui1_logon_server end - - class WkstaUserInfo1 < Ndr::NdrConfArray - default_parameter type: :user_info1 - end - class PwkstaUserInfo1 < WkstaUserInfo1 + class WkstaUserInfo1ArrayPtr < Ndr::NdrConfArray + default_parameter type: :wksta_user_info1 extend Ndr::PointerClassPlugin end @@ -84,8 +78,8 @@ class WkstaUserInfo1Container < Ndr::NdrStruct default_parameter byte_align: 4 endian :little - ndr_uint32 :wkui1_entries_read - pwksta_user_info1 :wkui1_buffer + ndr_uint32 :wkui1_entries_read + wksta_user_info1_array_ptr :wkui1_buffer end class PwkstaUserInfo1Container < WkstaUserInfo1Container @@ -93,7 +87,7 @@ class PwkstaUserInfo1Container < WkstaUserInfo1Container end # [2.2.5.14 WKSTA_USER_ENUM_STRUCT](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/4041455a-52be-4389-a4fc-82fea3cb3160) - class LpwkssvcUserEnumStructure < Ndr::NdrStruct + class WkstaUserEnumStructure < Ndr::NdrStruct default_parameter byte_align: 4 endian :little diff --git a/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb index 6c206c021..9412bce9a 100644 --- a/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb +++ b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb @@ -9,7 +9,7 @@ class NetrWkstaUserEnumRequest < BinData::Record endian :little wkssvc_identify_handle :server_name - lpwkssvc_user_enum_structure :user_info + wksta_user_enum_structure :user_info ndr_uint32 :preferred_max_length, initial_value: 0xFFFFFFFF ndr_uint32_ptr :result_handle, initial_value: 0 diff --git a/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb index f172ef769..70515907d 100644 --- a/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb +++ b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb @@ -8,7 +8,7 @@ class NetrWkstaUserEnumResponse < BinData::Record endian :little - lpwkssvc_user_enum_structure :user_info + wksta_user_enum_structure :user_info ndr_uint32_ptr :total_entries ndr_uint32_ptr :result_handle ndr_uint32 :error_status diff --git a/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb b/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb index 6a8469fe8..07116c0c2 100644 --- a/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +++ b/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb @@ -1,8 +1,8 @@ RSpec.describe RubySMB::Dcerpc::Wkssvc::WkssvcIdentifyHandle do subject(:packet) { described_class.new } - it 'is a Ndr::NdrWideStringPtr' do - expect(packet).to be_a(RubySMB::Dcerpc::Ndr::NdrWideStringPtr) + it 'is a Ndr::NdrWideStringzPtr' do + expect(packet).to be_a(RubySMB::Dcerpc::Ndr::NdrWideStringzPtr) end end From eaf2a649a2150e022139efe7d459a9d6bb650442 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Wed, 11 Sep 2024 09:48:15 -0400 Subject: [PATCH 6/6] The server_name is automatically null terminated --- lib/ruby_smb/dcerpc/wkssvc.rb | 8 ++++---- .../dcerpc/wkssvc/netr_wksta_user_enum_response.rb | 2 +- .../dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb | 4 ++-- .../dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb | 4 ++-- spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/ruby_smb/dcerpc/wkssvc.rb b/lib/ruby_smb/dcerpc/wkssvc.rb index e582e75e0..0291e2324 100644 --- a/lib/ruby_smb/dcerpc/wkssvc.rb +++ b/lib/ruby_smb/dcerpc/wkssvc.rb @@ -72,7 +72,7 @@ class WkstaUserInfo1ArrayPtr < Ndr::NdrConfArray default_parameter type: :wksta_user_info1 extend Ndr::PointerClassPlugin end - + # [2.2.5.13 WKSTA_USER_INFO_1_CONTAINER](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/22a813e4-fc7d-4fe3-a6d6-78debfd2c0c9) class WkstaUserInfo1Container < Ndr::NdrStruct default_parameter byte_align: 4 @@ -117,7 +117,7 @@ class WkstaUserEnumStructure < Ndr::NdrStruct # NetrWkstaGetInfoResponse packet # @raise [RubySMB::Dcerpc::Error::WkssvcError] if the response error status # is not STATUS_SUCCESS - def netr_wksta_get_info(server_name: "\x00", level: WKSTA_INFO_100) + def netr_wksta_get_info(server_name: '', level: WKSTA_INFO_100) wkst_netr_wksta_get_info_request = NetrWkstaGetInfoRequest.new( server_name: server_name, level: level @@ -141,13 +141,13 @@ def netr_wksta_get_info(server_name: "\x00", level: WKSTA_INFO_100) # @param server_name [optional, String] String that identifies the server (optional # since it is ignored by the server) # @param level [optional, Integer] The information level of the data (default: WKSTA_USER_INFO_0) - # @return [RubySMB::Dcerpc::Wkssvc::WkstaUserInfo0, RubySMB::Dcerpc::Wkssvc::WkstaUserInfo1] + # @return [RubySMB::Dcerpc::Wkssvc::WkstaUserInfo0, RubySMB::Dcerpc::Wkssvc::WkstaUserInfo1] # The structure containing the requested information # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a # NetrWkstaGetInfoResponse packet # @raise [RubySMB::Dcerpc::Error::WkssvcError] if the response error status # is not STATUS_SUCCESS - def netr_wksta_user_enum(server_name: "\x00", level: WKSTA_USER_INFO_0) + def netr_wksta_user_enum(server_name: '', level: WKSTA_USER_INFO_0) wkst_netr_wksta_enum_user_request = NetrWkstaUserEnumRequest.new( server_name: server_name, user_info: { diff --git a/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb index 70515907d..49cb256a0 100644 --- a/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb +++ b/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb @@ -8,7 +8,7 @@ class NetrWkstaUserEnumResponse < BinData::Record endian :little - wksta_user_enum_structure :user_info + wksta_user_enum_structure :user_info ndr_uint32_ptr :total_entries ndr_uint32_ptr :result_handle ndr_uint32 :error_status diff --git a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb index 8c07268e8..54a18258f 100644 --- a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb +++ b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb @@ -23,8 +23,8 @@ def random_str(nb = 8) end end describe '#user_info' do - it 'is a LpwkssvcUserEnumStructure structure' do - expect(packet.user_info).to be_a RubySMB::Dcerpc::Wkssvc::LpwkssvcUserEnumStructure + it 'is a WkstaUserEnumStructure structure' do + expect(packet.user_info).to be_a RubySMB::Dcerpc::Wkssvc::WkstaUserEnumStructure end end describe '#preferred_max_length' do diff --git a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb index bfae967d3..db09dcd2f 100644 --- a/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb +++ b/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb @@ -17,8 +17,8 @@ def random_str(nb = 8) expect(packet).to be_a(BinData::Record) end describe '#user_info' do - it 'is a LpwkssvcUserEnumStructure structure' do - expect(packet.user_info).to be_a RubySMB::Dcerpc::Wkssvc::LpwkssvcUserEnumStructure + it 'is a WkstaUserEnumStructure structure' do + expect(packet.user_info).to be_a RubySMB::Dcerpc::Wkssvc::WkstaUserEnumStructure end end describe '#total_entries' do diff --git a/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb b/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb index 07116c0c2..e97152041 100644 --- a/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +++ b/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb @@ -31,7 +31,7 @@ it 'sets the request with the expected values' do wkssvc.netr_wksta_get_info expect(described_class::NetrWkstaGetInfoRequest).to have_received(:new).with( - server_name: "\x00", + server_name: '', level: described_class::WKSTA_INFO_100 ) end @@ -92,7 +92,7 @@ it 'sets the request with the expected values' do wkssvc.netr_wksta_user_enum expect(described_class::NetrWkstaUserEnumRequest).to have_received(:new).with( - server_name: "\x00", + server_name: '', user_info: { level: described_class::WKSTA_USER_INFO_0, tag: described_class::WKSTA_USER_INFO_0,