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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/metasploit-framework_acceptance.yml +47 -0
- data/lib/ruby_smb/client.rb +5 -5
- data/lib/ruby_smb/dcerpc/client.rb +2 -4
- data/lib/ruby_smb/dcerpc/icpr.rb +2 -2
- data/lib/ruby_smb/dcerpc/request.rb +4 -1
- data/lib/ruby_smb/dcerpc/samr/samr_get_members_in_group_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_get_members_in_group_response.rb +34 -0
- data/lib/ruby_smb/dcerpc/samr/samr_open_group_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/samr/samr_open_group_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr.rb +71 -1
- data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb +0 -3
- data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/wkssvc.rb +118 -3
- data/lib/ruby_smb/gss/provider/ntlm.rb +5 -9
- data/lib/ruby_smb/ntlm/client.rb +3 -74
- data/lib/ruby_smb/ntlm.rb +0 -37
- data/lib/ruby_smb/server/server_client/tree_connect.rb +2 -2
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +0 -2
- data/ruby_smb.gemspec +9 -2
- data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb +0 -8
- data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb +1 -1
- data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_identity_handle.rb +7 -0
- data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb +71 -0
- data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb +65 -0
- data/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +58 -1
- data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +119 -4
- data/spec/lib/ruby_smb/ntlm/client/session_spec.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +19 -7
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/ntlm/custom/string_encoder.rb +0 -22
- data/lib/ruby_smb/utils.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 200e26ef444f49e42d6671ae291c557dbaa6d0b7ab5424df4b7c973fa990dbe5
|
4
|
+
data.tar.gz: 27ec24e3cd852a0634c1ef11067776a887ea06ad10e2752fc0acb2e6473d8d9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/ruby_smb/client.rb
CHANGED
@@ -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 =
|
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 =
|
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 =
|
421
|
-
@username =
|
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 =
|
124
|
-
@password =
|
121
|
+
@username = username
|
122
|
+
@password = password
|
125
123
|
@max_buffer_size = MAX_BUFFER_SIZE
|
126
124
|
@call_id = 1
|
127
125
|
@ctx_id = 0
|
data/lib/ruby_smb/dcerpc/icpr.rb
CHANGED
@@ -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: (
|
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('
|
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
|
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
|
+
|
data/lib/ruby_smb/dcerpc/samr.rb
CHANGED
@@ -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 =
|
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
|
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
|
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:
|
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
|
-
|
159
|
-
|
160
|
-
|
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
|
-
|
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
|
|