ruby_smb 3.3.10 → 3.3.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07f6abd7129c406c8b1c008c686eef226b8fa385fa06be6eec900b95b4f97a32
4
- data.tar.gz: 4ddc7ee9c516a1649faa15ad4316e36149aa4304c4dc176dfd5234f46f9218be
3
+ metadata.gz: 200e26ef444f49e42d6671ae291c557dbaa6d0b7ab5424df4b7c973fa990dbe5
4
+ data.tar.gz: 27ec24e3cd852a0634c1ef11067776a887ea06ad10e2752fc0acb2e6473d8d9a
5
5
  SHA512:
6
- metadata.gz: e948d03583b517435a912b4ea07086ee0bff1fc07c0f65f0ea89e51ec10ee4ab2074d33a82c4f774656909c938331a7080fb521e5e65989bb301e10cd80d1bf5
7
- data.tar.gz: 2f964acae7e74b25cb49e3d47ea125d4f4669f3f8868b9e895bb2f63be9e465e3fc1021e6695a1b837616a0f92419708ff4af6cb894b630d0b6292c95e3b4599
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
@@ -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
@@ -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.11'.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.11
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-15 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