Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Kerberos auth for DCERPC #253

Merged
merged 9 commits into from
Oct 25, 2023
287 changes: 165 additions & 122 deletions lib/ruby_smb/dcerpc.rb

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions lib/ruby_smb/dcerpc/alter_context.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module RubySMB
module Dcerpc
# The Alter context PDU as defined in
# [The alter_context PDU](https://pubs.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06_04_01)
class AlterContext < BinData::Record
PTYPE = PTypes::ALTER_CONTEXT

endian :little

# PDU Header
pdu_header :pdu_header, label: 'PDU header'
ndr_uint16 :max_xmit_frag, label: 'Max transmit frag size', initial_value: RubySMB::Dcerpc::MAX_XMIT_FRAG
ndr_uint16 :max_recv_frag, label: 'Max receive frag size', initial_value: RubySMB::Dcerpc::MAX_RECV_FRAG
ndr_uint32 :assoc_group_id, label: 'Incarnation of client-server assoc group'
p_cont_list_t :p_context_list, label: 'Presentation context list', endpoint: -> { endpoint }

# Auth Verifier
sec_trailer :sec_trailer, onlyif: -> { pdu_header.auth_length > 0 }
string :auth_value,
onlyif: -> { pdu_header.auth_length > 0 },
read_length: -> { pdu_header.auth_length }

def initialize_instance
super
pdu_header.ptype = PTYPE
end
end
end
end

42 changes: 42 additions & 0 deletions lib/ruby_smb/dcerpc/alter_context_resp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module RubySMB
module Dcerpc
# The Alter context resp PDU as defined in
# [The alter_context_resp PDU](https://pubs.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06_04_02)

class AlterContextResp < BinData::Record
PTYPE = PTypes::ALTER_CONTEXT_RESP

# Presentation context negotiation results
ACCEPTANCE = 0
USER_REJECTION = 1
PROVIDER_REJECTION = 2

# Reasons for rejection of a context element
REASON_NOT_SPECIFIED = 0
ABSTRACT_SYNTAX_NOT_SUPPORTED = 1
PROPOSED_TRANSFER_SYNTAXES_NOT_SUPPORTED = 2
LOCAL_LIMIT_EXCEEDED = 3

endian :little

# PDU Header
pdu_header :pdu_header, label: 'PDU header'
ndr_uint16 :max_xmit_frag, label: 'Max transmit frag size', initial_value: RubySMB::Dcerpc::MAX_XMIT_FRAG
ndr_uint16 :max_recv_frag, label: 'Max receive frag size', initial_value: RubySMB::Dcerpc::MAX_RECV_FRAG
ndr_uint32 :assoc_group_id, label: 'Association group ID'
port_any_t :sec_addr, label: 'Secondary address'
p_result_list_t :p_result_list, label: 'Presentation context result list'

# Auth Verifier
sec_trailer :sec_trailer, onlyif: -> { pdu_header.auth_length > 0 }
string :auth_value,
onlyif: -> { pdu_header.auth_length > 0 },
read_length: -> { pdu_header.auth_length }

def initialize_instance
super
pdu_header.ptype = PTYPE
end
end
end
end
38 changes: 3 additions & 35 deletions lib/ruby_smb/dcerpc/bind.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,16 @@ module RubySMB
module Dcerpc
# The Bind PDU as defined in
# [The bind PDU](http://pubs.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06_04_03)
class PContElemT < Ndr::NdrStruct
default_parameter byte_align: 4
endian :little

ndr_uint16 :p_cont_id, label: 'Context ID'
ndr_uint8 :n_transfer_syn, label: 'Number of transfer syntaxes', initial_value: 1
ndr_uint8 :reserved
p_syntax_id_t :abstract_syntax, label: 'Abstract syntax',
uuid: -> { endpoint::UUID },
ver_major: -> { endpoint::VER_MAJOR },
ver_minor: -> { endpoint::VER_MINOR }
array :transfer_syntaxes, label: 'Transfer syntax', type: :p_syntax_id_t,
initial_length: -> { n_transfer_syn },
uuid: -> { Ndr::UUID },
ver_major: -> { Ndr::VER_MAJOR },
ver_minor: -> { Ndr::VER_MINOR },
byte_align: 4
end

