ruby_smb 3.3.10 → 3.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07f6abd7129c406c8b1c008c686eef226b8fa385fa06be6eec900b95b4f97a32
4
- data.tar.gz: 4ddc7ee9c516a1649faa15ad4316e36149aa4304c4dc176dfd5234f46f9218be
3
+ metadata.gz: d9ee813b67da3f28bf15f24cd60e6ff6be3c931490f97fe95786537d191a171b
4
+ data.tar.gz: 0d36cfa0892d145bf40d1ad43ba8e0d35185357c50d35ce8e7bc64a586041bcf
5
5
  SHA512:
6
- metadata.gz: e948d03583b517435a912b4ea07086ee0bff1fc07c0f65f0ea89e51ec10ee4ab2074d33a82c4f774656909c938331a7080fb521e5e65989bb301e10cd80d1bf5
7
- data.tar.gz: 2f964acae7e74b25cb49e3d47ea125d4f4669f3f8868b9e895bb2f63be9e465e3fc1021e6695a1b837616a0f92419708ff4af6cb894b630d0b6292c95e3b4599
6
+ metadata.gz: b52b91a93e363be0ed1578a6b67c94409e831a848da346cf1e985e1686804bbf444e1ba15d3a6f350cb77bba9a8a035b06c4c066b96665399805da8bc0193011
7
+ data.tar.gz: 17d2c3bc2bb46204538a827aac81a9f46317518cbf24abdade18fd60460b57db131ed3d6d8db3cce8accadd752256c7fb8fc53b74a435a1226048166d93c0020
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
@@ -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
@@ -441,8 +443,8 @@ module RubySMB
441
443
  uint16 :reserved3
442
444
  string :reserved4, length: 96
443
445
  uint16 :property_signature, initial_value: 0x50
444
- uint16 :property_count, initial_value: -> { user_properties.size }
445
- array :user_properties, type: :user_property, initial_length: :property_count
446
+ uint16 :property_count, initial_value: -> { user_properties.size }, onlyif: -> { struct_length > 111 }
447
+ array :user_properties, type: :user_property, initial_length: :property_count, onlyif: :property_count?
446
448
  uint8 :reserved5
447
449
  end
448
450
 
@@ -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
@@ -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
@@ -157,7 +154,7 @@ module RubySMB
157
154
  ntlmv2_hash = Net::NTLM.ntlmv2_hash(
158
155
  Net::NTLM::EncodeUtil.encode_utf16le(account.username),
159
156
  Net::NTLM::EncodeUtil.encode_utf16le(account.password),
160
- type3_msg.domain.force_encoding('ASCII-8BIT'), # don't use the account domain because of the special '.' value
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
 
@@ -1,78 +1,7 @@
1
1
  module RubySMB::NTLM
2
- module Message
3
- def deflag
4
- security_buffers.inject(head_size) do |cur, a|
5
- a[1].offset = cur
6
- cur += a[1].data_size
7
- has_flag?(:UNICODE) ? cur + cur % 2 : cur
8
- end
9
- end
10
-
11
- def serialize
12
- deflag
13
- @alist.map { |n, f| f.serialize }.join + security_buffers.map { |n, f| f.value + (has_flag?(:UNICODE) ? "\x00".b * (f.value.length % 2) : '') }.join
14
- end
15
- end
16
-
17
2
  class Client < Net::NTLM::Client
18
- class Session < Net::NTLM::Client::Session
19
- def authenticate!
20
- calculate_user_session_key!
21
- type3_opts = {
22
- :lm_response => is_anonymous? ? "\x00".b : lmv2_resp,
23
- :ntlm_response => is_anonymous? ? '' : ntlmv2_resp,
24
- :domain => domain,
25
- :user => username,
26
- :workstation => workstation,
27
- :flag => (challenge_message.flag & client.flags)
28
- }
29
- t3 = Net::NTLM::Message::Type3.create type3_opts
30
- t3.extend(Message)
31
- if negotiate_key_exchange?
32
- t3.enable(:session_key)
33
- rc4 = OpenSSL::Cipher.new("rc4")
34
- rc4.encrypt
35
- rc4.key = user_session_key
36
- sk = rc4.update exported_session_key
37
- sk << rc4.final
38
- t3.session_key = sk
39
- end
40
- t3
41
- end
42
-
43
- def is_anonymous?
44
- username == '' && password == ''
45
- end
46
-
47
- private
48
-
49
- def use_oem_strings?
50
- # @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832
51
- !challenge_message.has_flag?(:UNICODE) && challenge_message.has_flag?(:OEM)
52
- end
53
-
54
- def ntlmv2_hash
55
- @ntlmv2_hash ||= RubySMB::NTLM.ntlmv2_hash(username, password, domain, {:client_challenge => client_challenge, :unicode => !use_oem_strings?})
56
- end
57
-
58
- def calculate_user_session_key!
59
- if is_anonymous?
60
- # see MS-NLMP section 3.4
61
- @user_session_key = "\x00".b * 16
62
- else
63
- @user_session_key = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, nt_proof_str)
64
- end
65
- end
66
- end
67
-
68
- def init_context(resp = nil, channel_binding = nil)
69
- if resp.nil?
70
- @session = nil
71
- type1_message
72
- else
73
- @session = Client::Session.new(self, Net::NTLM::Message.decode64(resp), channel_binding)
74
- @session.authenticate!
75
- end
76
- end
3
+ # There was a bunch of code in here that was necessary in versions up to and including rubyntlm version 0.6.3.
4
+ # The class is kept because there are references to it that should be kept in place in case future alterations to
5
+ # rubyntlm are required.
77
6
  end
