Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding NetrWkstaUserEnum implementation #270

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/ruby_smb/dcerpc/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
125 changes: 123 additions & 2 deletions lib/ruby_smb/dcerpc/wkssvc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -23,17 +24,99 @@ 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
NtAlexio2 marked this conversation as resolved.
Show resolved Hide resolved

# [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
NtAlexio2 marked this conversation as resolved.
Show resolved Hide resolved

# [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
NtAlexio2 marked this conversation as resolved.
Show resolved Hide resolved
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
NtAlexio2 marked this conversation as resolved.
Show resolved Hide resolved

# [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
NtAlexio2 marked this conversation as resolved.
Show resolved Hide resolved
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
NtAlexio2 marked this conversation as resolved.
Show resolved Hide resolved
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
# computer, and the operating system version.
#
# @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
Expand All @@ -59,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
Expand Down
3 changes: 0 additions & 3 deletions lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb
Original file line number Diff line number Diff line change
@@ -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
NtAlexio2 marked this conversation as resolved.
Show resolved Hide resolved
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

25 changes: 25 additions & 0 deletions lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb
Original file line number Diff line number Diff line change
@@ -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
NtAlexio2 marked this conversation as resolved.
Show resolved Hide resolved
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

Original file line number Diff line number Diff line change
@@ -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 }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_identity_handle.rb
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading