ruby_smb 3.2.5 → 3.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/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
|