78
7
  end
data/lib/ruby_smb/ntlm.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'ruby_smb/ntlm/custom/string_encoder'
2
-
3
1
  module RubySMB
4
2
  module NTLM
5
3
  # [[MS-NLMP] 2.2.2.5](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832)
@@ -58,41 +56,6 @@ module RubySMB
58
56
  "Version #{major}.#{minor} (Build #{build}); NTLM Current Revision #{ntlm_revision}"
59
57
  end
60
58
  end
61
-
62
- class << self
63
-
64
- # Generate a NTLMv2 Hash
65
- # @param [String] user The username
66
- # @param [String] password The password
67
- # @param [String] target The domain or workstation to authenticate to
68
- # @option opt :unicode (false) Unicode encode the domain
69
- def ntlmv2_hash(user, password, target, opt={})
70
- if Net::NTLM.is_ntlm_hash? password
71
- decoded_password = Net::NTLM::EncodeUtil.decode_utf16le(password)
72
- ntlmhash = [decoded_password.upcase[33,65]].pack('H32')
73
- else
74
- ntlmhash = Net::NTLM.ntlm_hash(password, opt)
75
- end
76
-
77
- if opt[:unicode]
78
- # Uppercase operation on username containing non-ASCII characters
79
- # after being unicode encoded with `EncodeUtil.encode_utf16le`
80
- # doesn't play well. Upcase should be done before encoding.
81
- user_upcase = Net::NTLM::EncodeUtil.decode_utf16le(user).upcase
82
- user_upcase = Net::NTLM::EncodeUtil.encode_utf16le(user_upcase)
83
- else
84
- user_upcase = user.upcase
85
- end
86
- userdomain = user_upcase + target
87
-
88
- unless opt[:unicode]
89
- userdomain = Net::NTLM::EncodeUtil.encode_utf16le(userdomain)
90
- end
91
- OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
92
- end
93
-
94
- end
95
-
96
59
  end
97
60
  end
98
61
 
@@ -7,7 +7,7 @@ module RubySMB
7
7
  # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/b062f3e3-1b65-4a9a-854a-0ee432499d8f
8
8
  response = RubySMB::SMB1::Packet::TreeConnectResponse.new
9
9
 
10
- share_name = RubySMB::Utils.safe_encode(request.data_block.path, 'UTF-8').split('\\', 4).last
10
+ share_name = request.data_block.path.split('\\', 4).last
11
11
  share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
12
12
  if share_provider.nil?
13
13
  logger.warn("Received TREE_CONNECT request for non-existent share: #{share_name}")
@@ -51,7 +51,7 @@ module RubySMB
51
51
  return response
52
52
  end
53
53
 
54
- share_name = RubySMB::Utils.safe_encode(request.path, 'UTF-8').split('\\', 4).last
54
+ share_name = request.path.encode.split('\\', 4).last
55
55
  share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
56
56
 
57
57
  if share_provider.nil?
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.3.10'.freeze
2
+ VERSION = '3.3.12'.freeze
3
3
  end
data/lib/ruby_smb.rb CHANGED
@@ -6,13 +6,11 @@ require 'openssl/ccm'
6
6
  require 'openssl/cmac'
7
7
  require 'windows_error'
8
8
  require 'windows_error/nt_status'
9
- require 'ruby_smb/ntlm/custom/string_encoder'
10
9
  # A packet parsing and manipulation library for the SMB1 and SMB2 protocols