class PContListT < Ndr::NdrStruct
default_parameter byte_align: 4
endian :little

ndr_uint8 :n_context_elem, label: 'Number of context elements', initial_value: -> { 1 }
ndr_uint8 :reserved
ndr_uint16 :reserved2
array :p_cont_elem, label: 'Presentation context elements', type: :p_cont_elem_t,
initial_length: -> {n_context_elem},
endpoint: -> {endpoint},
byte_align: 4
end

class Bind < BinData::Record
PTYPE = PTypes::BIND

endian :little

# PDU Header
pdu_header :pdu_header, label: 'PDU header'
ndr_uint16 :max_xmit_frag, label: 'max transmit frag size', initial_value: RubySMB::Dcerpc::MAX_XMIT_FRAG
ndr_uint16 :max_recv_frag, label: 'max receive frag size', initial_value: RubySMB::Dcerpc::MAX_RECV_FRAG
ndr_uint32 :assoc_group_id, label: 'incarnation of client-server assoc group'
ndr_uint16 :max_xmit_frag, label: 'Max transmit frag size', initial_value: RubySMB::Dcerpc::MAX_XMIT_FRAG
ndr_uint16 :max_recv_frag, label: 'Max receive frag size', initial_value: RubySMB::Dcerpc::MAX_RECV_FRAG
ndr_uint32 :assoc_group_id, label: 'Incarnation of client-server assoc group'
p_cont_list_t :p_context_list, label: 'Presentation context list', endpoint: -> { endpoint }

# Auth Verifier
Expand Down
31 changes: 0 additions & 31 deletions lib/ruby_smb/dcerpc/bind_ack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,6 @@ module RubySMB
module Dcerpc
# The Bind ACK PDU as defined in
# [The bind_ack PDU](http://pubs.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06_04_04)

class PResultT < Ndr::NdrStruct
default_parameter byte_align: 4
endian :little

ndr_uint16 :result, label: 'Presentation context negotiation results'
ndr_uint16 :reason, label: 'Rejection reason'
p_syntax_id_t :transfer_syntax, label: 'Presentation syntax ID',
uuid: -> { Ndr::UUID },
ver_major: -> { Ndr::VER_MAJOR },
ver_minor: -> { Ndr::VER_MINOR }
end

class PResultListT < Ndr::NdrStruct
default_parameter byte_align: 4
endian :little

ndr_uint8 :n_results, label: 'Number of results', initial_value: -> { p_results.size }
ndr_uint8 :reserved
ndr_uint16 :reserved2
array :p_results, label: 'Results', type: :p_result_t, initial_length: -> { n_results }, byte_align: 4
end

class PortAnyT < Ndr::NdrStruct
default_parameter byte_align: 2
endian :little

ndr_uint16 :str_length, label: 'Length', initial_value: -> { port_spec.to_binary_s.size }
stringz :port_spec, label: 'Port string spec', byte_align: 2
end

class BindAck < BinData::Record
PTYPE = PTypes::BIND_ACK

