ruby_smb 2.0.12 → 2.0.13
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/.github/workflows/verify.yml +1 -1
- data/examples/dump_secrets_from_sid.rb +207 -0
- data/examples/enum_domain_users.rb +75 -0
- data/examples/get_computer_info.rb +42 -0
- data/examples/query_service_status.rb +42 -4
- data/lib/ruby_smb/client.rb +3 -14
- data/lib/ruby_smb/dcerpc/bind.rb +28 -20
- data/lib/ruby_smb/dcerpc/bind_ack.rb +29 -28
- data/lib/ruby_smb/dcerpc/client.rb +542 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_bind_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_bind_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_request.rb +57 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_response.rb +76 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_request.rb +46 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_response.rb +168 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_extensions.rb +56 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_request.rb +121 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_response.rb +118 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_unbind_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_unbind_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/drsr.rb +909 -0
- data/lib/ruby_smb/dcerpc/epm/epm_ept_map_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/epm/epm_ept_map_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/epm/epm_twrt.rb +211 -0
- data/lib/ruby_smb/dcerpc/epm.rb +75 -0
- data/lib/ruby_smb/dcerpc/error.rb +17 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +1159 -297
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +3 -13
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +3 -3
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +3 -13
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +3 -11
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/netlogon.rb +5 -4
- data/lib/ruby_smb/dcerpc/p_syntax_id_t.rb +4 -3
- data/lib/ruby_smb/dcerpc/pdu_header.rb +7 -7
- data/lib/ruby_smb/dcerpc/ptypes.rb +1 -0
- data/lib/ruby_smb/dcerpc/request.rb +79 -32
- data/lib/ruby_smb/dcerpc/response.rb +45 -10
- data/lib/ruby_smb/dcerpc/rpc_auth3.rb +28 -0
- data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +11 -11
- data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +118 -0
- data/lib/ruby_smb/dcerpc/samr/rpc_sid.rb +150 -0
- data/lib/ruby_smb/dcerpc/samr/samr_close_handle_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_close_handle_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr/samr_connect_request.rb +32 -0
- data/lib/ruby_smb/dcerpc/samr/samr_connect_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response.rb +55 -0
- data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_request.rb +48 -0
- data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_response.rb +38 -0
- data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_response.rb +48 -0
- data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/samr/samr_open_domain_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/samr/samr_open_domain_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr/samr_open_user_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/samr/samr_open_user_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr.rb +613 -0
- data/lib/ruby_smb/dcerpc/sec_trailer.rb +26 -0
- data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +56 -79
- data/lib/ruby_smb/dcerpc/srvsvc.rb +27 -4
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +13 -25
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +2 -2
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +4 -14
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +3 -11
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +12 -11
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +9 -8
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +3 -3
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl.rb +1 -3
- data/lib/ruby_smb/dcerpc/uuid.rb +3 -0
- data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +2 -2
- data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +2 -13
- data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +3 -3
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +3 -20
- data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +3 -20
- data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +5 -14
- data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +5 -14
- data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +1 -9
- data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +4 -3
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +5 -6
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +2 -2
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +9 -18
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +4 -14
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +7 -15
- data/lib/ruby_smb/dcerpc/winreg/regsam.rb +3 -1
- data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +0 -9
- data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg.rb +10 -14
- data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response.rb +88 -0
- data/lib/ruby_smb/dcerpc/wkssvc.rb +65 -0
- data/lib/ruby_smb/dcerpc.rb +41 -11
- data/lib/ruby_smb/field/file_time.rb +1 -1
- data/lib/ruby_smb/field/string16.rb +5 -1
- data/lib/ruby_smb/ntlm.rb +18 -2
- data/lib/ruby_smb/smb1/pipe.rb +4 -0
- data/lib/ruby_smb/smb2/pipe.rb +4 -0
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +1 -2
- data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +69 -41
- data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +75 -21
- data/spec/lib/ruby_smb/dcerpc/client_spec.rb +714 -0
- data/spec/lib/ruby_smb/dcerpc/drsr_spec.rb +2169 -0
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +3792 -1373
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +4 -4
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +4 -4
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/p_syntax_id_t_spec.rb +18 -4
- data/spec/lib/ruby_smb/dcerpc/pdu_header_spec.rb +27 -1
- data/spec/lib/ruby_smb/dcerpc/request_spec.rb +76 -11
- data/spec/lib/ruby_smb/dcerpc/response_spec.rb +99 -9
- data/spec/lib/ruby_smb/dcerpc/rpc_auth3_spec.rb +75 -0
- data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +29 -28
- data/spec/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string_spec.rb +340 -0
- data/spec/lib/ruby_smb/dcerpc/samr/rpc_sid_spec.rb +116 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_request_spec.rb +40 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_response_spec.rb +48 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_request_spec.rb +56 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_response_spec.rb +47 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request_spec.rb +63 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response_spec.rb +265 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request_spec.rb +52 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response_spec.rb +36 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_request_spec.rb +56 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_response_spec.rb +48 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request_spec.rb +48 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response_spec.rb +42 -0
- data/spec/lib/ruby_smb/dcerpc/samr_spec.rb +420 -0
- data/spec/lib/ruby_smb/dcerpc/sec_trailer_spec.rb +92 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +149 -110
- data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +21 -17
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +56 -79
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +4 -4
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +19 -29
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +9 -15
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +22 -22
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +18 -14
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +5 -4
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +1 -5
- data/spec/lib/ruby_smb/dcerpc/uuid_spec.rb +15 -23
- data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +4 -41
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +4 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +4 -52
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +4 -56
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +10 -34
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +10 -34
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +2 -26
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +17 -25
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +20 -44
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +8 -32
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +10 -22
- data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +4 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +0 -12
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +18 -47
- data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb +43 -0
- data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb +410 -0
- data/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +70 -0
- data/spec/lib/ruby_smb/field/string16_spec.rb +22 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm/os_version_spec.rb +1 -1
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +18 -37
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +18 -16
- data/spec/support/bin_helper.rb +9 -0
- data.tar.gz.sig +0 -0
- metadata +96 -5
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +0 -38
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +0 -135
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module Dcerpc
|
|
3
|
+
|
|
4
|
+
# Represents DCERPC SMB client capable of talking to an RPC endpoint in stand-alone.
|
|
5
|
+
class Client
|
|
6
|
+
require 'bindata'
|
|
7
|
+
require 'windows_error'
|
|
8
|
+
require 'net/ntlm'
|
|
9
|
+
require 'ruby_smb/dcerpc'
|
|
10
|
+
require 'ruby_smb/gss'
|
|
11
|
+
|
|
12
|
+
include Epm
|
|
13
|
+
|
|
14
|
+
# The default maximum size of a RPC message that the Client accepts (in bytes)
|
|
15
|
+
MAX_BUFFER_SIZE = 64512
|
|
16
|
+
# The read timeout when receiving packets.
|
|
17
|
+
READ_TIMEOUT = 30
|
|
18
|
+
# The default Endpoint Mapper port
|
|
19
|
+
ENDPOINT_MAPPER_PORT = 135
|
|
20
|
+
|
|
21
|
+
# The domain you're trying to authenticate to
|
|
22
|
+
# @!attribute [rw] domain
|
|
23
|
+
# @return [String]
|
|
24
|
+
attr_accessor :domain
|
|
25
|
+
|
|
26
|
+
# The local workstation to pretend to be
|
|
27
|
+
# @!attribute [rw] local_workstation
|
|
28
|
+
# @return [String]
|
|
29
|
+
attr_accessor :local_workstation
|
|
30
|
+
|
|
31
|
+
# The NTLM client used for authentication
|
|
32
|
+
# @!attribute [rw] ntlm_client
|
|
33
|
+
# @return [String]
|
|
34
|
+
attr_accessor :ntlm_client
|
|
35
|
+
|
|
36
|
+
# The username to authenticate with
|
|
37
|
+
# @!attribute [rw] username
|
|
38
|
+
# @return [String]
|
|
39
|
+
attr_accessor :username
|
|
40
|
+
|
|
41
|
+
# The password to authenticate with
|
|
42
|
+
# @!attribute [rw] password
|
|
43
|
+
# @return [String]
|
|
44
|
+
attr_accessor :password
|
|
45
|
+
|
|
46
|
+
# The Netbios Name of the Peer/Server.
|
|
47
|
+
# @!attribute [rw] default_name
|
|
48
|
+
# @return [String]
|
|
49
|
+
attr_accessor :default_name
|
|
50
|
+
|
|
51
|
+
# The Netbios Domain of the Peer/Server.
|
|
52
|
+
# @!attribute [rw] default_domain
|
|
53
|
+
# @return [String]
|
|
54
|
+
attr_accessor :default_domain
|
|
55
|
+
|
|
56
|
+
# The Fully Qualified Domain Name (FQDN) of the computer.
|
|
57
|
+
# @!attribute [rw] dns_host_name
|
|
58
|
+
# @return [String]
|
|
59
|
+
attr_accessor :dns_host_name
|
|
60
|
+
|
|
61
|
+
# The Fully Qualified Domain Name (FQDN) of the domain.
|
|
62
|
+
# @!attribute [rw] dns_domain_name
|
|
63
|
+
# @return [String]
|
|
64
|
+
attr_accessor :dns_domain_name
|
|
65
|
+
|
|
66
|
+
# The Fully Qualified Domain Name (FQDN) of the forest.
|
|
67
|
+
# @!attribute [rw] dns_tree_name
|
|
68
|
+
# @return [String]
|
|
69
|
+
attr_accessor :dns_tree_name
|
|
70
|
+
|
|
71
|
+
# The OS version number (<major>.<minor>.<build>) of the Peer/Server.
|
|
72
|
+
# @!attribute [rw] os_version
|
|
73
|
+
# @return [String]
|
|
74
|
+
attr_accessor :os_version
|
|
75
|
+
|
|
76
|
+
# The maximum size SMB message that the Client accepts (in bytes)
|
|
77
|
+
# The default value is equal to {MAX_BUFFER_SIZE}.
|
|
78
|
+
# @!attribute [rw] max_buffer_size
|
|
79
|
+
# @return [Integer]
|
|
80
|
+
attr_accessor :max_buffer_size
|
|
81
|
+
|
|
82
|
+
# The TCP socket to connect to the remote host
|
|
83
|
+
# @!attribute [rw] tcp_socket
|
|
84
|
+
# @return [TcpSocket]
|
|
85
|
+
attr_accessor :tcp_socket
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# @param host [String] The remote host
|
|
89
|
+
# @param endpoint [Module] A module endpoint that defines UUID, VER_MAJOR and
|
|
90
|
+
# VER_MINOR constants (e.g. Drsr)
|
|
91
|
+
# @param tcp_socket [TcpSocket] The socket to use. If not provided, a new
|
|
92
|
+
# socket will be created when calling #connect
|
|
93
|
+
# @param read_timeout [Integer] The read timeout value to use
|
|
94
|
+
# @param username [String] The username to authenticate with, if needed
|
|
95
|
+
# @param password [String] The password to authenticate with, if needed.
|
|
96
|
+
# Note that a NTLM hash can be used instead of a password.
|
|
97
|
+
# @param domain [String] The domain to authenticate to, if needed
|
|
98
|
+
# @param local_workstation [String] The workstation name to authenticate to, if needed
|
|
99
|
+
# @param ntlm_flags [Integer] The flags to pass to the Net:NTLM client
|
|
100
|
+
def initialize(host,
|
|
101
|
+
endpoint,
|
|
102
|
+
tcp_socket: nil,
|
|
103
|
+
read_timeout: READ_TIMEOUT,
|
|
104
|
+
username: '',
|
|
105
|
+
password: '',
|
|
106
|
+
domain: '.',
|
|
107
|
+
local_workstation: 'WORKSTATION',
|
|
108
|
+
ntlm_flags: NTLM::DEFAULT_CLIENT_FLAGS)
|
|
109
|
+
|
|
110
|
+
@endpoint = endpoint
|
|
111
|
+
extend @endpoint
|
|
112
|
+
|
|
113
|
+
@host = host
|
|
114
|
+
@tcp_socket = tcp_socket
|
|
115
|
+
@read_timeout = read_timeout
|
|
116
|
+
@domain = domain
|
|
117
|
+
@local_workstation = local_workstation
|
|
118
|
+
@username = username.encode('utf-8')
|
|
119
|
+
@password = password.encode('utf-8')
|
|
120
|
+
@max_buffer_size = MAX_BUFFER_SIZE
|
|
121
|
+
@call_id = 1
|
|
122
|
+
@ctx_id = 0
|
|
123
|
+
@auth_ctx_id_base = rand(0xFFFFFFFF)
|
|
124
|
+
|
|
125
|
+
unless username.empty? && password.empty?
|
|
126
|
+
@ntlm_client = Net::NTLM::Client.new(
|
|
127
|
+
@username,
|
|
128
|
+
@password,
|
|
129
|
+
workstation: @local_workstation,
|
|
130
|
+
domain: @domain,
|
|
131
|
+
flags: ntlm_flags
|
|
132
|
+
)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Connect to the RPC endpoint. If a TCP socket was not provided, it takes
|
|
137
|
+
# care of asking the Enpoint Mapper Interface the port used by the given
|
|
138
|
+
# endpoint provided in #initialize and connect a TCP socket
|
|
139
|
+
#
|
|
140
|
+
# @param port [Integer] An optional port number to connect to. If
|
|
141
|
+
# provided, it will not ask the Enpoint Mapper Interface for a port
|
|
142
|
+
# number.
|
|
143
|
+
# @return [TcpSocket] The connected TCP socket
|
|
144
|
+
def connect(port: nil)
|
|
145
|
+
return if @tcp_socket
|
|
146
|
+
unless port
|
|
147
|
+
@tcp_socket = TCPSocket.new(@host, ENDPOINT_MAPPER_PORT)
|
|
148
|
+
bind(endpoint: Epm)
|
|
149
|
+
begin
|
|
150
|
+
host_port = get_host_port_from_ept_mapper(
|
|
151
|
+
uuid: @endpoint::UUID,
|
|
152
|
+
maj_ver: @endpoint::VER_MAJOR,
|
|
153
|
+
min_ver: @endpoint::VER_MINOR
|
|
154
|
+
)
|
|
155
|
+
rescue RubySMB::Dcerpc::Error::DcerpcError => e
|
|
156
|
+
e.message.prepend(
|
|
157
|
+
"Cannot resolve the remote port number for endpoint #{@endpoint::UUID}. "\
|
|
158
|
+
"Set @tcp_socket parameter to specify the service port number and bypass "\
|
|
159
|
+
"EPM port resolution. Error: "
|
|
160
|
+
)
|
|
161
|
+
raise e
|
|
162
|
+
end
|
|
163
|
+
port = host_port[:port]
|
|
164
|
+
@tcp_socket.close
|
|
165
|
+
@tcp_socket = nil
|
|
166
|
+
end
|
|
167
|
+
@tcp_socket = TCPSocket.new(@host, port)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Close the TCP Socket
|
|
171
|
+
def close
|
|
172
|
+
@tcp_socket.close if @tcp_socket && !@tcp_socket.closed?
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Add the authentication verifier to the packet. This includes a sec
|
|
176
|
+
# trailer and the actual authentication data.
|
|
177
|
+
#
|
|
178
|
+
# @param req [BinData::Record] the request to be updated
|
|
179
|
+
# @param auth [String] the authentication data
|
|
180
|
+
# @param auth_type [Integer] the authentication type
|
|
181
|
+
# @param auth_level [Integer] the authentication level
|
|
182
|
+
def add_auth_verifier(req, auth, auth_type, auth_level)
|
|
183
|
+
req.sec_trailer = {
|
|
184
|
+
auth_type: auth_type,
|
|
185
|
+
auth_level: auth_level,
|
|
186
|
+
auth_context_id: @ctx_id + @auth_ctx_id_base
|
|
187
|
+
}
|
|
188
|
+
req.auth_value = auth
|
|
189
|
+
req.pdu_header.auth_length = auth.length
|
|
190
|
+
|
|
191
|
+
nil
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def process_ntlm_type2(type2_message)
|
|
195
|
+
ntlmssp_offset = type2_message.index('NTLMSSP')
|
|
196
|
+
type2_blob = type2_message.slice(ntlmssp_offset..-1)
|
|
197
|
+
type2_b64_message = [type2_blob].pack('m')
|
|
198
|
+
type3_message = @ntlm_client.init_context(type2_b64_message)
|
|
199
|
+
auth3 = type3_message.serialize
|
|
200
|
+
|
|
201
|
+
@session_key = @ntlm_client.session_key
|
|
202
|
+
challenge_message = @ntlm_client.session.challenge_message
|
|
203
|
+
store_target_info(challenge_message.target_info) if challenge_message.has_flag?(:TARGET_INFO)
|
|
204
|
+
@os_version = extract_os_version(challenge_message.os_version.to_s) unless challenge_message.os_version.empty?
|
|
205
|
+
auth3
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Send a rpc_auth3 PDU that ends the authentication handshake.
|
|
209
|
+
#
|
|
210
|
+
# @param response [BindAck] the BindAck response packet
|
|
211
|
+
# @param auth_type [Integer] the authentication type
|
|
212
|
+
# @param auth_level [Integer] the authentication level
|
|
213
|
+
# @raise [ArgumentError] if `:auth_type` is unknown
|
|
214
|
+
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
|
215
|
+
def send_auth3(response, auth_type, auth_level)
|
|
216
|
+
case auth_type
|
|
217
|
+
when RPC_C_AUTHN_NONE
|
|
218
|
+
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
|
219
|
+
auth3 = process_ntlm_type2(response.auth_value)
|
|
220
|
+
when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
|
|
221
|
+
# TODO
|
|
222
|
+
raise NotImplementedError
|
|
223
|
+
else
|
|
224
|
+
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
rpc_auth3 = RpcAuth3.new
|
|
228
|
+
add_auth_verifier(rpc_auth3, auth3, auth_type, auth_level)
|
|
229
|
+
rpc_auth3.pdu_header.call_id = @call_id
|
|
230
|
+
|
|
231
|
+
# The server should not respond
|
|
232
|
+
send_packet(rpc_auth3)
|
|
233
|
+
@call_id += 1
|
|
234
|
+
|
|
235
|
+
nil
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Bind to the remote server interface endpoint. It takes care of adding
|
|
239
|
+
# the necessary authentication verifier if `:auth_level` is set to
|
|
240
|
+
# anything different than RPC_C_AUTHN_LEVEL_NONE
|
|
241
|
+
#
|
|
242
|
+
# @param endpoint [Module] the endpoint to bind to. This must be a Dcerpc
|
|
243
|
+
# class with UUID, VER_MAJOR and VER_MINOR constants defined.
|
|
244
|
+
# @param auth_level [Integer] the authentication level
|
|
245
|
+
# @param auth_type [Integer] the authentication type
|
|
246
|
+
# @return [BindAck] the BindAck response packet
|
|
247
|
+
# @raise [Error::InvalidPacket] if an invalid packet is received
|
|
248
|
+
# @raise [Error::BindError] if the response is not a BindAck packet or if the Bind result code is not ACCEPTANCE
|
|
249
|
+
# @raise [ArgumentError] if `:auth_type` is unknown
|
|
250
|
+
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
|
251
|
+
def bind(endpoint: @endpoint, auth_level: RPC_C_AUTHN_LEVEL_NONE, auth_type: nil)
|
|
252
|
+
bind_req = Bind.new(endpoint: endpoint)
|
|
253
|
+
bind_req.pdu_header.call_id = @call_id
|
|
254
|
+
# TODO: evasion: generate random UUIDs for bogus binds
|
|
255
|
+
|
|
256
|
+
if auth_level && auth_level != RPC_C_AUTHN_LEVEL_NONE
|
|
257
|
+
case auth_type
|
|
258
|
+
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
|
259
|
+
raise ArgumentError, "NTLM Client not initialized. Username and password must be provided" unless @ntlm_client
|
|
260
|
+
type1_message = @ntlm_client.init_context
|
|
261
|
+
auth = type1_message.serialize
|
|
262
|
+
when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE
|
|
263
|
+
when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL
|
|
264
|
+
# TODO
|
|
265
|
+
raise NotImplementedError
|
|
266
|
+
else
|
|
267
|
+
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
|
268
|
+
end
|
|
269
|
+
add_auth_verifier(bind_req, auth, auth_type, auth_level)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
send_packet(bind_req)
|
|
273
|
+
bindack_response = recv_struct(BindAck)
|
|
274
|
+
# TODO: see if BindNack response should be handled too
|
|
275
|
+
|
|
276
|
+
res_list = bindack_response.p_result_list
|
|
277
|
+
if res_list.n_results == 0 ||
|
|
278
|
+
res_list.p_results[0].result != BindAck::ACCEPTANCE
|
|
279
|
+
raise Error::BindError,
|
|
280
|
+
"Bind Failed (Result: #{res_list.p_results[0].result}, Reason: #{res_list.p_results[0].reason})"
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
@max_buffer_size = bindack_response.max_xmit_frag
|
|
284
|
+
@call_id = bindack_response.pdu_header.call_id
|
|
285
|
+
|
|
286
|
+
if auth_level && auth_level != RPC_C_AUTHN_LEVEL_NONE
|
|
287
|
+
# The number of legs needed to build the security context is defined
|
|
288
|
+
# by the security provider
|
|
289
|
+
# (see [2.2.1.1.7 Security Providers](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/d4097450-c62f-484b-872f-ddf59a7a0d36))
|
|
290
|
+
case auth_type
|
|
291
|
+
when RPC_C_AUTHN_WINNT
|
|
292
|
+
send_auth3(bindack_response, auth_type, auth_level)
|
|
293
|
+
when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE
|
|
294
|
+
# TODO
|
|
295
|
+
raise NotImplementedError
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
nil
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Extract and store useful information about the peer/server from the
|
|
303
|
+
# NTLM Type 2 (challenge) TargetInfo fields.
|
|
304
|
+
#
|
|
305
|
+
# @param target_info_str [String] the Target Info string
|
|
306
|
+
def store_target_info(target_info_str)
|
|
307
|
+
target_info = Net::NTLM::TargetInfo.new(target_info_str)
|
|
308
|
+
{
|
|
309
|
+
Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME => :@default_name,
|
|
310
|
+
Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME => :@default_domain,
|
|
311
|
+
Net::NTLM::TargetInfo::MSV_AV_DNS_COMPUTER_NAME => :@dns_host_name,
|
|
312
|
+
Net::NTLM::TargetInfo::MSV_AV_DNS_DOMAIN_NAME => :@dns_domain_name,
|
|
313
|
+
Net::NTLM::TargetInfo::MSV_AV_DNS_TREE_NAME => :@dns_tree_name
|
|
314
|
+
}.each do |constant, attribute|
|
|
315
|
+
if target_info.av_pairs[constant]
|
|
316
|
+
value = target_info.av_pairs[constant].dup
|
|
317
|
+
value.force_encoding('UTF-16LE')
|
|
318
|
+
instance_variable_set(attribute, value.encode('UTF-8'))
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Extract the peer/server version number from the NTLM Type 2 (challenge)
|
|
324
|
+
# Version field.
|
|
325
|
+
#
|
|
326
|
+
# @param version [String] the version number as a binary string
|
|
327
|
+
# @return [String] the formated version number (<major>.<minor>.<build>)
|
|
328
|
+
def extract_os_version(version)
|
|
329
|
+
#version.unpack('CCS').join('.')
|
|
330
|
+
begin
|
|
331
|
+
os_version = NTLM::OSVersion.read(version)
|
|
332
|
+
rescue IOError
|
|
333
|
+
return ''
|
|
334
|
+
end
|
|
335
|
+
return "#{os_version.major}.#{os_version.minor}.#{os_version.build}"
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Add the authentication verifier to a Request packet. This includes a
|
|
339
|
+
# sec trailer and the signature of the packet. This also encrypts the
|
|
340
|
+
# Request stub if privacy is required (`:auth_level` option is
|
|
341
|
+
# RPC_C_AUTHN_LEVEL_PKT_PRIVACY).
|
|
342
|
+
#
|
|
343
|
+
# @param dcerpc_req [Request] the Request packet to be updated
|
|
344
|
+
# @param opts [Hash] the authenticaiton options: `:auth_type` and `:auth_level`
|
|
345
|
+
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
|
346
|
+
# @raise [ArgumentError] if `:auth_type` is unknown
|
|
347
|
+
def set_integrity_privacy(dcerpc_req, auth_level:, auth_type:)
|
|
348
|
+
dcerpc_req.sec_trailer = {
|
|
349
|
+
auth_type: auth_type,
|
|
350
|
+
auth_level: auth_level,
|
|
351
|
+
auth_context_id: @ctx_id + @auth_ctx_id_base
|
|
352
|
+
}
|
|
353
|
+
dcerpc_req.auth_value = ' ' * 16
|
|
354
|
+
dcerpc_req.pdu_header.auth_length = 16
|
|
355
|
+
|
|
356
|
+
data_to_sign = plain_stub = dcerpc_req.stub.to_binary_s + dcerpc_req.auth_pad.to_binary_s
|
|
357
|
+
if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
|
|
358
|
+
data_to_sign = dcerpc_req.to_binary_s[0..-(dcerpc_req.pdu_header.auth_length + 1)]
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
encrypted_stub = ''
|
|
362
|
+
if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
|
363
|
+
case auth_type
|
|
364
|
+
when RPC_C_AUTHN_NONE
|
|
365
|
+
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
|
366
|
+
encrypted_stub = @ntlm_client.session.seal_message(plain_stub)
|
|
367
|
+
when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
|
|
368
|
+
# TODO
|
|
369
|
+
raise NotImplementedError
|
|
370
|
+
else
|
|
371
|
+
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
signature = @ntlm_client.session.sign_message(data_to_sign)
|
|
376
|
+
|
|
377
|
+
unless encrypted_stub.empty?
|
|
378
|
+
pad_length = dcerpc_req.sec_trailer.auth_pad_length.to_i
|
|
379
|
+
dcerpc_req.enable_encrypted_stub
|
|
380
|
+
dcerpc_req.stub = encrypted_stub[0..-(pad_length + 1)]
|
|
381
|
+
dcerpc_req.auth_pad = encrypted_stub[-(pad_length)..-1]
|
|
382
|
+
end
|
|
383
|
+
dcerpc_req.auth_value = signature
|
|
384
|
+
dcerpc_req.pdu_header.auth_length = signature.size
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Send a DCERPC request with the provided stub packet.
|
|
388
|
+
#
|
|
389
|
+
# @param stub_packet [BinData::Record] the stub packet to be sent as
|
|
390
|
+
# part of a Request packet
|
|
391
|
+
# @param opts [Hash] the authenticaiton options: `:auth_type` and `:auth_level`
|
|
392
|
+
# @raise [Error::CommunicationError] if socket-related error occurs
|
|
393
|
+
def dcerpc_request(stub_packet, auth_level: nil, auth_type: nil)
|
|
394
|
+
stub_class = stub_packet.class.name.split('::')
|
|
395
|
+
#opts.merge!(endpoint: stub_class[-2])
|
|
396
|
+
values = {
|
|
397
|
+
opnum: stub_packet.opnum,
|
|
398
|
+
p_cont_id: @ctx_id
|
|
399
|
+
}
|
|
400
|
+
dcerpc_req = Request.new(values, { endpoint: stub_class[-2] })
|
|
401
|
+
dcerpc_req.pdu_header.call_id = @call_id
|
|
402
|
+
dcerpc_req.stub.read(stub_packet.to_binary_s)
|
|
403
|
+
# TODO: handle fragmentation
|
|
404
|
+
# We should fragment PDUs if:
|
|
405
|
+
# 1) Payload exceeds max_xmit_frag (@max_buffer_size) received during BIND response
|
|
406
|
+
# 2) We'e explicitly fragmenting packets with lower values
|
|
407
|
+
|
|
408
|
+
if auth_level &&
|
|
409
|
+
[RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(auth_level)
|
|
410
|
+
set_integrity_privacy(dcerpc_req, auth_level: auth_level, auth_type: auth_type)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
send_packet(dcerpc_req)
|
|
414
|
+
|
|
415
|
+
dcerpc_res = recv_struct(Response)
|
|
416
|
+
unless dcerpc_res.pdu_header.pfc_flags.first_frag == 1
|
|
417
|
+
raise Error::InvalidPacket, "Not the first fragment"
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
if auth_level &&
|
|
421
|
+
[RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(auth_level)
|
|
422
|
+
handle_integrity_privacy(dcerpc_res, auth_level: auth_level, auth_type: auth_type)
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
raw_stub = dcerpc_res.stub.to_binary_s
|
|
426
|
+
loop do
|
|
427
|
+
break if dcerpc_res.pdu_header.pfc_flags.last_frag == 1
|
|
428
|
+
dcerpc_res = recv_struct(Response)
|
|
429
|
+
|
|
430
|
+
if auth_level &&
|
|
431
|
+
[RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(auth_level)
|
|
432
|
+
handle_integrity_privacy(dcerpc_res, auth_level: auth_level, auth_type: auth_type)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
raw_stub << dcerpc_res.stub.to_binary_s
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
raw_stub
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Send a packet to the remote host
|
|
442
|
+
#
|
|
443
|
+
# @param packet [BinData::Record] the packet to send
|
|
444
|
+
# @raise [Error::CommunicationError] if socket-related error occurs
|
|
445
|
+
def send_packet(packet)
|
|
446
|
+
data = packet.to_binary_s
|
|
447
|
+
bytes_written = 0
|
|
448
|
+
begin
|
|
449
|
+
loop do
|
|
450
|
+
break unless bytes_written < data.size
|
|
451
|
+
retval = @tcp_socket.write(data[bytes_written..-1])
|
|
452
|
+
bytes_written += retval
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
rescue IOError, Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE => e
|
|
456
|
+
raise Error::CommunicationError, "An error occurred writing to the Socket: #{e.message}"
|
|
457
|
+
end
|
|
458
|
+
nil
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
# Receive a packet from the remote host and parse it according to `struct`
|
|
462
|
+
#
|
|
463
|
+
# @param struct [Class] the structure class to parse the response with
|
|
464
|
+
# @raise [Error::CommunicationError] if socket-related error occurs
|
|
465
|
+
def recv_struct(struct)
|
|
466
|
+
raise Error::CommunicationError, 'Connection has already been closed' if @tcp_socket.closed?
|
|
467
|
+
if IO.select([@tcp_socket], nil, nil, @read_timeout).nil?
|
|
468
|
+
raise Error::CommunicationError, "Read timeout expired when reading from the Socket (timeout=#{@read_timeout})"
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
begin
|
|
472
|
+
response = struct.read(@tcp_socket)
|
|
473
|
+
rescue IOError
|
|
474
|
+
raise Error::InvalidPacket, "Error reading the #{struct} response"
|
|
475
|
+
end
|
|
476
|
+
unless response.pdu_header.ptype == struct::PTYPE
|
|
477
|
+
raise Error::InvalidPacket, "Not a #{struct} packet"
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
response
|
|
481
|
+
rescue Errno::EINVAL, Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE => e
|
|
482
|
+
raise Error::CommunicationError, "An error occurred reading from the Socket: #{e.message}"
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
# Process the security context received in a response. It decrypts the
|
|
486
|
+
# encrypted stub if `:auth_level` is set to anything different than
|
|
487
|
+
# RPC_C_AUTHN_LEVEL_PKT_PRIVACY. It also checks the packet signature and
|
|
488
|
+
# raises an InvalidPacket error if it fails. Note that the exception is
|
|
489
|
+
# disabled by default and can be enabled with the
|
|
490
|
+
# `:raise_signature_error` option
|
|
491
|
+
#
|
|
492
|
+
# @param dcerpc_response [Response] the Response packet
|
|
493
|
+
# containing the security context to process
|
|
494
|
+
# @param opts [Hash] the authenticaiton options: `:auth_type` and
|
|
495
|
+
# `:auth_level`. To enable errors when signature check fails, set the
|
|
496
|
+
# `:raise_signature_error` option to true
|
|
497
|
+
# @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
|
|
498
|
+
# @raise [Error::CommunicationError] if socket-related error occurs
|
|
499
|
+
def handle_integrity_privacy(dcerpc_response, auth_level:, auth_type:, raise_signature_error: false)
|
|
500
|
+
decrypted_stub = ''
|
|
501
|
+
if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
|
502
|
+
encrypted_stub = dcerpc_response.stub.to_binary_s + dcerpc_response.auth_pad.to_binary_s
|
|
503
|
+
case auth_type
|
|
504
|
+
when RPC_C_AUTHN_NONE
|
|
505
|
+
when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
|
|
506
|
+
decrypted_stub = @ntlm_client.session.unseal_message(encrypted_stub)
|
|
507
|
+
when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
|
|
508
|
+
# TODO
|
|
509
|
+
raise NotImplementedError
|
|
510
|
+
else
|
|
511
|
+
raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
unless decrypted_stub.empty?
|
|
516
|
+
pad_length = dcerpc_response.sec_trailer.auth_pad_length.to_i
|
|
517
|
+
dcerpc_response.stub = decrypted_stub[0..-(pad_length + 1)]
|
|
518
|
+
dcerpc_response.auth_pad = decrypted_stub[-(pad_length)..-1]
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
signature = dcerpc_response.auth_value
|
|
522
|
+
data_to_check = dcerpc_response.stub.to_binary_s
|
|
523
|
+
if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
|
|
524
|
+
data_to_check = dcerpc_response.to_binary_s[0..-(dcerpc_response.pdu_header.auth_length + 1)]
|
|
525
|
+
end
|
|
526
|
+
unless @ntlm_client.session.verify_signature(signature, data_to_check)
|
|
527
|
+
if raise_signature_error
|
|
528
|
+
raise Error::InvalidPacket.new(
|
|
529
|
+
"Wrong packet signature received (set `raise_signature_error` to false to ignore)"
|
|
530
|
+
)
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
@call_id += 1
|
|
535
|
+
|
|
536
|
+
nil
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module Dcerpc
|
|
3
|
+
module Drsr
|
|
4
|
+
|
|
5
|
+
# [4.1.3 IDL_DRSBind (Opnum 0)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/605b1ea1-9cdc-428f-ab7a-70120e020a3d)
|
|
6
|
+
class DrsBindRequest < BinData::Record
|
|
7
|
+
attr_reader :opnum
|
|
8
|
+
|
|
9
|
+
endian :little
|
|
10
|
+
|
|
11
|
+
uuid_ptr :puuid_client_dsa, initial_value: NTSAPI_CLIENT_GUID
|
|
12
|
+
drs_extensions_ptr :pext_client
|
|
13
|
+
|
|
14
|
+
def initialize_instance
|
|
15
|
+
super
|
|
16
|
+
@opnum = DRS_BIND
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module Dcerpc
|
|
3
|
+
module Drsr
|
|
4
|
+
|
|
5
|
+
# [4.1.3 IDL_DRSBind (Opnum 0)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/605b1ea1-9cdc-428f-ab7a-70120e020a3d)
|
|
6
|
+
class DrsBindResponse < BinData::Record
|
|
7
|
+
attr_reader :opnum
|
|
8
|
+
|
|
9
|
+
endian :little
|
|
10
|
+
|
|
11
|
+
drs_extensions_ptr :ppext_server
|
|
12
|
+
drs_handle :ph_drs
|
|
13
|
+
ndr_uint32 :error_status
|
|
14
|
+
|
|
15
|
+
def initialize_instance
|
|
16
|
+
super
|
|
17
|
+
@opnum = DRS_BIND
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module RubySMB
|
|
2
|
+
module Dcerpc
|
|
3
|
+
module Drsr
|
|
4
|
+
|
|
5
|
+
class DrsNameArrayPtr < Ndr::NdrConfArray
|
|
6
|
+
default_parameters type: :ndr_wide_stringz_ptr
|
|
7
|
+
extend Ndr::PointerClassPlugin
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
#[4.1.4.1.2 DRS_MSG_CRACKREQ_V1](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/b47debc0-59ee-40e4-ad0f-4bc9f96043b2)
|
|
11
|
+
class DrsMsgCrackreqV1 < Ndr::NdrStruct
|
|
12
|
+
default_parameter byte_align: 4
|
|
13
|
+
endian :little
|
|
14
|
+
|
|
15
|
+
ndr_uint32 :code_page
|
|
16
|
+
ndr_uint32 :locale_id
|
|
17
|
+
ndr_uint32 :dw_flags
|
|
18
|
+
ndr_uint32 :format_offered
|
|
19
|
+
ndr_uint32 :format_desired
|
|
20
|
+
ndr_uint32 :c_names, initial_value: -> { rp_names.size }
|
|
21
|
+
drs_name_array_ptr :rp_names
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# [4.1.4.1.1 DRS_MSG_CRACKREQ](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/f2d5166e-09f6-4788-a391-66471b2f7d6d)
|
|
25
|
+
class DrsMsgCrackreq < Ndr::NdrStruct
|
|
26
|
+
default_parameter byte_align: 4
|
|
27
|
+
endian :little
|
|
28
|
+
|
|
29
|
+
ndr_uint32 :switch_type, initial_value: 1
|
|
30
|
+
choice :msg_crack, selection: :switch_type, byte_align: 4 do
|
|
31
|
+
drs_msg_crackreq_v1 1
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# [4.1.4 IDL_DRSCrackNames (Opnum 12)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/9b4bfb44-6656-4404-bcc8-dc88111658b3)
|
|
36
|
+
class DrsCrackNamesRequest < BinData::Record
|
|
37
|
+
attr_reader :opnum
|
|
38
|
+
|
|
39
|
+
endian :little
|
|
40
|
+
|
|
41
|
+
drs_handle :h_drs
|
|
42
|
+
ndr_uint32 :dw_in_version, initial_value: 1
|
|
43
|
+
drs_msg_crackreq :pmsg_in
|
|
44
|
+
|
|
45
|
+
def initialize_instance
|
|
46
|
+
super
|
|
47
|
+
@opnum = DRS_CRACK_NAMES
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|