11
10
  #
12
11
  # [[MS-SMB] Server Message Block (SMB) Protocol Version 1](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
13
12
  # [[MS-SMB2] Server Message Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
14
13
  module RubySMB
15
- require 'ruby_smb/utils'
16
14
  require 'ruby_smb/error'
17
15
  require 'ruby_smb/create_actions'
18
16
  require 'ruby_smb/dispositions'
data/ruby_smb.gemspec CHANGED
@@ -6,7 +6,14 @@ require 'ruby_smb/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'ruby_smb'
8
8
  spec.version = RubySMB::VERSION
9
- spec.authors = ['Metasploit Hackers', 'David Maloney', 'James Lee', 'Dev Mohanty', 'Christophe De La Fuente']
9
+ spec.authors = [
10
+ 'Metasploit Hackers',
11
+ 'David Maloney',
12
+ 'James Lee',
13
+ 'Dev Mohanty',
14
+ 'Christophe De La Fuente',
15
+ 'Spencer McIntyre'
16
+ ]
10
17
  spec.email = ['msfdev@metasploit.com']
11
18
  spec.summary = 'A pure Ruby implementation of the SMB Protocol Family'
12
19
  spec.description = ''
@@ -33,7 +40,7 @@ Gem::Specification.new do |spec|
33
40
  spec.add_development_dependency 'rake'
34
41
  spec.add_development_dependency 'yard'
35
42
 
36
- spec.add_runtime_dependency 'rubyntlm'
43
+ spec.add_runtime_dependency 'rubyntlm', '>= 0.6.5'
37
44
  spec.add_runtime_dependency 'windows_error', '>= 0.1.4'
38
45
  spec.add_runtime_dependency 'bindata', '2.4.15'
39
46
  spec.add_runtime_dependency 'openssl-ccm'
@@ -9,8 +9,11 @@ RSpec.describe RubySMB::Gss::Provider::NTLM::Authenticator do
9
9
  msg.domain = domain
10
10
  end
11
11
  end
12
+ let(:type2_msg) do
13
+ Net::NTLM::Message::Type2.new
14
+ end
12
15
  let(:type3_msg) do
13
- Net::NTLM::Message::Type2.new.response(user: username, password: '', domain: domain)
16
+ type2_msg.response({user: username, password: password, domain: domain}, {ntlmv2: true})
14
17
  end
15
18
 
16
19
  before(:each) do
@@ -65,9 +68,121 @@ RSpec.describe RubySMB::Gss::Provider::NTLM::Authenticator do
65
68
  end
66
69
 
67
70
  describe '#process_ntlm_type3' do
