ruby_smb 3.3.9 → 3.3.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/metasploit-framework_acceptance.yml +47 -0
  4. data/lib/ruby_smb/client.rb +5 -5
  5. data/lib/ruby_smb/dcerpc/client.rb +2 -4
  6. data/lib/ruby_smb/dcerpc/icpr.rb +2 -2
  7. data/lib/ruby_smb/dcerpc/request.rb +4 -1
  8. data/lib/ruby_smb/dcerpc/samr/samr_get_members_in_group_request.rb +23 -0
  9. data/lib/ruby_smb/dcerpc/samr/samr_get_members_in_group_response.rb +34 -0
  10. data/lib/ruby_smb/dcerpc/samr/samr_open_group_request.rb +26 -0
  11. data/lib/ruby_smb/dcerpc/samr/samr_open_group_response.rb +24 -0
  12. data/lib/ruby_smb/dcerpc/samr.rb +71 -1
  13. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb +0 -3
  14. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb +25 -0
  15. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb +25 -0
  16. data/lib/ruby_smb/dcerpc/wkssvc.rb +118 -3
  17. data/lib/ruby_smb/gss/provider/ntlm.rb +5 -9
  18. data/lib/ruby_smb/ntlm/client.rb +3 -74
  19. data/lib/ruby_smb/ntlm.rb +0 -37
  20. data/lib/ruby_smb/server/server_client/tree_connect.rb +2 -2
  21. data/lib/ruby_smb/version.rb +1 -1
  22. data/lib/ruby_smb.rb +0 -2
  23. data/ruby_smb.gemspec +9 -2
  24. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb +0 -8
  25. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb +1 -1
  26. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_identity_handle.rb +7 -0
  27. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb +71 -0
  28. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb +65 -0
  29. data/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +58 -1
  30. data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +119 -4
  31. data/spec/lib/ruby_smb/ntlm/client/session_spec.rb +1 -1
  32. data.tar.gz.sig +0 -0
  33. metadata +19 -7
  34. metadata.gz.sig +0 -0
  35. data/lib/ruby_smb/ntlm/custom/string_encoder.rb +0 -22
  36. data/lib/ruby_smb/utils.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 278206369fbc36f22011dbdaa45154a7a937468c2178e05c76b2ccc0b5032345
4
- data.tar.gz: 9fc1a683bc32bd5e35860fba5a74749611d9ae4298c3cd3a93b7b0949adea78d
3
+ metadata.gz: 200e26ef444f49e42d6671ae291c557dbaa6d0b7ab5424df4b7c973fa990dbe5
4
+ data.tar.gz: 27ec24e3cd852a0634c1ef11067776a887ea06ad10e2752fc0acb2e6473d8d9a
5
5
  SHA512:
6
- metadata.gz: d7cd41abca3c816ad18fedad7f544a42a46f8d863447678e408661d11688696bab1ef91cf85557c5757b916f5bcaee7b047231a4f35e8d4683f8a9e56a2a559b
7
- data.tar.gz: 408e4e81542732f5f31e16c8bc970629518f7a8a56c62c697cd98e3066e93280fb2270f346a63769e05d2cf9cd7bdd2b5ac9344e058c5190d9d88621d321bf24
6
+ metadata.gz: 84922acc15245aee475e8d8b3db354be2b389ef68fd9988ee2e14f850e9173b03220b51152a18bdcf097aaabbac08b78aeea7dc6e2be5ee3994e5894c5c7bc60
7
+ data.tar.gz: 1bc3d330a3c241a2e2e2b877d8b932830621fc6f12dd66cf718536a43971ab8c15bb4bcba498da72b891233a68d87d02a28396ee959652382f74d02d274bb2b0
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,47 @@
1
+ name: Metasploit Framework Acceptance
2
+
3
+ # Optional, enabling concurrency limits: https://docs.github.com/en/actions/using-jobs/using-concurrency
4
+ #concurrency:
5
+ # group: ${{ github.ref }}-${{ github.workflow }}
6
+ # cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
7
+
8
+ # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
9
+ permissions:
10
+ actions: none
11
+ checks: none
12
+ contents: none
13
+ deployments: none
14
+ id-token: none
15
+ issues: none
16
+ discussions: none
17
+ packages: none
18
+ pages: none
19
+ pull-requests: none
20
+ repository-projects: none
21
+ security-events: none
22
+ statuses: none
23
+
24
+ on:
25
+ workflow_dispatch:
26
+ inputs:
27
+ metasploitFrameworkCommit:
28
+ description: 'metasploit-framework branch would like to test'
29
+ required: true
30
+ default: 'master'
31
+ push:
32
+ branches-ignore:
33
+ - gh-pages
34
+ - metakitty
35
+ pull_request:
36
+ branches:
37
+ - '*'
38
+ # Example of running as a cron, to weed out flaky tests
39
+ # schedule:
40
+ # - cron: '*/15 * * * *'
41
+
42
+ jobs:
43
+ build:
44
+ uses: rapid7/metasploit-framework/.github/workflows/shared_smb_acceptance.yml@master
45
+ with:
46
+ metasploit_framework_commit: ${{ github.event.inputs.metasploitFrameworkCommit }}
47
+ build_smb: true
@@ -4,7 +4,6 @@ module RubySMB
4
4
  class Client
5
5
  require 'ruby_smb/ntlm'
6
6
  require 'ruby_smb/signing'
7
- require 'ruby_smb/utils'
8
7
  require 'ruby_smb/client/negotiation'
9
8
  require 'ruby_smb/client/authentication'
10
9
  require 'ruby_smb/client/tree_connect'
@@ -320,11 +319,12 @@ module RubySMB
320
319
  if smb1 == false && smb2 == false && smb3 == false
321
320
  raise ArgumentError, 'You must enable at least one Protocol'
322
321
  end
322
+
323
323
  @dispatcher = dispatcher
324
324
  @pid = rand(0xFFFF)
325
325
  @domain = domain
326
326
  @local_workstation = local_workstation
327
- @password = RubySMB::Utils.safe_encode((password||''), 'utf-8')
327
+ @password = (password || '')
328
328
  @sequence_counter = 0
329
329
  @session_id = 0x00
330
330
  @session_key = ''
@@ -334,7 +334,7 @@ module RubySMB
334
334
  @smb1 = smb1
335
335
  @smb2 = smb2
336
336
  @smb3 = smb3
337
- @username = RubySMB::Utils.safe_encode((username||''), 'utf-8')
337
+ @username = (username || '')
338
338
  @max_buffer_size = MAX_BUFFER_SIZE
339
339
  # These sizes will be modified during negotiation
340
340
  @server_max_buffer_size = SERVER_MAX_BUFFER_SIZE
@@ -417,8 +417,8 @@ module RubySMB
417
417
  local_workstation: self.local_workstation, ntlm_flags: NTLM::DEFAULT_CLIENT_FLAGS)
418
418
  @domain = domain
419
419
  @local_workstation = local_workstation
420
- @password = RubySMB::Utils.safe_encode((pass||''), 'utf-8')
421
- @username = RubySMB::Utils.safe_encode((user||''), 'utf-8')
420
+ @password = (pass || '')
421
+ @username = (user || '')
422
422
 
