ruby_smb 3.2.5 → 3.2.6
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/cortex.yaml +15 -0
- data/examples/dump_secrets_from_sid.rb +1 -1
- data/lib/ruby_smb/dcerpc/alter_context.rb +30 -0
- data/lib/ruby_smb/dcerpc/alter_context_resp.rb +42 -0
- data/lib/ruby_smb/dcerpc/bind.rb +3 -35
- data/lib/ruby_smb/dcerpc/bind_ack.rb +0 -31
- data/lib/ruby_smb/dcerpc/client.rb +4 -0
- data/lib/ruby_smb/dcerpc/drsr.rb +13 -13
- data/lib/ruby_smb/dcerpc/p_cont_list_t.rb +37 -0
- data/lib/ruby_smb/dcerpc/p_result_list_t.rb +13 -0
- data/lib/ruby_smb/dcerpc/p_result_t.rb +15 -0
- data/lib/ruby_smb/dcerpc/port_any_t.rb +11 -0
- data/lib/ruby_smb/dcerpc/request.rb +8 -3
- data/lib/ruby_smb/dcerpc/response.rb +6 -1
- data/lib/ruby_smb/dcerpc.rb +165 -122
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/dcerpc/client_spec.rb +31 -16
- data/spec/lib/ruby_smb/dcerpc/drsr_spec.rb +4 -1
- data/spec/lib/ruby_smb/dcerpc/request_spec.rb +0 -6
- data/spec/lib/ruby_smb/dcerpc/response_spec.rb +0 -6
- data/spec/lib/ruby_smb/dcerpc/sec_trailer_spec.rb +0 -14
- data.tar.gz.sig +0 -0
- metadata +9 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6220ba1edc47882a9d30dd1937d877c22094ebaf9c2a72cb806489a65598e1ce
|
4
|
+
data.tar.gz: 1b650bfdd2b6ba8323e9d3e7f4e161c7a659dddbffc7fa21a03d4a2e538f7297
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e49bc0af1cd4ad61cba01ec70aecead1adbe3b4d58d6127593878596bdbc7f64c8d50b9aaa396004ffc3a3b5e8cf1806053d0825d44322aa4d4584bdddeced7
|
7
|
+
data.tar.gz: 36ca2d7c9e6256a0faabac441e89ea86d43ccd8d2de342dba4965679d2e2c4fafa541fbc09c4457d656a2cccef1af53fe5ab487245800914ad8b9a6431679088
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/cortex.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
info:
|
3
|
+
title: Ruby Smb
|
4
|
+
description: A native Ruby implementation of the SMB Protocol Family
|
5
|
+
x-cortex-git:
|
6
|
+
github:
|
7
|
+
alias: r7org
|
8
|
+
repository: rapid7/ruby_smb
|
9
|
+
x-cortex-tag: ruby-smb
|
10
|
+
x-cortex-type: service
|
11
|
+
x-cortex-domain-parents:
|
12
|
+
- tag: metasploit
|
13
|
+
openapi: 3.0.1
|
14
|
+
servers:
|
15
|
+
- url: "/"
|
@@ -60,7 +60,7 @@ dc_infos.each do |dc_info|
|
|
60
60
|
puts "Decrypting hash for user: #{dn}"
|
61
61
|
|
62
62
|
entinf_struct = user_record.pmsg_out.msg_getchg.p_objects.entinf
|
63
|
-
object_sid = rid = entinf_struct.p_name.sid[-4..-1].unpack('<
|
63
|
+
object_sid = rid = entinf_struct.p_name.sid[-4..-1].unpack('L<').first
|
64
64
|
lm_hash = Net::NTLM.lm_hash('')
|
65
65
|
nt_hash = Net::NTLM.ntlm_hash('')
|
66
66
|
disabled = nil
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
# The Alter context PDU as defined in
|
4
|
+
# [The alter_context PDU](https://pubs.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06_04_01)
|
5
|
+
class AlterContext < BinData::Record
|
6
|
+
PTYPE = PTypes::ALTER_CONTEXT
|
7
|
+
|
8
|
+
endian :little
|
9
|
+
|
10
|
+
# PDU Header
|
11
|
+
pdu_header :pdu_header, label: 'PDU header'
|
12
|
+
ndr_uint16 :max_xmit_frag, label: 'Max transmit frag size', initial_value: RubySMB::Dcerpc::MAX_XMIT_FRAG
|
13
|
+
ndr_uint16 :max_recv_frag, label: 'Max receive frag size', initial_value: RubySMB::Dcerpc::MAX_RECV_FRAG
|
14
|
+
ndr_uint32 :assoc_group_id, label: 'Incarnation of client-server assoc group'
|
15
|
+
p_cont_list_t :p_context_list, label: 'Presentation context list', endpoint: -> { endpoint }
|
16
|
+
|
17
|
+
# Auth Verifier
|
18
|
+
sec_trailer :sec_trailer, onlyif: -> { pdu_header.auth_length > 0 }
|
19
|
+
string :auth_value,
|
20
|
+
onlyif: -> { pdu_header.auth_length > 0 },
|
21
|
+
read_length: -> { pdu_header.auth_length }
|
22
|
+
|
23
|
+
def initialize_instance
|
24
|
+
super
|
25
|
+
pdu_header.ptype = PTYPE
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
# The Alter context resp PDU as defined in
|
4
|
+
# [The alter_context_resp PDU](https://pubs.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06_04_02)
|
5
|
+
|
6
|
+
class AlterContextResp < BinData::Record
|
7
|
+
PTYPE = PTypes::ALTER_CONTEXT_RESP
|
8
|
+
|
9
|
+
# Presentation context negotiation results
|
10
|
+
ACCEPTANCE = 0
|
11
|
+
USER_REJECTION = 1
|
12
|
+
PROVIDER_REJECTION = 2
|
13
|
+
|
14
|
+
# Reasons for rejection of a context element
|
15
|
+
REASON_NOT_SPECIFIED = 0
|
16
|
+
ABSTRACT_SYNTAX_NOT_SUPPORTED = 1
|
17
|
+
PROPOSED_TRANSFER_SYNTAXES_NOT_SUPPORTED = 2
|
18
|
+
LOCAL_LIMIT_EXCEEDED = 3
|
19
|
+
|
20
|
+
endian :little
|
21
|
+
|
22
|
+
# PDU Header
|
23
|
+
pdu_header :pdu_header, label: 'PDU header'
|
24
|
+
ndr_uint16 :max_xmit_frag, label: 'Max transmit frag size', initial_value: RubySMB::Dcerpc::MAX_XMIT_FRAG
|
25
|
+
ndr_uint16 :max_recv_frag, label: 'Max receive frag size', initial_value: RubySMB::Dcerpc::MAX_RECV_FRAG
|
26
|
+
ndr_uint32 :assoc_group_id, label: 'Association group ID'
|
27
|
+
port_any_t :sec_addr, label: 'Secondary address'
|
28
|
+
p_result_list_t :p_result_list, label: 'Presentation context result list'
|
29
|
+
|
30
|
+
# Auth Verifier
|
31
|
+
sec_trailer :sec_trailer, onlyif: -> { pdu_header.auth_length > 0 }
|
32
|
+
string :auth_value,
|
33
|
+
onlyif: -> { pdu_header.auth_length > 0 },
|
34
|
+
read_length: -> { pdu_header.auth_length }
|
35
|
+
|
36
|
+
def initialize_instance
|
37
|
+
super
|
38
|
+
pdu_header.ptype = PTYPE
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/ruby_smb/dcerpc/bind.rb
CHANGED
@@ -2,38 +2,6 @@ module RubySMB
|
|
2
2
|
module Dcerpc
|
3
3
|
# The Bind PDU as defined in
|
4
4
|
# [The bind PDU](http://pubs.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06_04_03)
|
5
|
-
class PContElemT < Ndr::NdrStruct
|
6
|
-
default_parameter byte_align: 4
|
7
|
-
endian :little
|
8
|
-
|
9
|
-
ndr_uint16 :p_cont_id, label: 'Context ID'
|
10
|
-
ndr_uint8 :n_transfer_syn, label: 'Number of transfer syntaxes', initial_value: 1
|
11
|
-
ndr_uint8 :reserved
|
12
|
-
p_syntax_id_t :abstract_syntax, label: 'Abstract syntax',
|
13
|
-
uuid: -> { endpoint::UUID },
|
14
|
-
ver_major: -> { endpoint::VER_MAJOR },
|
15
|
-
ver_minor: -> { endpoint::VER_MINOR }
|
16
|
-
array :transfer_syntaxes, label: 'Transfer syntax', type: :p_syntax_id_t,
|
17
|
-
initial_length: -> { n_transfer_syn },
|
18
|
-
uuid: -> { Ndr::UUID },
|
19
|
-
ver_major: -> { Ndr::VER_MAJOR },
|
20
|
-
ver_minor: -> { Ndr::VER_MINOR },
|
21
|
-
byte_align: 4
|
22
|
-
end
|
23
|
-
|
24
|
-
class PContListT < Ndr::NdrStruct
|
25
|
-
default_parameter byte_align: 4
|
26
|
-
endian :little
|
27
|
-
|
28
|
-
ndr_uint8 :n_context_elem, label: 'Number of context elements', initial_value: -> { 1 }
|
29
|
-
ndr_uint8 :reserved
|
30
|
-
ndr_uint16 :reserved2
|
31
|
-
array :p_cont_elem, label: 'Presentation context elements', type: :p_cont_elem_t,
|
32
|
-
initial_length: -> {n_context_elem},
|
33
|
-
endpoint: -> {endpoint},
|
34
|
-
byte_align: 4
|
35
|
-
end
|
36
|
-
|
37
5
|
class Bind < BinData::Record
|
38
6
|
PTYPE = PTypes::BIND
|
39
7
|
|
@@ -41,9 +9,9 @@ module RubySMB
|
|
41
9
|
|
42
10
|
# PDU Header
|
43
11
|
pdu_header :pdu_header, label: 'PDU header'
|
44
|
-
ndr_uint16 :max_xmit_frag, label: '
|
45
|
-
ndr_uint16 :max_recv_frag, label: '
|
46
|
-
ndr_uint32 :assoc_group_id, label: '
|
12
|
+
ndr_uint16 :max_xmit_frag, label: 'Max transmit frag size', initial_value: RubySMB::Dcerpc::MAX_XMIT_FRAG
|
13
|
+
ndr_uint16 :max_recv_frag, label: 'Max receive frag size', initial_value: RubySMB::Dcerpc::MAX_RECV_FRAG
|
14
|
+
ndr_uint32 :assoc_group_id, label: 'Incarnation of client-server assoc group'
|
47
15
|
p_cont_list_t :p_context_list, label: 'Presentation context list', endpoint: -> { endpoint }
|
48
16
|
|
49
17
|
# Auth Verifier
|
@@ -2,37 +2,6 @@ module RubySMB
|
|
2
2
|
module Dcerpc
|
3
3
|
# The Bind ACK PDU as defined in
|
4
4
|
# [The bind_ack PDU](http://pubs.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06_04_04)
|
5
|
-
|
6
|
-
class PResultT < Ndr::NdrStruct
|
7
|
-
default_parameter byte_align: 4
|
8
|
-
endian :little
|
9
|
-
|
10
|
-
ndr_uint16 :result, label: 'Presentation context negotiation results'
|
11
|
-
ndr_uint16 :reason, label: 'Rejection reason'
|
12
|
-
p_syntax_id_t :transfer_syntax, label: 'Presentation syntax ID',
|
13
|
-
uuid: -> { Ndr::UUID },
|
14
|
-
ver_major: -> { Ndr::VER_MAJOR },
|
15
|
-
ver_minor: -> { Ndr::VER_MINOR }
|
16
|
-
end
|
17
|
-
|
18
|
-
class PResultListT < Ndr::NdrStruct
|
19
|
-
default_parameter byte_align: 4
|
20
|
-
endian :little
|
21
|
-
|
22
|
-
ndr_uint8 :n_results, label: 'Number of results', initial_value: -> { p_results.size }
|
23
|
-
ndr_uint8 :reserved
|
24
|
-
ndr_uint16 :reserved2
|
25
|
-
array :p_results, label: 'Results', type: :p_result_t, initial_length: -> { n_results }, byte_align: 4
|
26
|
-
end
|
27
|
-
|
28
|
-
class PortAnyT < Ndr::NdrStruct
|
29
|
-
default_parameter byte_align: 2
|
30
|
-
endian :little
|
31
|
-
|
32
|
-
ndr_uint16 :str_length, label: 'Length', initial_value: -> { port_spec.to_binary_s.size }
|
33
|
-
stringz :port_spec, label: 'Port string spec', byte_align: 2
|
34
|
-
end
|
35
|
-
|
36
5
|
class BindAck < BinData::Record
|
37
6
|
PTYPE = PTypes::BIND_ACK
|
38
7
|
|
@@ -209,6 +209,10 @@ module RubySMB
|
|
209
209
|
if auth_level &&
|
210
210
|
[RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(auth_level)
|
211
211
|
set_integrity_privacy(dcerpc_req, auth_level: auth_level, auth_type: auth_type)
|
212
|
+
# Per the spec (MS_RPCE 2.2.2.11): start of the trailer should be a multiple of 16 bytes offset from the start of the stub
|
213
|
+
valid_offset = (((dcerpc_req.sec_trailer.abs_offset - dcerpc_req.stub.abs_offset) % 16))
|
214
|
+
valid_auth_pad = (dcerpc_req.sec_trailer.auth_pad_length == dcerpc_req.auth_pad.length)
|
215
|
+
raise Error::InvalidPacket unless valid_offset == 0 && valid_auth_pad
|
212
216
|
end
|
213
217
|
|
214
218
|
send_packet(dcerpc_req)
|
data/lib/ruby_smb/dcerpc/drsr.rb
CHANGED
@@ -613,8 +613,8 @@ module RubySMB
|
|
613
613
|
drs_bind_request = DrsBindRequest.new(pext_client: drs_extensions_int)
|
614
614
|
response = dcerpc_request(
|
615
615
|
drs_bind_request,
|
616
|
-
auth_level:
|
617
|
-
auth_type:
|
616
|
+
auth_level: @auth_level,
|
617
|
+
auth_type: @auth_type
|
618
618
|
)
|
619
619
|
begin
|
620
620
|
drs_bind_response = DrsBindResponse.read(response)
|
@@ -640,8 +640,8 @@ module RubySMB
|
|
640
640
|
drs_bind_request.pext_client.assign(drs_extensions_int)
|
641
641
|
response = dcerpc_request(
|
642
642
|
drs_bind_request,
|
643
|
-
auth_level:
|
644
|
-
auth_type:
|
643
|
+
auth_level: @auth_level,
|
644
|
+
auth_type: @auth_type
|
645
645
|
)
|
646
646
|
begin
|
647
647
|
drs_bind_response = DrsBindResponse.read(response)
|
@@ -668,8 +668,8 @@ module RubySMB
|
|
668
668
|
drs_unbind_request = DrsUnbindRequest.new(ph_drs: ph_drs)
|
669
669
|
response = dcerpc_request(
|
670
670
|
drs_unbind_request,
|
671
|
-
|
672
|
-
|
671
|
+
auth_level: @auth_level,
|
672
|
+
auth_type: @auth_type
|
673
673
|
)
|
674
674
|
begin
|
675
675
|
drs_unbind_response = DrsUnbindResponse.read(response)
|
@@ -709,8 +709,8 @@ module RubySMB
|
|
709
709
|
)
|
710
710
|
response = dcerpc_request(
|
711
711
|
drs_domain_controller_info_request,
|
712
|
-
auth_level:
|
713
|
-
auth_type:
|
712
|
+
auth_level: @auth_level,
|
713
|
+
auth_type: @auth_type
|
714
714
|
)
|
715
715
|
begin
|
716
716
|
drs_domain_controller_info_response = DrsDomainControllerInfoResponse.read(response)
|
@@ -759,8 +759,8 @@ module RubySMB
|
|
759
759
|
)
|
760
760
|
response = dcerpc_request(
|
761
761
|
drs_crack_names_request,
|
762
|
-
auth_level:
|
763
|
-
auth_type:
|
762
|
+
auth_level: @auth_level,
|
763
|
+
auth_type: @auth_type
|
764
764
|
)
|
765
765
|
begin
|
766
766
|
drs_crack_names_response = DrsCrackNamesResponse.read(response)
|
@@ -790,8 +790,8 @@ module RubySMB
|
|
790
790
|
unless @session_key
|
791
791
|
raise RubySMB::Error::EncryptionError, 'Unable to decrypt attribute value: session key is empty'
|
792
792
|
end
|
793
|
-
encrypted_payload = EncryptedPayload.read(attribute)
|
794
793
|
|
794
|
+
encrypted_payload = EncryptedPayload.read(attribute)
|
795
795
|
signature = OpenSSL::Digest::MD5.digest(@session_key + encrypted_payload.salt.to_binary_s)
|
796
796
|
rc4 = OpenSSL::Cipher.new('rc4')
|
797
797
|
rc4.decrypt
|
@@ -886,8 +886,8 @@ module RubySMB
|
|
886
886
|
|
887
887
|
response = dcerpc_request(
|
888
888
|
drs_get_nc_changes_request,
|
889
|
-
auth_level:
|
890
|
-
auth_type:
|
889
|
+
auth_level: @auth_level,
|
890
|
+
auth_type: @auth_type
|
891
891
|
)
|
892
892
|
begin
|
893
893
|
drs_get_nc_changes_response = DrsGetNcChangesResponse.read(response)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
# The presentation context list and its element as defined in
|
4
|
+
# [Connection-oriented PDU Data Types - Declarations](https://pubs.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06_03_01)
|
5
|
+
class PContElemT < Ndr::NdrStruct
|
6
|
+
default_parameter byte_align: 4
|
7
|
+
endian :little
|
8
|
+
|
9
|
+
ndr_uint16 :p_cont_id, label: 'Context ID'
|
10
|
+
ndr_uint8 :n_transfer_syn, label: 'Number of transfer syntaxes', initial_value: 1
|
11
|
+
ndr_uint8 :reserved
|
12
|
+
p_syntax_id_t :abstract_syntax, label: 'Abstract syntax',
|
13
|
+
uuid: -> { endpoint::UUID },
|
14
|
+
ver_major: -> { endpoint::VER_MAJOR },
|
15
|
+
ver_minor: -> { endpoint::VER_MINOR }
|
16
|
+
array :transfer_syntaxes, label: 'Transfer syntax', type: :p_syntax_id_t,
|
17
|
+
initial_length: -> { n_transfer_syn },
|
18
|
+
uuid: -> { Ndr::UUID },
|
19
|
+
ver_major: -> { Ndr::VER_MAJOR },
|
20
|
+
ver_minor: -> { Ndr::VER_MINOR },
|
21
|
+
byte_align: 4
|
22
|
+
end
|
23
|
+
|
24
|
+
class PContListT < Ndr::NdrStruct
|
25
|
+
default_parameter byte_align: 4
|
26
|
+
endian :little
|
27
|
+
|
28
|
+
ndr_uint8 :n_context_elem, label: 'Number of context elements', initial_value: -> { 1 }
|
29
|
+
ndr_uint8 :reserved
|
30
|
+
ndr_uint16 :reserved2
|
31
|
+
array :p_cont_elem, label: 'Presentation context elements', type: :p_cont_elem_t,
|
32
|
+
initial_length: -> {n_context_elem},
|
33
|
+
endpoint: -> {endpoint},
|
34
|
+
byte_align: 4
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
class PResultListT < Ndr::NdrStruct
|
4
|
+
default_parameter byte_align: 4
|
5
|
+
endian :little
|
6
|
+
|
7
|
+
ndr_uint8 :n_results, label: 'Number of results', initial_value: -> { p_results.size }
|
8
|
+
ndr_uint8 :reserved
|
9
|
+
ndr_uint16 :reserved2
|
10
|
+
array :p_results, label: 'Results', type: :p_result_t, initial_length: -> { n_results }, byte_align: 4
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
class PResultT < Ndr::NdrStruct
|
4
|
+
default_parameter byte_align: 4
|
5
|
+
endian :little
|
6
|
+
|
7
|
+
ndr_uint16 :result, label: 'Presentation context negotiation results'
|
8
|
+
ndr_uint16 :reason, label: 'Rejection reason'
|
9
|
+
p_syntax_id_t :transfer_syntax, label: 'Presentation syntax ID',
|
10
|
+
uuid: -> { Ndr::UUID },
|
11
|
+
ver_major: -> { Ndr::VER_MAJOR },
|
12
|
+
ver_minor: -> { Ndr::VER_MINOR }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Dcerpc
|
3
|
+
class PortAnyT < Ndr::NdrStruct
|
4
|
+
default_parameter byte_align: 2
|
5
|
+
endian :little
|
6
|
+
|
7
|
+
ndr_uint16 :str_length, label: 'Length', initial_value: -> { port_spec.to_binary_s.size }
|
8
|
+
stringz :port_spec, label: 'Port string spec', byte_align: 2, onlyif: -> { str_length > 0 }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -103,9 +103,10 @@ module RubySMB
|
|
103
103
|
end
|
104
104
|
string :default
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
|
+
string :auth_pad,
|
107
108
|
onlyif: -> { has_auth_verifier? },
|
108
|
-
length: -> {
|
109
|
+
length: -> { calculate_padding_size }
|
109
110
|
|
110
111
|
# Auth Verifier
|
111
112
|
sec_trailer :sec_trailer, onlyif: -> { has_auth_verifier? }
|
@@ -113,6 +114,11 @@ module RubySMB
|
|
113
114
|
onlyif: -> { has_auth_verifier? },
|
114
115
|
read_length: -> { pdu_header.auth_length }
|
115
116
|
|
117
|
+
# Per the spec (MS_RPCE 2.2.2.11): start of the trailer should be a multiple of 16 bytes offset from the start of the stub
|
118
|
+
def calculate_padding_size
|
119
|
+
(16 - (stub.num_bytes % 16)) % 16
|
120
|
+
end
|
121
|
+
|
116
122
|
def initialize_instance
|
117
123
|
super
|
118
124
|
pdu_header.ptype = PTYPE
|
@@ -125,7 +131,6 @@ module RubySMB
|
|
125
131
|
def has_auth_verifier?
|
126
132
|
self.pdu_header.auth_length > 0
|
127
133
|
end
|
128
|
-
|
129
134
|
end
|
130
135
|
end
|
131
136
|
end
|
@@ -18,7 +18,7 @@ module RubySMB
|
|
18
18
|
string :stub, label: 'Stub', read_length: -> { stub_length }
|
19
19
|
string :auth_pad,
|
20
20
|
onlyif: -> { has_auth_verifier? },
|
21
|
-
length: -> {
|
21
|
+
length: -> { calculate_padding_size }
|
22
22
|
|
23
23
|
# Auth Verifier
|
24
24
|
sec_trailer :sec_trailer, onlyif: -> { has_auth_verifier? }
|
@@ -26,6 +26,11 @@ module RubySMB
|
|
26
26
|
onlyif: -> { has_auth_verifier? },
|
27
27
|
read_length: -> { pdu_header.auth_length }
|
28
28
|
|
29
|
+
# Per the spec (MS_RPCE 2.2.2.11): start of the trailer should be a multiple of 16 bytes offset from the start of the stub
|
30
|
+
def calculate_padding_size
|
31
|
+
(16 - (stub.num_bytes % 16)) % 16
|
32
|
+
end
|
33
|
+
|
29
34
|
def initialize_instance
|
30
35
|
super
|
31
36
|
pdu_header.ptype = PTYPE
|
data/lib/ruby_smb/dcerpc.rb
CHANGED
@@ -51,11 +51,128 @@ module RubySMB
|
|
51
51
|
require 'ruby_smb/dcerpc/request'
|
52
52
|
require 'ruby_smb/dcerpc/response'
|
53
53
|
require 'ruby_smb/dcerpc/rpc_auth3'
|
54
|
+
require 'ruby_smb/dcerpc/port_any_t'
|
55
|
+
require 'ruby_smb/dcerpc/p_cont_list_t'
|
56
|
+
require 'ruby_smb/dcerpc/p_result_t'
|
57
|
+
require 'ruby_smb/dcerpc/p_result_list_t'
|
58
|
+
require 'ruby_smb/dcerpc/alter_context'
|
54
59
|
require 'ruby_smb/dcerpc/bind'
|
55
60
|
require 'ruby_smb/dcerpc/bind_ack'
|
61
|
+
require 'ruby_smb/dcerpc/alter_context_resp'
|
56
62
|
require 'ruby_smb/dcerpc/print_system'
|
57
63
|
require 'ruby_smb/dcerpc/encrypting_file_system'
|
58
64
|
|
65
|
+
# Initialize the auth provider using NTLM. This function should be overriden for other providers (e.g. Kerberos, etc.)
|
66
|
+
# @raise ArgumentError If @ntlm_client isn't initialized with a username and password.
|
67
|
+
# @return Serialized message for initializing the auth provider (NTLM, unless this class is extended/overridden)
|
68
|
+
def auth_provider_init
|
69
|
+
raise ArgumentError, "NTLM Client not initialized. Username and password must be provided" unless @ntlm_client
|
70
|
+
type1_message = @ntlm_client.init_context
|
71
|
+
|
72
|
+
type1_message.serialize
|
73
|
+
end
|
74
|
+
|
75
|
+
# Encrypt the value in dcerpc_req.stub, and add a valid signature to the request.
|
76
|
+
# This function modifies the request object in-place, and does not return anything.
|
77
|
+
# This function should be overriden for other providers (e.g. Kerberos, etc.)
|
78
|
+
# @param dcerpc_req [Request] The Request object to be encrypted and signed in-place
|
79
|
+
def auth_provider_encrypt_and_sign(dcerpc_req)
|
80
|
+
auth_type = dcerpc_req.sec_trailer.auth_type
|
81
|
+
auth_level = dcerpc_req.sec_trailer.auth_level
|
82
|
+
unless [RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT].include?(auth_type)
|
83
|
+
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
84
|
+
end
|
85
|
+
plaintext = dcerpc_req.stub.to_binary_s
|
86
|
+
pad_length = get_auth_padding_length(plaintext.length)
|
87
|
+
dcerpc_req.auth_pad = "\x00" * pad_length
|
88
|
+
data_to_sign = plain_stub_with_padding = dcerpc_req.stub.to_binary_s + dcerpc_req.auth_pad.to_binary_s
|
89
|
+
dcerpc_req.sec_trailer.auth_pad_length = pad_length
|
90
|
+
if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
|
91
|
+
data_to_sign = dcerpc_req.to_binary_s[0..-(dcerpc_req.pdu_header.auth_length + 1)]
|
92
|
+
end
|
93
|
+
|
94
|
+
if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
95
|
+
encrypted = @ntlm_client.session.seal_message(plain_stub_with_padding)
|
96
|
+
set_encrypted_packet(dcerpc_req, encrypted, pad_length)
|
97
|
+
end
|
98
|
+
signature = @ntlm_client.session.sign_message(data_to_sign)
|
99
|
+
|
100
|
+
set_signature_on_packet(dcerpc_req, signature)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Get the response's full stub value (which will include the auth-pad)
|
104
|
+
# @param dcerpc_response [Response] The Response object to extract from
|
105
|
+
# @return [String] The full stub, including auth_pad
|
106
|
+
def get_response_full_stub(dcerpc_response)
|
107
|
+
dcerpc_response.stub.to_binary_s + dcerpc_response.auth_pad.to_binary_s
|
108
|
+
end
|
109
|
+
|
110
|
+
# Decrypt the value in dcerpc_req.stub, and validate its signature.
|
111
|
+
# This function modifies the request object in-place, and returns whether the signature was valid.
|
112
|
+
# This function should be overriden for other providers (e.g. Kerberos, etc.)
|
113
|
+
# @param dcerpc_response [Response] The Response packet to decrypt and verify in-place
|
114
|
+
# @raise ArgumentError If the auth type is not NTLM
|
115
|
+
# @return [Boolean] Is the packet's signature valid?
|
116
|
+
def auth_provider_decrypt_and_verify(dcerpc_response)
|
117
|
+
auth_type = dcerpc_response.sec_trailer.auth_type
|
118
|
+
auth_level = dcerpc_response.sec_trailer.auth_level
|
119
|
+
unless [RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT].include?(auth_type)
|
120
|
+
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
121
|
+
end
|
122
|
+
signature = dcerpc_response.auth_value
|
123
|
+
if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
124
|
+
encrypted_stub = get_response_full_stub(dcerpc_response)
|
125
|
+
plaintext = @ntlm_client.session.unseal_message(encrypted_stub)
|
126
|
+
set_decrypted_packet(dcerpc_response, plaintext)
|
127
|
+
end
|
128
|
+
data_to_check = dcerpc_response.stub.to_binary_s
|
129
|
+
if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
|
130
|
+
data_to_check = dcerpc_response.to_binary_s[0..-(dcerpc_response.pdu_header.auth_length + 1)]
|
131
|
+
end
|
132
|
+
|
133
|
+
@ntlm_client.session.verify_signature(signature, data_to_check)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Completes local initialisation of @ntlm_client using the server's response
|
137
|
+
#
|
138
|
+
# @param type2_message [String] NTLM type 2 message sent from server
|
139
|
+
# @return [String] Type 3 message to be sent to the server to complete the NTLM handshake
|
140
|
+
def process_ntlm_type2(type2_message)
|
141
|
+
ntlmssp_offset = type2_message.index('NTLMSSP')
|
142
|
+
type2_blob = type2_message.slice(ntlmssp_offset..-1)
|
143
|
+
type2_b64_message = [type2_blob].pack('m')
|
144
|
+
type3_message = @ntlm_client.init_context(type2_b64_message)
|
145
|
+
auth3 = type3_message.serialize
|
146
|
+
|
147
|
+
@session_key = @ntlm_client.session_key
|
148
|
+
auth3
|
149
|
+
end
|
150
|
+
|
151
|
+
# Send a rpc_auth3 PDU that ends the authentication handshake.
|
152
|
+
# This function should be overriden for other providers (e.g. Kerberos, etc.)
|
153
|
+
#
|
154
|
+
# @param response [BindAck] the BindAck response packet
|
155
|
+
# @param options [Hash] Unused by the NTLM auth provider
|
156
|
+
def auth_provider_complete_handshake(response, options)
|
157
|
+
auth3 = process_ntlm_type2(response.auth_value)
|
158
|
+
|
159
|
+
rpc_auth3 = RpcAuth3.new
|
160
|
+
add_auth_verifier(rpc_auth3, auth3)
|
161
|
+
rpc_auth3.pdu_header.call_id = @call_id
|
162
|
+
|
163
|
+
# The server should not respond
|
164
|
+
send_packet(rpc_auth3)
|
165
|
+
@call_id += 1
|
166
|
+
|
167
|
+
nil
|
168
|
+
end
|
169
|
+
|
170
|
+
def force_set_auth_params(auth_type, auth_level)
|
171
|
+
@auth_type = auth_type
|
172
|
+
@auth_level = auth_level
|
173
|
+
end
|
174
|
+
|
175
|
+
|
59
176
|
# Bind to the remote server interface endpoint. It takes care of adding
|
60
177
|
# the necessary authentication verifier if `:auth_level` is set to
|
61
178
|
# anything different than RPC_C_AUTHN_LEVEL_NONE
|
@@ -74,23 +191,16 @@ module RubySMB
|
|
74
191
|
@call_id ||= 1
|
75
192
|
bind_req = Bind.new(options)
|
76
193
|
bind_req.pdu_header.call_id = @call_id
|
194
|
+
auth_type = options.fetch(:auth_type) { RPC_C_AUTHN_WINNT }
|
195
|
+
auth_level = options.fetch(:auth_level) { RPC_C_AUTHN_LEVEL_NONE }
|
77
196
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
auth = type1_message.serialize
|
86
|
-
when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE
|
87
|
-
when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL
|
88
|
-
# TODO
|
89
|
-
raise NotImplementedError
|
90
|
-
else
|
91
|
-
raise ArgumentError, "Unsupported Auth Type: #{options[:auth_type]}"
|
92
|
-
end
|
93
|
-
add_auth_verifier(bind_req, auth, options[:auth_type], options[:auth_level])
|
197
|
+
force_set_auth_params(auth_type, auth_level)
|
198
|
+
|
199
|
+
if @auth_level != RPC_C_AUTHN_LEVEL_NONE
|
200
|
+
@ctx_id = 0
|
201
|
+
@auth_ctx_id_base = rand(0xFFFFFFFF)
|
202
|
+
auth = auth_provider_init
|
203
|
+
add_auth_verifier(bind_req, auth)
|
94
204
|
end
|
95
205
|
|
96
206
|
send_packet(bind_req)
|
@@ -110,17 +220,11 @@ module RubySMB
|
|
110
220
|
self.max_buffer_size = dcerpc_response.max_xmit_frag
|
111
221
|
@call_id = dcerpc_response.pdu_header.call_id
|
112
222
|
|
113
|
-
if
|
223
|
+
if auth_level != RPC_C_AUTHN_LEVEL_NONE
|
114
224
|
# The number of legs needed to build the security context is defined
|
115
225
|
# by the security provider
|
116
|
-
# (see [2.2.1.1.7 Security Providers](https://
|
117
|
-
|
118
|
-
when RPC_C_AUTHN_WINNT
|
119
|
-
send_auth3(dcerpc_response, options[:auth_type], options[:auth_level])
|
120
|
-
when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE
|
121
|
-
# TODO
|
122
|
-
raise NotImplementedError
|
123
|
-
end
|
226
|
+
# (see [2.2.1.1.7 Security Providers](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/d4097450-c62f-484b-872f-ddf59a7a0d36))
|
227
|
+
auth_provider_complete_handshake(dcerpc_response, options)
|
124
228
|
end
|
125
229
|
|
126
230
|
dcerpc_response
|
@@ -156,16 +260,24 @@ module RubySMB
|
|
156
260
|
nil
|
157
261
|
end
|
158
262
|
|
263
|
+
def get_auth_padding_length(plaintext_len)
|
264
|
+
(16 - (plaintext_len % 16)) % 16
|
265
|
+
end
|
266
|
+
|
159
267
|
# Add the authentication verifier to a Request packet. This includes a
|
160
268
|
# sec trailer and the signature of the packet. This also encrypts the
|
161
269
|
# Request stub if privacy is required (`:auth_level` option is
|
162
270
|
# RPC_C_AUTHN_LEVEL_PKT_PRIVACY).
|
163
271
|
#
|
164
272
|
# @param dcerpc_req [Request] the Request packet to be updated
|
165
|
-
# @param opts [Hash] the
|
273
|
+
# @param opts [Hash] the authentication options: `:auth_type` and `:auth_level`
|
166
274
|
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
167
275
|
# @raise [ArgumentError] if `:auth_type` is unknown
|
168
276
|
def set_integrity_privacy(dcerpc_req, auth_level:, auth_type:)
|
277
|
+
unless [RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(auth_level)
|
278
|
+
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
279
|
+
end
|
280
|
+
|
169
281
|
dcerpc_req.sec_trailer = {
|
170
282
|
auth_type: auth_type,
|
171
283
|
auth_level: auth_level,
|
@@ -174,35 +286,30 @@ module RubySMB
|
|
174
286
|
dcerpc_req.auth_value = ' ' * 16
|
175
287
|
dcerpc_req.pdu_header.auth_length = 16
|
176
288
|
|
177
|
-
|
178
|
-
|
179
|
-
data_to_sign = dcerpc_req.to_binary_s[0..-(dcerpc_req.pdu_header.auth_length + 1)]
|
180
|
-
end
|
181
|
-
|
182
|
-
encrypted_stub = ''
|
183
|
-
if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
184
|
-
case auth_type
|
185
|
-
when RPC_C_AUTHN_NONE
|
186
|
-
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
187
|
-
encrypted_stub = @ntlm_client.session.seal_message(plain_stub)
|
188
|
-
when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
|
189
|
-
# TODO
|
190
|
-
raise NotImplementedError
|
191
|
-
else
|
192
|
-
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
193
|
-
end
|
194
|
-
end
|
289
|
+
auth_provider_encrypt_and_sign(dcerpc_req)
|
290
|
+
end
|
195
291
|
|
196
|
-
|
292
|
+
def set_signature_on_packet(dcerpc_req, signature)
|
293
|
+
dcerpc_req.auth_value = signature
|
294
|
+
dcerpc_req.pdu_header.auth_length = signature.size
|
295
|
+
end
|
197
296
|
|
297
|
+
def set_encrypted_packet(dcerpc_req, encrypted_stub, pad_length)
|
198
298
|
unless encrypted_stub.empty?
|
199
|
-
pad_length = dcerpc_req.sec_trailer.auth_pad_length.to_i
|
200
299
|
dcerpc_req.enable_encrypted_stub
|
201
|
-
dcerpc_req.stub = encrypted_stub[0..-(pad_length
|
202
|
-
|
300
|
+
dcerpc_req.stub = encrypted_stub[0..-(pad_length+1)]
|
301
|
+
if pad_length != 0
|
302
|
+
dcerpc_req.auth_pad = encrypted_stub[-(pad_length)..-1]
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def set_decrypted_packet(dcerpc_response, decrypted_stub)
|
308
|
+
unless decrypted_stub.empty?
|
309
|
+
pad_length = dcerpc_response.sec_trailer.auth_pad_length.to_i
|
310
|
+
dcerpc_response.stub = decrypted_stub[0..-(pad_length + 1)]
|
311
|
+
dcerpc_response.auth_pad = decrypted_stub[-(pad_length + 1)..-1]
|
203
312
|
end
|
204
|
-
dcerpc_req.auth_value = signature
|
205
|
-
dcerpc_req.pdu_header.auth_length = signature.size
|
206
313
|
end
|
207
314
|
|
208
315
|
# Process the security context received in a response. It decrypts the
|
@@ -214,39 +321,18 @@ module RubySMB
|
|
214
321
|
#
|
215
322
|
# @param dcerpc_response [Response] the Response packet
|
216
323
|
# containing the security context to process
|
217
|
-
# @param opts [Hash] the
|
324
|
+
# @param opts [Hash] the authentication options: `:auth_type` and
|
218
325
|
# `:auth_level`. To enable errors when signature check fails, set the
|
219
326
|
# `:raise_signature_error` option to true
|
220
327
|
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
221
328
|
# @raise [Error::CommunicationError] if socket-related error occurs
|
222
329
|
def handle_integrity_privacy(dcerpc_response, auth_level:, auth_type:, raise_signature_error: false)
|
223
|
-
|
224
|
-
|
225
|
-
encrypted_stub = dcerpc_response.stub.to_binary_s + dcerpc_response.auth_pad.to_binary_s
|
226
|
-
case auth_type
|
227
|
-
when RPC_C_AUTHN_NONE
|
228
|
-
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
229
|
-
decrypted_stub = @ntlm_client.session.unseal_message(encrypted_stub)
|
230
|
-
when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
|
231
|
-
# TODO
|
232
|
-
raise NotImplementedError
|
233
|
-
else
|
234
|
-
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
unless decrypted_stub.empty?
|
239
|
-
pad_length = dcerpc_response.sec_trailer.auth_pad_length.to_i
|
240
|
-
dcerpc_response.stub = decrypted_stub[0..-(pad_length + 1)]
|
241
|
-
dcerpc_response.auth_pad = decrypted_stub[-(pad_length)..-1]
|
330
|
+
unless [RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(auth_level)
|
331
|
+
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
242
332
|
end
|
333
|
+
signature_valid = auth_provider_decrypt_and_verify(dcerpc_response)
|
243
334
|
|
244
|
-
|
245
|
-
data_to_check = dcerpc_response.stub.to_binary_s
|
246
|
-
if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
|
247
|
-
data_to_check = dcerpc_response.to_binary_s[0..-(dcerpc_response.pdu_header.auth_length + 1)]
|
248
|
-
end
|
249
|
-
unless @ntlm_client.session.verify_signature(signature, data_to_check)
|
335
|
+
unless signature_valid
|
250
336
|
if raise_signature_error
|
251
337
|
raise Error::InvalidPacket.new(
|
252
338
|
"Wrong packet signature received (set `raise_signature_error` to false to ignore)"
|
@@ -264,12 +350,10 @@ module RubySMB
|
|
264
350
|
#
|
265
351
|
# @param req [BinData::Record] the request to be updated
|
266
352
|
# @param auth [String] the authentication data
|
267
|
-
|
268
|
-
# @param auth_level [Integer] the authentication level
|
269
|
-
def add_auth_verifier(req, auth, auth_type, auth_level)
|
353
|
+
def add_auth_verifier(req, auth)
|
270
354
|
req.sec_trailer = {
|
271
|
-
auth_type: auth_type,
|
272
|
-
auth_level: auth_level,
|
355
|
+
auth_type: @auth_type,
|
356
|
+
auth_level: @auth_level,
|
273
357
|
auth_context_id: @ctx_id + @auth_ctx_id_base
|
274
358
|
}
|
275
359
|
req.auth_value = auth
|
@@ -277,46 +361,5 @@ module RubySMB
|
|
277
361
|
|
278
362
|
nil
|
279
363
|
end
|
280
|
-
|
281
|
-
def process_ntlm_type2(type2_message)
|
282
|
-
ntlmssp_offset = type2_message.index('NTLMSSP')
|
283
|
-
type2_blob = type2_message.slice(ntlmssp_offset..-1)
|
284
|
-
type2_b64_message = [type2_blob].pack('m')
|
285
|
-
type3_message = @ntlm_client.init_context(type2_b64_message)
|
286
|
-
auth3 = type3_message.serialize
|
287
|
-
|
288
|
-
@session_key = @ntlm_client.session_key
|
289
|
-
auth3
|
290
|
-
end
|
291
|
-
|
292
|
-
# Send a rpc_auth3 PDU that ends the authentication handshake.
|
293
|
-
#
|
294
|
-
# @param response [BindAck] the BindAck response packet
|
295
|
-
# @param auth_type [Integer] the authentication type
|
296
|
-
# @param auth_level [Integer] the authentication level
|
297
|
-
# @raise [ArgumentError] if `:auth_type` is unknown
|
298
|
-
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
299
|
-
def send_auth3(response, auth_type, auth_level)
|
300
|
-
case auth_type
|
301
|
-
when RPC_C_AUTHN_NONE
|
302
|
-
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
303
|
-
auth3 = process_ntlm_type2(response.auth_value)
|
304
|
-
when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
|
305
|
-
# TODO
|
306
|
-
raise NotImplementedError
|
307
|
-
else
|
308
|
-
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
309
|
-
end
|
310
|
-
|
311
|
-
rpc_auth3 = RpcAuth3.new
|
312
|
-
add_auth_verifier(rpc_auth3, auth3, auth_type, auth_level)
|
313
|
-
rpc_auth3.pdu_header.call_id = @call_id
|
314
|
-
|
315
|
-
# The server should not respond
|
316
|
-
send_packet(rpc_auth3)
|
317
|
-
@call_id += 1
|
318
|
-
|
319
|
-
nil
|
320
|
-
end
|
321
364
|
end
|
322
365
|
end
|
data/lib/ruby_smb/version.rb
CHANGED
@@ -9,7 +9,12 @@ RSpec.describe RubySMB::Dcerpc::Client do
|
|
9
9
|
let(:endpoint) { RubySMB::Dcerpc::Samr }
|
10
10
|
|
11
11
|
subject(:client) { described_class.new(host, endpoint) }
|
12
|
-
subject(:auth_client)
|
12
|
+
subject(:auth_client) do
|
13
|
+
result = described_class.new(host, endpoint, username: 'testuser', password: '1234')
|
14
|
+
result.force_set_auth_params(RubySMB::Dcerpc::RPC_C_AUTHN_WINNT, RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
|
15
|
+
|
16
|
+
result
|
17
|
+
end
|
13
18
|
|
14
19
|
it { is_expected.to respond_to :domain }
|
15
20
|
it { is_expected.to respond_to :local_workstation }
|
@@ -135,21 +140,21 @@ RSpec.describe RubySMB::Dcerpc::Client do
|
|
135
140
|
describe '#add_auth_verifier' do
|
136
141
|
let(:req) { RubySMB::Dcerpc::Bind.new }
|
137
142
|
let(:auth_type) { RubySMB::Dcerpc::RPC_C_AUTHN_WINNT }
|
138
|
-
let(:auth_level) {
|
143
|
+
let(:auth_level) { RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY }
|
139
144
|
let(:auth) { 'serialized auth value' }
|
140
145
|
|
141
146
|
it 'sets #auth_value field to the expected value' do
|
142
|
-
auth_client.add_auth_verifier(req, auth
|
147
|
+
auth_client.add_auth_verifier(req, auth)
|
143
148
|
expect(req.auth_value).to eq(auth)
|
144
149
|
end
|
145
150
|
|
146
151
|
it 'sets PDUHeader #auth_length field to the expected value' do
|
147
|
-
auth_client.add_auth_verifier(req, auth
|
152
|
+
auth_client.add_auth_verifier(req, auth)
|
148
153
|
expect(req.pdu_header.auth_length).to eq(auth.length)
|
149
154
|
end
|
150
155
|
|
151
156
|
it 'sets #sec_trailer field to the expected value' do
|
152
|
-
auth_client.add_auth_verifier(req, auth
|
157
|
+
auth_client.add_auth_verifier(req, auth)
|
153
158
|
expect(req.sec_trailer.auth_type).to eq(auth_type)
|
154
159
|
expect(req.sec_trailer.auth_level).to eq(auth_level)
|
155
160
|
expect(req.sec_trailer.auth_context_id).to eq(auth_client.instance_variable_get(:@auth_ctx_id_base))
|
@@ -202,29 +207,29 @@ RSpec.describe RubySMB::Dcerpc::Client do
|
|
202
207
|
end
|
203
208
|
|
204
209
|
it 'add an auth verifier to the RpcAuth3 packet' do
|
205
|
-
auth_client.
|
206
|
-
expect(auth_client).to have_received(:add_auth_verifier).with(rpc_auth3, auth3
|
210
|
+
auth_client.auth_provider_complete_handshake(bindack, auth_type: auth_type, auth_level: auth_level)
|
211
|
+
expect(auth_client).to have_received(:add_auth_verifier).with(rpc_auth3, auth3)
|
207
212
|
end
|
208
213
|
|
209
214
|
it 'sets the PDUHeader #call_id to the expected value' do
|
210
215
|
auth_client.instance_variable_set(:@call_id, 56)
|
211
|
-
auth_client.
|
216
|
+
auth_client.auth_provider_complete_handshake(bindack, auth_type: auth_type, auth_level: auth_level)
|
212
217
|
expect(rpc_auth3.pdu_header.call_id).to eq(56)
|
213
218
|
end
|
214
219
|
|
215
220
|
it 'sends the RpcAuth3 packet' do
|
216
|
-
auth_client.
|
221
|
+
auth_client.auth_provider_complete_handshake(bindack, auth_type: auth_type, auth_level: auth_level)
|
217
222
|
expect(auth_client).to have_received(:send_packet).with(rpc_auth3)
|
218
223
|
end
|
219
224
|
|
220
225
|
it 'increments #call_id' do
|
221
|
-
auth_client.
|
226
|
+
auth_client.auth_provider_complete_handshake(bindack, auth_type: auth_type, auth_level: auth_level)
|
222
227
|
expect(auth_client.instance_variable_get(:@call_id)).to eq(2)
|
223
228
|
end
|
224
229
|
|
225
230
|
context 'with RPC_C_AUTHN_WINNT auth_type' do
|
226
231
|
it 'processes NTLM type2 message' do
|
227
|
-
auth_client.
|
232
|
+
auth_client.auth_provider_complete_handshake(bindack, auth_type: auth_type, auth_level: auth_level)
|
228
233
|
expect(auth_client).to have_received(:process_ntlm_type2).with(auth)
|
229
234
|
end
|
230
235
|
end
|
@@ -300,7 +305,7 @@ RSpec.describe RubySMB::Dcerpc::Client do
|
|
300
305
|
|
301
306
|
context 'with RPC_C_AUTHN_WINNT auth_type' do
|
302
307
|
let(:kwargs) do {
|
303
|
-
auth_level: RubySMB::Dcerpc::
|
308
|
+
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
|
304
309
|
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
|
305
310
|
}
|
306
311
|
end
|
@@ -310,7 +315,7 @@ RSpec.describe RubySMB::Dcerpc::Client do
|
|
310
315
|
allow(client.ntlm_client).to receive(:init_context).and_return(type1_message)
|
311
316
|
allow(type1_message).to receive(:serialize).and_return(auth)
|
312
317
|
allow(client).to receive(:add_auth_verifier)
|
313
|
-
allow(client).to receive(:
|
318
|
+
allow(client).to receive(:auth_provider_complete_handshake)
|
314
319
|
end
|
315
320
|
|
316
321
|
it 'raises an exception if the NTLM client is not initialized' do
|
@@ -320,12 +325,12 @@ RSpec.describe RubySMB::Dcerpc::Client do
|
|
320
325
|
|
321
326
|
it 'adds the auth verifier with a NTLM type1 message' do
|
322
327
|
client.bind(**kwargs)
|
323
|
-
expect(client).to have_received(:add_auth_verifier).with(bind_req, auth
|
328
|
+
expect(client).to have_received(:add_auth_verifier).with(bind_req, auth)
|
324
329
|
end
|
325
330
|
|
326
331
|
it 'sends an auth3 request' do
|
327
332
|
client.bind(**kwargs)
|
328
|
-
expect(client).to have_received(:
|
333
|
+
expect(client).to have_received(:auth_provider_complete_handshake).with(bindack_response, auth_type: kwargs[:auth_type], auth_level: kwargs[:auth_level])
|
329
334
|
end
|
330
335
|
end
|
331
336
|
end
|
@@ -372,10 +377,14 @@ RSpec.describe RubySMB::Dcerpc::Client do
|
|
372
377
|
|
373
378
|
describe '#set_integrity_privacy' do
|
374
379
|
let(:dcerpc_req) do
|
375
|
-
RubySMB::Dcerpc::Request.new(
|
380
|
+
req = RubySMB::Dcerpc::Request.new(
|
376
381
|
{ opnum: RubySMB::Dcerpc::Winreg::REG_ENUM_KEY },
|
377
382
|
{ endpoint: 'Winreg' }
|
378
383
|
)
|
384
|
+
req.sec_trailer.auth_pad_length = 8
|
385
|
+
req.auth_pad = "\x00" * 8
|
386
|
+
|
387
|
+
req
|
379
388
|
end
|
380
389
|
let(:auth_level) { RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY }
|
381
390
|
let(:auth_type) { RubySMB::Dcerpc::RPC_C_AUTHN_WINNT }
|
@@ -656,6 +665,8 @@ RSpec.describe RubySMB::Dcerpc::Client do
|
|
656
665
|
# Make sure the encrypted stub includes the correct pad to make sure sec_trailer is 16-bytes aligned
|
657
666
|
allow(session).to receive(:unseal_message).and_return(decrypted_stub + auth_pad)
|
658
667
|
allow(session).to receive(:verify_signature).and_return true
|
668
|
+
dcerpc_res.sec_trailer.auth_type = auth_type
|
669
|
+
dcerpc_res.sec_trailer.auth_level = auth_level
|
659
670
|
end
|
660
671
|
|
661
672
|
it 'verifies the signature' do
|
@@ -698,6 +709,8 @@ RSpec.describe RubySMB::Dcerpc::Client do
|
|
698
709
|
context 'without RPC_C_AUTHN_LEVEL_PKT_PRIVACY auth_level' do
|
699
710
|
it 'does not encrypt the stub' do
|
700
711
|
plain_stub = dcerpc_res.stub.to_binary_s
|
712
|
+
dcerpc_res.sec_trailer.auth_level = RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
|
713
|
+
dcerpc_res.sec_trailer.auth_type = RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
|
701
714
|
auth_client.handle_integrity_privacy(dcerpc_res, auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, auth_type: auth_type)
|
702
715
|
expect(dcerpc_res.stub).to eq(plain_stub)
|
703
716
|
expect(session).to_not have_received(:unseal_message)
|
@@ -706,6 +719,8 @@ RSpec.describe RubySMB::Dcerpc::Client do
|
|
706
719
|
|
707
720
|
context 'with an unsupported auth_level' do
|
708
721
|
it 'raises an Argument exception' do
|
722
|
+
dcerpc_res.sec_trailer.auth_level = RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
|
723
|
+
dcerpc_res.sec_trailer.auth_type = 88
|
709
724
|
expect { auth_client.handle_integrity_privacy(dcerpc_res, auth_level: auth_level, auth_type: 88) }.to raise_error(ArgumentError)
|
710
725
|
end
|
711
726
|
end
|
@@ -6,7 +6,10 @@ RSpec.describe RubySMB::Dcerpc::Drsr do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
let(:drsr) do
|
9
|
-
RubySMB::Dcerpc::Client.new('1.2.3.4', RubySMB::Dcerpc::Drsr)
|
9
|
+
drsr = RubySMB::Dcerpc::Client.new('1.2.3.4', RubySMB::Dcerpc::Drsr)
|
10
|
+
drsr.force_set_auth_params(RubySMB::Dcerpc::RPC_C_AUTHN_WINNT, RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
|
11
|
+
|
12
|
+
drsr
|
10
13
|
end
|
11
14
|
|
12
15
|
describe described_class::DrsHandle do
|
@@ -134,12 +134,6 @@ RSpec.describe RubySMB::Dcerpc::Request do
|
|
134
134
|
packet.pdu_header.auth_length = 10
|
135
135
|
expect(packet.auth_pad?).to be true
|
136
136
|
end
|
137
|
-
|
138
|
-
it 'makes sure #sec_trailer is 16-bytes aligned with the begining of the PDU body (stub)' do
|
139
|
-
packet.pdu_header.auth_length = 6
|
140
|
-
packet.stub = 'A' * rand(0xFF)
|
141
|
-
expect((packet.sec_trailer.abs_offset - packet.stub.abs_offset) % 16).to eq(0)
|
142
|
-
end
|
143
137
|
end
|
144
138
|
|
145
139
|
describe '#sec_trailer' do
|
@@ -77,12 +77,6 @@ RSpec.describe RubySMB::Dcerpc::Response do
|
|
77
77
|
packet.pdu_header.auth_length = 10
|
78
78
|
expect(packet.auth_pad?).to be true
|
79
79
|
end
|
80
|
-
|
81
|
-
it 'makes sure #sec_trailer is 16-bytes aligned with the begining of the PDU body (stub)' do
|
82
|
-
packet.pdu_header.auth_length = 6
|
83
|
-
packet.stub = 'A' * rand(0xFF)
|
84
|
-
expect((packet.sec_trailer.abs_offset - packet.stub.abs_offset) % 16).to eq(0)
|
85
|
-
end
|
86
80
|
end
|
87
81
|
|
88
82
|
describe '#sec_trailer' do
|
@@ -36,20 +36,6 @@ RSpec.describe RubySMB::Dcerpc::SecTrailer do
|
|
36
36
|
expect(packet.auth_pad_length).to eq(0)
|
37
37
|
end
|
38
38
|
|
39
|
-
context 'when the parent structure has an #auth_pad field' do
|
40
|
-
let(:pad) { 'A' * rand(0xFF) }
|
41
|
-
let(:packet_with_parent) do
|
42
|
-
Class.new(BinData::Record) do
|
43
|
-
string :auth_pad
|
44
|
-
sec_trailer :sec_trailer
|
45
|
-
end.new(auth_pad: pad)
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'has a value equal to the size of the #auth_pad field' do
|
49
|
-
expect(packet_with_parent.sec_trailer.auth_pad_length).to eq(pad.size)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
39
|
context 'when the parent structure does not have an #auth_pad field' do
|
54
40
|
let(:pad) { 'A' * rand(0xFF) }
|
55
41
|
let(:packet_with_parent) 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.2.
|
4
|
+
version: 3.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Metasploit Hackers
|
@@ -97,7 +97,7 @@ cert_chain:
|
|
97
97
|
EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
|
98
98
|
9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
|
99
99
|
-----END CERTIFICATE-----
|
100
|
-
date: 2023-
|
100
|
+
date: 2023-10-25 00:00:00.000000000 Z
|
101
101
|
dependencies:
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: redcarpet
|
@@ -256,6 +256,7 @@ files:
|
|
256
256
|
- LICENSE.txt
|
257
257
|
- README.md
|
258
258
|
- Rakefile
|
259
|
+
- cortex.yaml
|
259
260
|
- examples/anonymous_auth.rb
|
260
261
|
- examples/append_file.rb
|
261
262
|
- examples/auth_capture.rb
|
@@ -295,6 +296,8 @@ files:
|
|
295
296
|
- lib/ruby_smb/create_actions.rb
|
296
297
|
- lib/ruby_smb/crypto.rb
|
297
298
|
- lib/ruby_smb/dcerpc.rb
|
299
|
+
- lib/ruby_smb/dcerpc/alter_context.rb
|
300
|
+
- lib/ruby_smb/dcerpc/alter_context_resp.rb
|
298
301
|
- lib/ruby_smb/dcerpc/bind.rb
|
299
302
|
- lib/ruby_smb/dcerpc/bind_ack.rb
|
300
303
|
- lib/ruby_smb/dcerpc/client.rb
|
@@ -343,8 +346,12 @@ files:
|
|
343
346
|
- lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb
|
344
347
|
- lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb
|
345
348
|
- lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb
|
349
|
+
- lib/ruby_smb/dcerpc/p_cont_list_t.rb
|
350
|
+
- lib/ruby_smb/dcerpc/p_result_list_t.rb
|
351
|
+
- lib/ruby_smb/dcerpc/p_result_t.rb
|
346
352
|
- lib/ruby_smb/dcerpc/p_syntax_id_t.rb
|
347
353
|
- lib/ruby_smb/dcerpc/pdu_header.rb
|
354
|
+
- lib/ruby_smb/dcerpc/port_any_t.rb
|
348
355
|
- lib/ruby_smb/dcerpc/print_system.rb
|
349
356
|
- lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_request.rb
|
350
357
|
- lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_response.rb
|
metadata.gz.sig
CHANGED
Binary file
|