Expand Down
4 changes: 4 additions & 0 deletions lib/ruby_smb/dcerpc/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ def dcerpc_request(stub_packet, auth_level: nil, auth_type: nil)
if auth_level &&
[RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(auth_level)
set_integrity_privacy(dcerpc_req, auth_level: auth_level, auth_type: auth_type)
# 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
valid_offset = (((dcerpc_req.sec_trailer.abs_offset - dcerpc_req.stub.abs_offset) % 16))
valid_auth_pad = (dcerpc_req.sec_trailer.auth_pad_length == dcerpc_req.auth_pad.length)
raise Error::InvalidPacket unless valid_offset == 0 && valid_auth_pad
end

send_packet(dcerpc_req)
Expand Down
26 changes: 13 additions & 13 deletions lib/ruby_smb/dcerpc/drsr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -613,8 +613,8 @@ def drs_bind
drs_bind_request = DrsBindRequest.new(pext_client: drs_extensions_int)
response = dcerpc_request(
drs_bind_request,
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
auth_level: @auth_level,
auth_type: @auth_type
)
begin
drs_bind_response = DrsBindResponse.read(response)
Expand All @@ -640,8 +640,8 @@ def drs_bind
drs_bind_request.pext_client.assign(drs_extensions_int)
response = dcerpc_request(
drs_bind_request,
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
auth_level: @auth_level,
auth_type: @auth_type
)
begin
drs_bind_response = DrsBindResponse.read(response)
Expand All @@ -668,8 +668,8 @@ def drs_unbind(ph_drs)
drs_unbind_request = DrsUnbindRequest.new(ph_drs: ph_drs)
response = dcerpc_request(
drs_unbind_request,
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
auth_level: @auth_level,
auth_type: @auth_type
)
begin
drs_unbind_response = DrsUnbindResponse.read(response)
Expand Down Expand Up @@ -709,8 +709,8 @@ def drs_domain_controller_info(h_drs, domain)
)
response = dcerpc_request(
drs_domain_controller_info_request,
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
auth_level: @auth_level,
auth_type: @auth_type
)
begin
drs_domain_controller_info_response = DrsDomainControllerInfoResponse.read(response)
Expand Down Expand Up @@ -759,8 +759,8 @@ def drs_crack_names(h_drs, flags: 0, format_offered: DS_SID_OR_SID_HISTORY_NAME,
)
response = dcerpc_request(
drs_crack_names_request,
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
auth_level: @auth_level,
auth_type: @auth_type
)
begin
drs_crack_names_response = DrsCrackNamesResponse.read(response)
Expand Down Expand Up @@ -790,8 +790,8 @@ def decrypt_attribute_value(attribute)
unless @session_key
raise RubySMB::Error::EncryptionError, 'Unable to decrypt attribute value: session key is empty'
end
encrypted_payload = EncryptedPayload.read(attribute)

encrypted_payload = EncryptedPayload.read(attribute)
signature = OpenSSL::Digest::MD5.digest(@session_key + encrypted_payload.salt.to_binary_s)
rc4 = OpenSSL::Cipher.new('rc4')
rc4.decrypt
Expand Down Expand Up @@ -886,8 +886,8 @@ def drs_get_nc_changes(h_drs, nc_guid:, dsa_object_guid:)

response = dcerpc_request(
drs_get_nc_changes_request,
auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
auth_level: @auth_level,
auth_type: @auth_type
)
begin
drs_get_nc_changes_response = DrsGetNcChangesResponse.read(response)
Expand Down
37 changes: 37 additions & 0 deletions lib/ruby_smb/dcerpc/p_cont_list_t.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module RubySMB
module Dcerpc
# The presentation context list and its element as defined in
# [Connection-oriented PDU Data Types - Declarations](https://pubs.opengroup.org/onlinepubs/9629399/chap12.htm#tagcjh_17_06_03_01)
class PContElemT < Ndr::NdrStruct
default_parameter byte_align: 4
endian :little

ndr_uint16 :p_cont_id, label: 'Context ID'
ndr_uint8 :n_transfer_syn, label: 'Number of transfer syntaxes', initial_value: 1
ndr_uint8 :reserved
p_syntax_id_t :abstract_syntax, label: 'Abstract syntax',
uuid: -> { endpoint::UUID },
ver_major: -> { endpoint::VER_MAJOR },
ver_minor: -> { endpoint::VER_MINOR }
array :transfer_syntaxes, label: 'Transfer syntax', type: :p_syntax_id_t,
initial_length: -> { n_transfer_syn },
uuid: -> { Ndr::UUID },
ver_major: -> { Ndr::VER_MAJOR },
ver_minor: -> { Ndr::VER_MINOR },
byte_align: 4
end