423
423
  @ntlm_client = RubySMB::NTLM::Client.new(
424
424
  @username,
@@ -9,12 +9,10 @@ module RubySMB
9
9
  require 'ruby_smb/dcerpc'
10
10
  require 'ruby_smb/gss'
11
11
  require 'ruby_smb/peer_info'
12
- require 'ruby_smb/utils'
13
12
 
14
13
  include Dcerpc
15
14
  include Epm
16
15
  include PeerInfo
17
- include Utils
18
16
 
19
17
  # The default maximum size of a RPC message that the Client accepts (in bytes)
20
18
  MAX_BUFFER_SIZE = 64512
@@ -120,8 +118,8 @@ module RubySMB
120
118
  @read_timeout = read_timeout
121
119
  @domain = domain
122
120
  @local_workstation = local_workstation
123
- @username = RubySMB::Utils.safe_encode(username, 'utf-8')
124
- @password = RubySMB::Utils.safe_encode(password, 'utf-8')
121
+ @username = username
122
+ @password = password
125
123
  @max_buffer_size = MAX_BUFFER_SIZE
126
124
  @call_id = 1
127
125
  @ctx_id = 0
@@ -35,7 +35,7 @@ module RubySMB
35
35
  def cert_server_request(attributes:, authority:, csr:)
36
36
  cert_server_request_request = CertServerRequestRequest.new(
37
37
  pwsz_authority: authority,
38
- pctb_attribs: { pb: (RubySMB::Utils.safe_encode(attributes.map { |k,v| "#{k}:#{v}" }.join("\n"), 'UTF-16le').force_encoding('ASCII-8bit') + "\x00\x00".b) },
38
+ pctb_attribs: { pb: (attributes.map { |k,v| "#{k}:#{v}" }.join("\n").encode('UTF-16LE').force_encoding('ASCII-8BIT') + "\x00\x00".b) },
39
39
  pctb_request: { pb: csr.to_der }
40
40
  )
41
41
 
@@ -53,7 +53,7 @@ module RubySMB
53
53
  ret = {
54
54
  certificate: nil,
55
55
  disposition: cert_server_request_response.pdw_disposition.value,
56
- disposition_message: cert_server_request_response.pctb_disposition_message.buffer.chomp("\x00\x00").force_encoding('utf-16le').encode,
56
+ disposition_message: cert_server_request_response.pctb_disposition_message.buffer.chomp("\x00\x00").force_encoding('UTF-16LE').encode,
57
57
  status: {
58
58
  CR_DISP_ISSUED => :issued,
59
59
  CR_DISP_UNDER_SUBMISSION => :submitted,
@@ -70,6 +70,8 @@ module RubySMB
70
70
  samr_close_handle_request Samr::SAMR_CLOSE_HANDLE
71
71
  samr_get_alias_membership_request Samr::SAMR_GET_ALIAS_MEMBERSHIP
72
72
  samr_open_user_request Samr::SAMR_OPEN_USER
73
+ samr_open_group_request Samr::SAMR_OPEN_GROUP
74
+ samr_get_members_in_group_request Samr::SAMR_GET_MEMBERS_IN_GROUP
73
75
  samr_get_groups_for_user_request Samr::SAMR_GET_GROUPS_FOR_USER
74
76
  samr_enumerate_domains_in_sam_server_request Samr::SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER
75
77
  samr_lookup_names_in_domain_request Samr::SAMR_LOOKUP_NAMES_IN_DOMAIN
@@ -80,7 +82,8 @@ module RubySMB
80
82
  string :default
81
83
  end
82
84
  choice 'Wkssvc', selection: -> { opnum } do
83
- netr_wksta_get_info_request Wkssvc::NETR_WKSTA_GET_INFO
85
+ netr_wksta_get_info_request Wkssvc::NETR_WKSTA_GET_INFO
86
+ netr_wksta_user_enum_request Wkssvc::NETR_WKSTA_USER_ENUM
84
87
  string :default
85
88
  end
86
89
  choice 'Epm', selection: -> { opnum } do
@@ -0,0 +1,23 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Samr
4
+
5
+ # [3.1.5.8.3 SamrGetMembersInGroup (Opnum 25)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/3ed5030d-88a3-42ca-a6e0-8c12aa2fdfbd)
6
+ class SamrGetMembersInGroupRequest < BinData::Record
7
+ attr_reader :opnum
8
+
9
+ endian :little
10
+
11
+ sampr_handle :group_handle
12
+
13
+ def initialize_instance
14
+ super
15
+ @opnum = SAMR_GET_MEMBERS_IN_GROUP
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+
23
+
@@ -0,0 +1,34 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Samr
4
+ # [2.2.7.14 SAMPR_GET_MEMBERS_BUFFER](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/225147b1-45b7-4fde-a5bf-bf420e18fa08)
5
+ class SamprGetMembersBuffer < Ndr::NdrStruct
6
+ default_parameter byte_align: 4
7
+
8
+ ndr_uint32 :member_count
9
+ ndr_uint32_conf_array_ptr :members, type: :ndr_uint32
10
+ ndr_uint32_conf_array_ptr :attributes, type: :ndr_uint32
11
+ end
12
+
13
+ class PsamprGetMembersBuffer < SamprGetMembersBuffer
14
+ extend Ndr::PointerClassPlugin
15
+ end
16
+
17
+ # [2.1.5.8.3 SamrGetMembersInGroup (Opnum 25)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/a4adbf20-040f-4416-a960-e5b7917fdae7)
18
+ class SamrGetMembersInGroupResponse < BinData::Record
19
+ attr_reader :opnum
20
+
21
+ endian :little
22
+
23
+ psampr_get_members_buffer :members
24
+ ndr_uint32 :error_status
25
+
26
+ def initialize_instance
27
+ super
28
+ @opnum = SAMR_GET_MEMBERS_IN_GROUP
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,26 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Samr
4
+
5
+ # [3.1.5.1.7 SamrOpenGroup (Opnum 19)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/d396e6c9-d04a-4729-b0d8-f50f2748f3c8)
6
+ class SamrOpenGroupRequest < BinData::Record
7
+ attr_reader :opnum
8
+
9
+ endian :little
10
+
11
+ sampr_handle :domain_handle
12
+ # Access control on a server object: bitwise OR of common ACCESS_MASK
13
+ # and user ACCESS_MASK values (see lib/ruby_smb/dcerpc/samr.rb)
14
+ ndr_uint32 :desired_access
15
+ ndr_uint32 :group_id
16
+
17
+ def initialize_instance
18
+ super
19
+ @opnum = SAMR_OPEN_GROUP
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,24 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Samr
4
+
5
+ # [3.1.5.1.7 SamrOpenGroup (Opnum 19)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/d396e6c9-d04a-4729-b0d8-f50f2748f3c8)
6
+ class SamrOpenGroupResponse < BinData::Record
7
+ attr_reader :opnum
8
+
9
+ endian :little
10
+
11
+ sampr_handle :group_handle
12
+ ndr_uint32 :error_status
13
+
14
+ def initialize_instance
15
+ super
16
+ @opnum = SAMR_OPEN_GROUP
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+
24
+
@@ -20,6 +20,8 @@ module RubySMB
20
20
  SAMR_ENUMERATE_USERS_IN_DOMAIN = 0x000D
21
21
  SAMR_GET_ALIAS_MEMBERSHIP = 0x0010
22
22
  SAMR_LOOKUP_NAMES_IN_DOMAIN = 0x0011
23
+ SAMR_OPEN_GROUP = 0x0013
24
+ SAMR_GET_MEMBERS_IN_GROUP = 0x0019
23
25
  SAMR_OPEN_USER = 0x0022
24
26
  SAMR_DELETE_USER = 0x0023
25
27
  SAMR_GET_GROUPS_FOR_USER = 0x0027
@@ -337,7 +339,7 @@ module RubySMB
337
339
 
338
340
  def self.encrypt_password(password, key)
339
341
  # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/5fe3c4c4-e71b-440d-b2fd-8448bfaf6e04
340
- password = RubySMB::Utils.safe_encode(password, 'UTF-16LE').force_encoding('ASCII-8bit')
342
+ password = password.encode('UTF-16LE').force_encoding('ASCII-8BIT')
341
343
  buffer = password.rjust(512, "\x00") + [ password.length ].pack('V')
342
344
  salt = SecureRandom.random_bytes(16)
343
345
  key = OpenSSL::Digest::MD5.new(salt + key).digest
@@ -509,8 +511,12 @@ module RubySMB
509
511
  require 'ruby_smb/dcerpc/samr/samr_rid_to_sid_response'
510
512
  require 'ruby_smb/dcerpc/samr/samr_close_handle_request'
511
513
  require 'ruby_smb/dcerpc/samr/samr_close_handle_response'
514
+ require 'ruby_smb/dcerpc/samr/samr_get_members_in_group_request'
515
+ require 'ruby_smb/dcerpc/samr/samr_get_members_in_group_response'
512
516
  require 'ruby_smb/dcerpc/samr/samr_get_alias_membership_request'
513
517
  require 'ruby_smb/dcerpc/samr/samr_get_alias_membership_response'
518
+ require 'ruby_smb/dcerpc/samr/samr_open_group_request'
519
+ require 'ruby_smb/dcerpc/samr/samr_open_group_response'
514
520
  require 'ruby_smb/dcerpc/samr/samr_open_user_request'
515
521
  require 'ruby_smb/dcerpc/samr/samr_open_user_response'
516
522
  require 'ruby_smb/dcerpc/samr/samr_get_groups_for_user_request'
@@ -935,6 +941,40 @@ module RubySMB
935
941
  samr_get_alias_membership_reponse.membership.elements.to_ary
936
942
  end
937
943
 
944
+ # Returns a handle to a group, given a RID
945
+ #
946
+ # @param domain_handle [RubySMB::Dcerpc::Samr::SamprHandle] An RPC context
947
+ # representing a domain object
948
+ # @param access [Integer] An access control that indicates the requested
949
+ # access for the returned handle. It is a bitwise OR of common
950
+ # ACCESS_MASK and user ACCESS_MASK values (see
951
+ # lib/ruby_smb/dcerpc/samr.rb)
952
+ # @param group_id [Integer] RID of a group
953
+ # @return [RubySMB::Dcerpc::Samr::SamprHandle] The group handle
954
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a
955
+ # SamrOpenGroup packet
956
+ # @raise [RubySMB::Dcerpc::Error::SamrError] if the response error status
957
+ # is not STATUS_SUCCESS
958
+ def samr_open_group(domain_handle:, access: MAXIMUM_ALLOWED, group_id:)
959
+ samr_open_group_request = SamrOpenGroupRequest.new(
960
+ domain_handle: domain_handle,
961
+ desired_access: access,
962
+ group_id: group_id
963
+ )
964
+ response = dcerpc_request(samr_open_group_request)
965
+ begin
966
+ samr_open_group_response = SamrOpenGroupResponse.read(response)
967
+ rescue IOError
968
+ raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading SamrOpenGroupResponse'
969
+ end
970
+ unless samr_open_group_response.error_status == WindowsError::NTStatus::STATUS_SUCCESS
971
+ raise RubySMB::Dcerpc::Error::SamrError,
972
+ "Error returned when getting a handle to group #{group_id}: "\
973
+ "#{WindowsError::NTStatus.find_by_retval(samr_open_grou_response.error_status.value).join(',')}"
974
+ end
975
+ samr_open_group_response.group_handle
976
+ end
977
+
938
978
  # Returns a handle to a user, given a RID
939
979
  #
940
980
  # @param domain_handle [RubySMB::Dcerpc::Samr::SamprHandle] An RPC context
@@ -969,6 +1009,36 @@ module RubySMB
969
1009
  samr_open_user_response.user_handle
970
1010
  end
971
1011
 
1012
+ # Returns a listing of members of the given group
1013
+ #
1014
+ # @param group_handle [RubySMB::Dcerpc::Samr::SamprHandle] An RPC context
1015
+ # representing a group object.
1016
+ # @return [Array<Array<String,String>>] Array of RID and Attributes
1017
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a
1018
+ # SamrGetMembersInGroup packet
1019
+ # @raise [RubySMB::Dcerpc::Error::SamrError] if the response error status
1020
+ # is not STATUS_SUCCESS
1021
+ def samr_get_members_in_group(group_handle:)
1022
+ samr_get_members_in_group_request = SamrGetMembersInGroupRequest.new(
1023
+ group_handle: group_handle
1024
+ )
1025
+ response = dcerpc_request(samr_get_members_in_group_request)
1026
+ begin
1027
+ samr_get_members_in_group_response = SamrGetMembersInGroupResponse.read(response)
1028
+ rescue IOError
1029
+ raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading SamrGetMembersInGroupResponse'
1030
+ end
1031
+ unless samr_get_members_in_group_response.error_status == WindowsError::NTStatus::STATUS_SUCCESS
1032
+ raise RubySMB::Dcerpc::Error::SamrError,
1033
+ "Error returned while getting group membership: "\
1034
+ "#{WindowsError::NTStatus.find_by_retval(samr_get_members_in_group_response.error_status.value).join(',')}"
1035
+ end
1036
+ members = samr_get_members_in_group_response.members.members.to_ary
1037
+ attributes = samr_get_members_in_group_response.members.attributes.to_ary
1038
+
1039
+ members.zip(attributes)
1040
+ end
1041
+
972
1042
  # Returns a listing of groups that a user is a member of
973
1043
  #
974
1044
  # @param user_handle [RubySMB::Dcerpc::Samr::SamprHandle] An RPC context
@@ -2,9 +2,6 @@ module RubySMB
2
2
  module Dcerpc
3
3
  module Wkssvc
4
4
 
5
- # [2.2.2.1 WKSSVC_IDENTIFY_HANDLE](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/9ef94a11-0e5c-49d7-9ac7-68d6f03565de)
6
- class WkssvcIdentifyHandle < Ndr::NdrWideStringPtr; end
7
-
8
5
  # [3.2.4.1 NetrWkstaGetInfo (Opnum 0)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/4af41d6f-b800-4de1-af5b-0b15a85f8e04)
9
6
  class NetrWkstaGetInfoRequest < BinData::Record
10
7
  attr_reader :opnum
@@ -0,0 +1,25 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Wkssvc
4
+
5
+ # [3.2.4.3 NetrWkstaUserEnum (Opnum 2)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/4af41d6f-b800-4de1-af5b-0b15a85f8e04)
6
+ class NetrWkstaUserEnumRequest < BinData::Record
7
+ attr_reader :opnum
8
+
9
+ endian :little
10
+
11
+ wkssvc_identify_handle :server_name
12
+ wksta_user_enum_structure :user_info
13
+ ndr_uint32 :preferred_max_length, initial_value: 0xFFFFFFFF
14
+ ndr_uint32_ptr :result_handle, initial_value: 0
15
+
16
+ def initialize_instance
17
+ super
18
+ @opnum = NETR_WKSTA_USER_ENUM
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,25 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Wkssvc
4
+
5
+ # [3.2.4.3 NetrWkstaUserEnum (Opnum 2)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/4af41d6f-b800-4de1-af5b-0b15a85f8e04)
6
+ class NetrWkstaUserEnumResponse < BinData::Record
7
+ attr_reader :opnum
8
+
9
+ endian :little
10
+
11
+ wksta_user_enum_structure :user_info
12
+ ndr_uint32_ptr :total_entries
13
+ ndr_uint32_ptr :result_handle
14
+ ndr_uint32 :error_status
15
+
16
+ def initialize_instance
17
+ super
18
+ @opnum = NETR_WKSTA_USER_ENUM
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+
@@ -7,7 +7,8 @@ module RubySMB
7
7
  VER_MINOR = 0
8
8
 
9
9
  # Operation numbers
10
- NETR_WKSTA_GET_INFO = 0x0000
10
+ NETR_WKSTA_GET_INFO = 0x0000
11
+ NETR_WKSTA_USER_ENUM = 0x0002
11
12
 
12
13
  PLATFORM_ID = {
13
14
  0x0000012C => "DOS",
@@ -23,9 +24,85 @@ module RubySMB
23
24
  WKSTA_INFO_102 = 0x00000066
24
25
  #TODO: WKSTA_INFO_502 = 0x000001F6
25
26
 
27
+ # User Enum Information Level
28
+ WKSTA_USER_INFO_0 = 0x00000000
29
+ WKSTA_USER_INFO_1 = 0x00000001
30
+
31
+ # [2.2.2.1 WKSSVC_IDENTIFY_HANDLE](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/9ef94a11-0e5c-49d7-9ac7-68d6f03565de)
32
+ class WkssvcIdentifyHandle < Ndr::NdrWideStringzPtr; end
33
+
34
+ # [2.2.5.9 WKSTA_USER_INFO_0](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/b7c53c6f-8b92-4e5d-9a2e-6462cb4ef1ac)
35
+ class WkstaUserInfo0 < Ndr::NdrStruct
36
+ default_parameter byte_align: 4
37
+ endian :little
38
+
39
+ ndr_wide_stringz_ptr :wkui0_username
40
+ end
41
+
42
+ class WkstaUserInfo0ArrayPtr < Ndr::NdrConfArray
43
+ default_parameter type: :wksta_user_info0
44
+ extend Ndr::PointerClassPlugin
45
+ end
46
+
47
+ # [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)
48
+ class WkstaUserInfo0Container < Ndr::NdrStruct
49
+ default_parameter byte_align: 4
50
+ endian :little
51
+
52
+ ndr_uint32 :wkui0_entries_read
53
+ wksta_user_info0_array_ptr :wkui0_buffer
54
+ end
55
+
56
+ class PwkstaUserInfo0Container < WkstaUserInfo0Container
57
+ extend Ndr::PointerClassPlugin
58
+ end
59
+
60
+ # [2.2.5.10 WKSTA_USER_INFO_1](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/c37b9606-866f-40ac-9490-57b8334968e2)
61
+ class WkstaUserInfo1 < Ndr::NdrStruct
62
+ default_parameter byte_align: 4
63
+ endian :little
64
+
65
+ ndr_wide_stringz_ptr :wkui1_username
66
+ ndr_wide_stringz_ptr :wkui1_logon_domain
67
+ ndr_wide_stringz_ptr :wkui1_oth_domains
68
+ ndr_wide_stringz_ptr :wkui1_logon_server
69
+ end
70
+
71
+ class WkstaUserInfo1ArrayPtr < Ndr::NdrConfArray
72
+ default_parameter type: :wksta_user_info1
73
+ extend Ndr::PointerClassPlugin
74
+ end
75
+
76
+ # [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)
77
+ class WkstaUserInfo1Container < Ndr::NdrStruct
78
+ default_parameter byte_align: 4
79
+ endian :little
80
+
81
+ ndr_uint32 :wkui1_entries_read
82
+ wksta_user_info1_array_ptr :wkui1_buffer
83
+ end
84
+
85
+ class PwkstaUserInfo1Container < WkstaUserInfo1Container
86
+ extend Ndr::PointerClassPlugin
87
+ end
88
+
89
+ # [2.2.5.14 WKSTA_USER_ENUM_STRUCT](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wkst/4041455a-52be-4389-a4fc-82fea3cb3160)
90
+ class WkstaUserEnumStructure < Ndr::NdrStruct
91
+ default_parameter byte_align: 4
92
+ endian :little
93
+
94
+ ndr_uint32 :level
95
+ ndr_uint32 :tag, value: -> { self.level }
96
+ choice :info, selection: :level, byte_align: 4 do
97
+ pwksta_user_info0_container WKSTA_USER_INFO_0
98
+ pwksta_user_info1_container WKSTA_USER_INFO_1
99
+ end
100
+ end
26
101
 
27
102
  require 'ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request'
28
103
  require 'ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response'
104
+ require 'ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request'
105
+ require 'ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response'
29
106
 
30
107
  # Returns details about a computer environment, including
31
108
  # platform-specific information, the names of the domain and local
@@ -33,14 +110,14 @@ module RubySMB
33
110
  #
34
111
  # @param server_name [optional, String] String that identifies the server (optional
35
112
  # since it is ignored by the server)
36
- # @param server_name [optional, Integer] The information level of the data (default: WKSTA_INFO_100)
113
+ # @param level [optional, Integer] The information level of the data (default: WKSTA_INFO_100)
37
114
  # @return [RubySMB::Dcerpc::Wkssvc::WkstaInfo100, RubySMB::Dcerpc::Wkssvc::WkstaInfo101,
38
115
  # RubySMB::Dcerpc::Wkssvc::WkstaInfo102] The structure containing the requested information
39
116
  # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a
40
117
  # NetrWkstaGetInfoResponse packet
41
118
  # @raise [RubySMB::Dcerpc::Error::WkssvcError] if the response error status
42
119
  # is not STATUS_SUCCESS
43
- def netr_wksta_get_info(server_name: "\x00", level: WKSTA_INFO_100)
120
+ def netr_wksta_get_info(server_name: '', level: WKSTA_INFO_100)
44
121
  wkst_netr_wksta_get_info_request = NetrWkstaGetInfoRequest.new(
45
122
  server_name: server_name,
46
123
  level: level
@@ -59,6 +136,44 @@ module RubySMB
59
136
  wkst_netr_wksta_get_info_response.wksta_info.info
60
137
  end
61
138
 
139
+ # Returns details about users who are currently active on a remote computer.
140
+ #
141
+ # @param server_name [optional, String] String that identifies the server (optional
142
+ # since it is ignored by the server)
143
+ # @param level [optional, Integer] The information level of the data (default: WKSTA_USER_INFO_0)
144
+ # @return [RubySMB::Dcerpc::Wkssvc::WkstaUserInfo0, RubySMB::Dcerpc::Wkssvc::WkstaUserInfo1]
145
+ # The structure containing the requested information
146
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a
147
+ # NetrWkstaGetInfoResponse packet
148
+ # @raise [RubySMB::Dcerpc::Error::WkssvcError] if the response error status
149
+ # is not STATUS_SUCCESS
150
+ def netr_wksta_user_enum(server_name: '', level: WKSTA_USER_INFO_0)
151
+ wkst_netr_wksta_enum_user_request = NetrWkstaUserEnumRequest.new(
152
+ server_name: server_name,
153
+ user_info: {
154
+ level: level,
155
+ tag: level,
156
+ info: {
157
+ wkui0_entries_read: 0,
158
+ },
159
+ },
160
+ preferred_max_length: 0xFFFFFFFF,
161
+ result_handle: 0
162
+ )
163
+ response = dcerpc_request(wkst_netr_wksta_enum_user_request)
164
+ begin
165
+ wkst_netr_wksta_enum_user_response = NetrWkstaUserEnumResponse.read(response)
166
+ rescue IOError
167
+ raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading WkstNetrWkstaUserEnumResponse'
168
+ end
169
+ unless wkst_netr_wksta_enum_user_response.error_status == WindowsError::NTStatus::STATUS_SUCCESS
170
+ raise RubySMB::Dcerpc::Error::WkssvcError,
171
+ "Error returned with netr_wksta_enum_user: #{wkst_netr_wksta_enum_user_response.error_status.value} - "\
172
+ "#{WindowsError::NTStatus.find_by_retval(wkst_netr_wksta_enum_user_response.error_status.value).join(',')}"
173
+ end
174
+ wkst_netr_wksta_enum_user_response.user_info.info
175
+ end
176
+
62
177
  end
63
178
  end
64
179
  end
@@ -142,10 +142,7 @@ module RubySMB
142
142
  case type3_msg.ntlm_version
143
143
  when :ntlmv1
144
144
  my_ntlm_response = Net::NTLM::ntlm_response(
145
- ntlm_hash: Net::NTLM::ntlm_hash(
146
- RubySMB::Utils.safe_encode(account.password, 'UTF-16LE'),
147
- unicode: true
148
- ),
145
+ ntlm_hash: Net::NTLM::ntlm_hash(account.password),
149
146
  challenge: @server_challenge
150
147
  )
151
148
  matches = my_ntlm_response == type3_msg.ntlm_response
@@ -155,9 +152,9 @@ module RubySMB
155
152
  their_blob = type3_msg.ntlm_response[digest.digest_length..-1]
156
153
 
157
154
  ntlmv2_hash = Net::NTLM.ntlmv2_hash(
158
- RubySMB::Utils.safe_encode(account.username, 'UTF-16LE'),
159
- RubySMB::Utils.safe_encode(account.password, 'UTF-16LE'),
160
- RubySMB::Utils.safe_encode(type3_msg.domain, 'UTF-16LE'), # don't use the account domain because of the special '.' value
155
+ Net::NTLM::EncodeUtil.encode_utf16le(account.username),
156
+ Net::NTLM::EncodeUtil.encode_utf16le(account.password),
157
+ type3_msg.domain.dup.force_encoding('ASCII-8BIT'), # don't use the account domain because of the special '.' value
161
158
  {client_challenge: their_blob[16...24], unicode: true}
162
159
  )
163
160
 
@@ -310,8 +307,7 @@ module RubySMB
310
307
  domain = @default_domain if domain.nil? || domain == '.'.encode(domain.encoding)
311
308
  domain = domain.downcase
312
309
  @accounts.find do |account|
313
- RubySMB::Utils.safe_encode(account.username, username.encoding).downcase == username &&
314
- RubySMB::Utils.safe_encode(account.domain, domain.encoding).downcase == domain
310
+ account.username.encode(username.encoding).downcase == username && account.domain.encode(domain.encoding).downcase == domain
315
311
  end
316
312
  end
317
313