68
- it 'should process a NTLM type 3 message and return an error code' do
69
- expect(authenticator.process_ntlm_type3(type3_msg)).to be_a WindowsError::ErrorCode
70
- expect(authenticator.process_ntlm_type3(type3_msg)).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
71
+ context 'when the message is anonymous' do
72
+ let(:type3_msg) do
73
+ type2_msg.response({user: '', password: ''}, {ntlmv2: true})
74
+ end
75
+
76
+ context 'when anonymous access is disabled' do
77
+ before(:each) do
78
+ expect(provider).to_not receive(:allow_guests)
79
+ expect(provider).to receive(:allow_anonymous).and_return(false)
80
+ end
81
+
82
+ it 'should process a NTLM type 3 message and return STATUS_LOGON_FAILURE' do
83
+ status = authenticator.process_ntlm_type3(type3_msg)
84
+ expect(status).to be_a WindowsError::ErrorCode
85
+ expect(status).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
86
+ end
87
+
88
+ after(:each) do
89
+ expect(authenticator.session_key).to be_nil
90
+ end
91
+ end
92
+
93
+ context 'when anonymous access is enabled' do
94
+ before(:each) do
95
+ expect(provider).to_not receive(:allow_guests)
96
+ expect(provider).to receive(:allow_anonymous).and_return(true)
97
+ end
98
+
99
+ it 'should process a NTLM type 3 message and return STATUS_SUCCESS' do
100
+ status = authenticator.process_ntlm_type3(type3_msg)
101
+ expect(status).to be_a WindowsError::ErrorCode
102
+ expect(status).to eq WindowsError::NTStatus::STATUS_SUCCESS
103
+ end
104
+
105
+ after(:each) do
106
+ expect(authenticator.session_key).to eq "\x00".b * 16
107
+ end
108
+ end
109
+ end
110
+
111
+ context 'when the message is a guest' do
112
+ let(:type3_msg) do
113
+ type2_msg.response({user: 'Spencer', password: password}, {ntlmv2: true})
114
+ end
115
+
116
+ context 'when guest access is disabled' do
117
+ before(:each) do
118
+ expect(provider).to_not receive(:allow_anonymous)
119
+ expect(provider).to receive(:allow_guests).and_return(false)
120
+ end
121
+
122
+ it 'should process a NTLM type 3 message and return STATUS_LOGON_FAILURE' do
123
+ status = authenticator.process_ntlm_type3(type3_msg)
124
+ expect(status).to be_a WindowsError::ErrorCode
125
+ expect(status).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
126
+ end
127
+
128
+ after(:each) do
129
+ expect(authenticator.session_key).to be_nil
130
+ end
131
+ end
132
+
133
+ context 'when guest access is enabled' do
134
+ before(:each) do
135
+ expect(provider).to_not receive(:allow_anonymous)
136
+ expect(provider).to receive(:allow_guests).and_return(true)
137
+ end
138
+
139
+ it 'should process a NTLM type 3 message and return STATUS_SUCCESS' do
140
+ status = authenticator.process_ntlm_type3(type3_msg)
141
+ expect(status).to be_a WindowsError::ErrorCode
142
+ expect(status).to eq WindowsError::NTStatus::STATUS_SUCCESS
143
+ end
144
+
145
+ after(:each) do
146
+ expect(authenticator.session_key).to eq "\x00".b * 16
147
+ end
148
+ end
149
+ end
150
+
151
+ context 'when the message is a known user' do
152
+ before(:each) do
153
+ authenticator.instance_variable_set(:@server_challenge, type2_msg[:challenge].serialize)
154
+ end
155
+
156
+ context 'when the password is correct' do
157
+ it 'should process a NTLM type 3 message and return STATUS_SUCCESS' do
158
+ type3_msg.user.force_encoding('UTF-16LE')
159
+ type3_msg.domain.force_encoding('UTF-16LE')
160
+ status = authenticator.process_ntlm_type3(type3_msg)
161
+ expect(status).to be_a WindowsError::ErrorCode
162
+ expect(status).to eq WindowsError::NTStatus::STATUS_SUCCESS
163
+ end
164
+
165
+ after(:each) do
166
+ expect(authenticator.session_key).to be_a String
167
+ expect(authenticator.session_key.length).to eq 16
168
+ end
169
+ end
170
+
171
+ context 'when the password is wrong' do
172
+ let(:type3_msg) do
173
+ type2_msg.response({user: username, password: 'Wrong' + password, domain: domain}, {ntlmv2: true})
174
+ end
175
+
176
+ it 'should process a NTLM type 3 message and return STATUS_LOGON_FAILURE' do
177
+ status = authenticator.process_ntlm_type3(type3_msg)
178
+ expect(status).to be_a WindowsError::ErrorCode
179
+ expect(status).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
180
+ end
181
+
182
+ after(:each) do
183
+ expect(authenticator.session_key).to be nil
184
+ end
185
+ end
71
186
  end
72
187
  end
73
188
 
@@ -24,7 +24,7 @@ RSpec.describe RubySMB::NTLM::Client::Session do
24
24
 
25
25
  it 'returns a Type3 message' do
26
26
  expect(session.authenticate!).to be_a Net::NTLM::Message::Type3
27
- expect(session.authenticate!).to be_a RubySMB::NTLM::Message
27
+ expect(session.authenticate!).to be_a Net::NTLM::Message
28
28
  end
29
29
 
30
30
  context 'when it is anonymous' do
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_smb
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.10
4
+ version: 3.3.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Metasploit Hackers
@@ -9,6 +9,7 @@ authors:
9
9
  - James Lee
10
10
  - Dev Mohanty
11
11
  - Christophe De La Fuente
12
+ - Spencer McIntyre
12
13
  autorequire:
13
14
  bindir: bin
14
15
  cert_chain:
@@ -38,7 +39,7 @@ cert_chain:
38
39
  DgscAao7wB3xW2BWEp1KnaDWkf1x9ttgoBEYyuYwU7uatB67kBQG1PKvLt79wHvz
39
40
  Dxs+KOjGbBRfMnPgVGYkORKVrZIwlaboHbDKxcVW5xv+oZc7KYXWGg==
40
41
  -----END CERTIFICATE-----
41
- date: 2024-09-11 00:00:00.000000000 Z
42
+ date: 2024-11-22 00:00:00.000000000 Z
42
43
  dependencies:
43
44
  - !ruby/object:Gem::Dependency