class PContListT < Ndr::NdrStruct
default_parameter byte_align: 4
endian :little

ndr_uint8 :n_context_elem, label: 'Number of context elements', initial_value: -> { 1 }
ndr_uint8 :reserved
ndr_uint16 :reserved2
array :p_cont_elem, label: 'Presentation context elements', type: :p_cont_elem_t,
initial_length: -> {n_context_elem},
endpoint: -> {endpoint},
byte_align: 4
end
end
end
13 changes: 13 additions & 0 deletions lib/ruby_smb/dcerpc/p_result_list_t.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module RubySMB
module Dcerpc
class PResultListT < Ndr::NdrStruct
default_parameter byte_align: 4
endian :little

ndr_uint8 :n_results, label: 'Number of results', initial_value: -> { p_results.size }
ndr_uint8 :reserved
ndr_uint16 :reserved2
array :p_results, label: 'Results', type: :p_result_t, initial_length: -> { n_results }, byte_align: 4
end
end
end
15 changes: 15 additions & 0 deletions lib/ruby_smb/dcerpc/p_result_t.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module RubySMB
module Dcerpc
class PResultT < Ndr::NdrStruct
default_parameter byte_align: 4
endian :little

ndr_uint16 :result, label: 'Presentation context negotiation results'
ndr_uint16 :reason, label: 'Rejection reason'
p_syntax_id_t :transfer_syntax, label: 'Presentation syntax ID',
uuid: -> { Ndr::UUID },
ver_major: -> { Ndr::VER_MAJOR },
ver_minor: -> { Ndr::VER_MINOR }
end
end
end
11 changes: 11 additions & 0 deletions lib/ruby_smb/dcerpc/port_any_t.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module RubySMB
module Dcerpc
class PortAnyT < Ndr::NdrStruct
default_parameter byte_align: 2
endian :little

ndr_uint16 :str_length, label: 'Length', initial_value: -> { port_spec.to_binary_s.size }
stringz :port_spec, label: 'Port string spec', byte_align: 2, onlyif: -> { str_length > 0 }
end
end
end
11 changes: 8 additions & 3 deletions lib/ruby_smb/dcerpc/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,22 @@ class Request < BinData::Record
end
string :default
end
string :auth_pad,

string :auth_pad,
onlyif: -> { has_auth_verifier? },
length: -> { (16 - (stub.num_bytes % 16)) % 16 }
length: -> { calculate_padding_size }

# Auth Verifier
sec_trailer :sec_trailer, onlyif: -> { has_auth_verifier? }
string :auth_value, label: 'Authentication verifier',
onlyif: -> { has_auth_verifier? },
read_length: -> { pdu_header.auth_length }

# 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
def calculate_padding_size
(16 - (stub.num_bytes % 16)) % 16
end

def initialize_instance
super
pdu_header.ptype = PTYPE
Expand All @@ -125,7 +131,6 @@ def enable_encrypted_stub
def has_auth_verifier?
self.pdu_header.auth_length > 0
end

end
end
end
7 changes: 6 additions & 1 deletion lib/ruby_smb/dcerpc/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@ class Response < BinData::Record
string :stub, label: 'Stub', read_length: -> { stub_length }
string :auth_pad,
onlyif: -> { has_auth_verifier? },
length: -> { (16 - (stub.num_bytes % 16)) % 16 }
length: -> { calculate_padding_size }

# Auth Verifier
sec_trailer :sec_trailer, onlyif: -> { has_auth_verifier? }
string :auth_value, label: 'Authentication verifier',
onlyif: -> { has_auth_verifier? },
read_length: -> { pdu_header.auth_length }

# 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
def calculate_padding_size
(16 - (stub.num_bytes % 16)) % 16
end

def initialize_instance
super
pdu_header.ptype = PTYPE
Expand Down
Loading
Loading