44
45
  name: redcarpet
@@ -116,14 +117,14 @@ dependencies:
116
117
  requirements:
117
118
  - - ">="
118
119
  - !ruby/object:Gem::Version
119
- version: '0'
120
+ version: 0.6.5
120
121
  type: :runtime
121
122
  prerelease: false
122
123
  version_requirements: !ruby/object:Gem::Requirement
123
124
  requirements:
124
125
  - - ">="
125
126
  - !ruby/object:Gem::Version
126
- version: '0'
127
+ version: 0.6.5
127
128
  - !ruby/object:Gem::Dependency
128
129
  name: windows_error
129
130
  requirement: !ruby/object:Gem::Requirement
@@ -187,6 +188,7 @@ executables: []
187
188
  extensions: []
188
189
  extra_rdoc_files: []
189
190
  files:
191
+ - ".github/workflows/metasploit-framework_acceptance.yml"
190
192
  - ".github/workflows/verify.yml"
191
193
  - ".gitignore"
192
194
  - ".rspec"
@@ -342,12 +344,16 @@ files:
342
344
  - lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_response.rb
343
345
  - lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_request.rb
344
346
  - lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_response.rb
347
+ - lib/ruby_smb/dcerpc/samr/samr_get_members_in_group_request.rb
348
+ - lib/ruby_smb/dcerpc/samr/samr_get_members_in_group_response.rb
345
349
  - lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request.rb
346
350
  - lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response.rb
347
351
  - lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_request.rb
348
352
  - lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_response.rb
349
353
  - lib/ruby_smb/dcerpc/samr/samr_open_domain_request.rb
350
354
  - lib/ruby_smb/dcerpc/samr/samr_open_domain_response.rb
355
+ - lib/ruby_smb/dcerpc/samr/samr_open_group_request.rb
356
+ - lib/ruby_smb/dcerpc/samr/samr_open_group_response.rb
351
357
  - lib/ruby_smb/dcerpc/samr/samr_open_user_request.rb
352
358
  - lib/ruby_smb/dcerpc/samr/samr_open_user_response.rb
353
359
  - lib/ruby_smb/dcerpc/samr/samr_query_information_domain_request.rb
@@ -473,7 +479,6 @@ files:
473
479
  - lib/ruby_smb/nbss/session_request.rb
474
480
  - lib/ruby_smb/ntlm.rb
475
481
  - lib/ruby_smb/ntlm/client.rb
476
- - lib/ruby_smb/ntlm/custom/string_encoder.rb
477
482
  - lib/ruby_smb/peer_info.rb
478
483
  - lib/ruby_smb/server.rb
479
484
  - lib/ruby_smb/server/cli.rb
@@ -654,7 +659,6 @@ files:
654
659
  - lib/ruby_smb/smb2/smb2_header.rb
655
660
  - lib/ruby_smb/smb2/tree.rb
656
661
  - lib/ruby_smb/smb_error.rb
657
- - lib/ruby_smb/utils.rb
658
662
  - lib/ruby_smb/version.rb
659
663
  - ruby_smb.gemspec
660
664
  - spec/lib/ruby_smb/client_spec.rb
@@ -996,7 +1000,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
996
1000
  - !ruby/object:Gem::Version
997
1001
  version: '0'
998
1002
  requirements: []
999
- rubygems_version: 3.2.3
1003
+ rubygems_version: 3.5.22
1000
1004
  signing_key:
1001
1005
  specification_version: 4
1002
1006
  summary: A pure Ruby implementation of the SMB Protocol Family
metadata.gz.sig CHANGED
Binary file
@@ -1,22 +0,0 @@
1
- require 'net/ntlm'
2
-
3
- module RubySMB
4
- module NTLM
5
- module Custom
6
- module StringEncoder
7
-
8
- def self.prepended(base)
9
- base.singleton_class.send(:prepend, ClassMethods)
10
- end
11
-
12
- module ClassMethods
13
- def encode_utf16le(str)
14
- str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('ASCII-8BIT')
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end
21
-
22
- Net::NTLM::EncodeUtil.send(:prepend, RubySMB::NTLM::Custom::StringEncoder)
@@ -1,15 +0,0 @@
1
- module RubySMB
2
- module Utils
3
-
4
- def self.safe_encode(str, encoding)
5
- str.encode(encoding)
6
- rescue EncodingError
7
- if str.encoding == ::Encoding::ASCII_8BIT
8
- str.dup.force_encoding(encoding)
9
- else
10
- raise
11
- end
12
- end
13
-
14
- end
15
- end