mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00

* 'rdp_client_cluster_data' of https://github.com/neslog/zeek: Fixing types. Correcting types. Removing misc data from Client Cluster data trying to assign values. Adding options field to RDP::ClientChannelDef Adding Client Cluster Data Adjustments: - Reformatting - Added comments - Changed the REDIRECTED_SESSIONID_FIELD_VALID field to a bool
403 lines
14 KiB
JavaScript
403 lines
14 KiB
JavaScript
%include ../asn1/asn1.pac
|
|
|
|
type TPKT(is_orig: bool) = record {
|
|
version: uint8;
|
|
reserved: uint8;
|
|
tpkt_len: uint16;
|
|
|
|
# These data structures are merged together into TPKT
|
|
# because there are packets that report incorrect
|
|
# lengths in the tpkt length field. No clue why.
|
|
|
|
cotp: COTP(this);
|
|
} &byteorder=bigendian &length=tpkt_len;
|
|
|
|
type COTP(tpkt: TPKT) = record {
|
|
cotp_len: uint8;
|
|
pdu: uint8;
|
|
switch: case pdu of {
|
|
0xd0 -> connect_confirm: Connect_Confirm(this);
|
|
0xe0 -> client_request: Connect_Request(this);
|
|
0xf0 -> data: DT_Data;
|
|
|
|
# In case we don't support the PDU we just
|
|
# consume the rest of it and throw it away.
|
|
default -> not_done: bytestring &restofdata &transient;
|
|
};
|
|
} &byteorder=littleendian;
|
|
|
|
type DT_Data = record {
|
|
tpdu_number: uint8;
|
|
# multiple octet variant of the ASN.1 type field, should handle this better.
|
|
application_defined_type: uint8;
|
|
application_type: uint8;
|
|
|
|
data: case application_type of {
|
|
0x65 -> client: Client_Header; # 0x65 is a client
|
|
0x66 -> server: Server_Header; # 0x66 is a server
|
|
default -> none: empty;
|
|
};
|
|
} &byteorder=littleendian;
|
|
|
|
######################################################################
|
|
# Data Blocks
|
|
######################################################################
|
|
|
|
type Data_Header = record {
|
|
type: uint16;
|
|
length: uint16;
|
|
} &byteorder=littleendian;
|
|
|
|
type Data_Block = record {
|
|
header: Data_Header;
|
|
block: case header.type of {
|
|
0xc001 -> client_core: Client_Core_Data;
|
|
0xc002 -> client_security: Client_Security_Data;
|
|
0xc003 -> client_network: Client_Network_Data;
|
|
0xc004 -> client_cluster: Client_Cluster_Data;
|
|
#0xc005 -> client_monitor: Client_Monitor_Data;
|
|
#0xc006 -> client_msgchannel: Client_MsgChannel_Data;
|
|
#0xc008 -> client_monitor_ex: Client_MonitorExtended_Data;
|
|
#0xc00A -> client_multitrans: Client_MultiTransport_Data;
|
|
|
|
0x0c01 -> server_core: Server_Core_Data(header);
|
|
0x0c02 -> server_security: Server_Security_Data;
|
|
0x0c03 -> server_network: Server_Network_Data;
|
|
#0x0c04 -> server_msgchannel: Server_MsgChannel_Data;
|
|
#0x0c08 -> server_multitrans: Server_MultiTransport_Data;
|
|
|
|
default -> unhandled: bytestring &restofdata &transient;
|
|
} &length=header.length-4;
|
|
} &byteorder=littleendian;
|
|
|
|
######################################################################
|
|
# Client X.224
|
|
######################################################################
|
|
|
|
type Connect_Request(cotp: COTP) = record {
|
|
destination_reference: uint16;
|
|
source_reference: uint16;
|
|
flow_control: uint8;
|
|
cookie_mstshash: RE/Cookie: mstshash\=/;
|
|
cookie_value: RE/[^\x0d]*/;
|
|
cookie_terminator: RE/\x0d\x0a/;
|
|
# Terrifying little case statement to figure out if there
|
|
# is any data left in the COTP structure.
|
|
switch1: case (offsetof(switch1) + 2 - cotp.cotp_len - 1) of {
|
|
0 -> none: empty;
|
|
default -> rdp_neg_req: RDP_Negotiation_Request;
|
|
};
|
|
} &byteorder=littleendian;
|
|
|
|
type RDP_Negotiation_Request = record {
|
|
type: uint8;
|
|
flags: uint8;
|
|
length: uint16; # must be set to 8
|
|
requested_protocols: uint32;
|
|
} &let {
|
|
PROTOCOL_RDP: bool = requested_protocols & 0x00;
|
|
PROTOCOL_SSL: bool = requested_protocols & 0x01;
|
|
PROTOCOL_HYBRID: bool = requested_protocols & 0x02;
|
|
PROTOCOL_HYBRID_EX: bool = requested_protocols & 0x08;
|
|
} &byteorder=littleendian;
|
|
|
|
######################################################################
|
|
# Server X.224
|
|
######################################################################
|
|
|
|
type Connect_Confirm(cotp: COTP) = record {
|
|
destination_reference: uint16;
|
|
source_reference: uint16;
|
|
flags: uint8;
|
|
# Terrifying little case statement to figure out if there
|
|
# is any data left in the COTP structure.
|
|
switch1: case (offsetof(switch1) + 2 - cotp.cotp_len - 1) of {
|
|
0 -> none1: empty;
|
|
default -> response: Connect_Confirm_Record;
|
|
};
|
|
};
|
|
|
|
type Connect_Confirm_Record = record {
|
|
response_type: uint8;
|
|
switch1: case response_type of {
|
|
0x02 -> neg_resp: RDP_Negotiation_Response;
|
|
0x03 -> neg_fail: RDP_Negotiation_Failure;
|
|
};
|
|
};
|
|
|
|
type RDP_Negotiation_Response = record {
|
|
flags: uint8;
|
|
length: uint16; # must be set to 8
|
|
selected_protocol: uint32;
|
|
} &let {
|
|
# Seems to be SSL encrypted (maybe CredSSP also?)
|
|
# after this message if the selected_protocol is > 0.
|
|
enc_ssl: bool = $context.connection.go_encrypted(selected_protocol) &if(selected_protocol > 0);
|
|
} &byteorder=littleendian;
|
|
|
|
type RDP_Negotiation_Failure = record {
|
|
flags: uint8;
|
|
length: uint16;
|
|
failure_code: uint32;
|
|
} &byteorder=littleendian;
|
|
|
|
######################################################################
|
|
# Client MCS
|
|
######################################################################
|
|
|
|
type Client_Header = record {
|
|
type_length: ASN1Integer;
|
|
calling_domain_selector: ASN1OctetString;
|
|
called_domain_selector: ASN1OctetString;
|
|
upward_flag: ASN1Boolean;
|
|
target_parameters: ASN1SequenceMeta;
|
|
targ_parameters_pad: bytestring &length=target_parameters.encoding.length &transient;
|
|
minimum_parameters: ASN1SequenceMeta;
|
|
min_parameters_pad: bytestring &length=minimum_parameters.encoding.length &transient;
|
|
maximum_parameters: ASN1SequenceMeta;
|
|
max_parameters_pad: bytestring &length=maximum_parameters.encoding.length &transient;
|
|
# BER encoded OctetString and long variant, can be safely skipped for now
|
|
user_data_length: uint32;
|
|
gcc_connection_data: GCC_Client_Connection_Data;
|
|
gcc_client_create_request: GCC_Client_Create_Request;
|
|
data_blocks: Data_Block[] &until($input.length() == 0);
|
|
};
|
|
|
|
type GCC_Client_Connection_Data = record {
|
|
key_object_length: uint16;
|
|
key_object: uint8[key_object_length];
|
|
connect_data_connect_pdu: uint16;
|
|
} &byteorder=bigendian;
|
|
|
|
type GCC_Client_Create_Request = record {
|
|
extension_bit: uint8;
|
|
privileges: uint8;
|
|
numeric_length: uint8;
|
|
numeric: uint8;
|
|
termination_method: uint8;
|
|
number_user_data_sets: uint8;
|
|
user_data_value_present: uint8;
|
|
h221_nonstandard_length: uint8;
|
|
h221_nonstandard_key: RE/Duca/;
|
|
user_data_value_length: uint16;
|
|
} &byteorder=bigendian;
|
|
|
|
type Client_Core_Data = record {
|
|
version_major: uint16;
|
|
version_minor: uint16;
|
|
desktop_width: uint16;
|
|
desktop_height: uint16;
|
|
color_depth: uint16;
|
|
sas_sequence: uint16;
|
|
keyboard_layout: uint32;
|
|
client_build: uint32;
|
|
client_name: bytestring &length=32;
|
|
keyboard_type: uint32;
|
|
keyboard_sub: uint32;
|
|
keyboard_function_key: uint32;
|
|
ime_file_name: bytestring &length=64;
|
|
# Everything below here is optional and should be handled better.
|
|
# If some of these fields aren't included it could lead to parse failure.
|
|
post_beta2_color_depth: uint16;
|
|
client_product_id: uint16;
|
|
serial_number: uint32;
|
|
high_color_depth: uint16;
|
|
supported_color_depths: uint16;
|
|
early_capability_flags: uint16;
|
|
dig_product_id: bytestring &length=64;
|
|
# There are more optional fields here but they are
|
|
# annoying to optionally parse in binpac.
|
|
# Documented here: https://msdn.microsoft.com/en-us/library/cc240510.aspx
|
|
} &let {
|
|
SUPPORT_ERRINFO_PDU: bool = early_capability_flags & 0x01;
|
|
WANT_32BPP_SESSION: bool = early_capability_flags & 0x02;
|
|
SUPPORT_STATUSINFO_PDU: bool = early_capability_flags & 0x04;
|
|
STRONG_ASYMMETRIC_KEYS: bool = early_capability_flags & 0x08;
|
|
SUPPORT_MONITOR_LAYOUT_PDU: bool = early_capability_flags & 0x40;
|
|
SUPPORT_NETCHAR_AUTODETECT: bool = early_capability_flags & 0x80;
|
|
SUPPORT_DYNVC_GFX_PROTOCOL: bool = early_capability_flags & 0x0100;
|
|
SUPPORT_DYNAMIC_TIME_ZONE: bool = early_capability_flags & 0x0200;
|
|
SUPPORT_HEARTBEAT_PDU: bool = early_capability_flags & 0x0400;
|
|
} &byteorder=littleendian;
|
|
|
|
type Client_Security_Data = record {
|
|
encryption_methods: uint32;
|
|
ext_encryption_methods: uint32;
|
|
} &byteorder=littleendian;
|
|
|
|
type Client_Network_Data = record {
|
|
channel_count: uint32;
|
|
channel_def_array: Client_Channel_Def[channel_count];
|
|
} &byteorder=littleendian;
|
|
|
|
type Client_Cluster_Data = record {
|
|
flags: uint32;
|
|
redir_session_id: uint32;
|
|
} &let {
|
|
REDIRECTION_SUPPORTED: bool = redir_session_id & 0x00000001;
|
|
SERVER_SESSION_REDIRECTION_VERSION_MASK: uint8 = (redir_session_id & 0x0000003C);
|
|
REDIRECTED_SESSIONID_FIELD_VALID: bool = (redir_session_id & 0x00000002);
|
|
REDIRECTED_SMARTCARD: bool = redir_session_id & 0x00000040;
|
|
} &byteorder=littleendian;
|
|
|
|
type Client_Channel_Def = record {
|
|
name: bytestring &length=8;
|
|
options: uint32;
|
|
} &let {
|
|
REMOTE_CONTROL_PERSISTENT: bool = options & 0x00100000;
|
|
CHANNEL_OPTION_SHOW_PROTOCOL: bool = options & 0x00200000;
|
|
CHANNEL_OPTION_COMPRESS: bool = options & 0x00400000;
|
|
CHANNEL_OPTION_COMPRESS_RDP: bool = options & 0x00800000;
|
|
CHANNEL_OPTION_PRI_LOW: bool = options & 0x02000000;
|
|
CHANNEL_OPTION_PRI_MED: bool = options & 0x04000000;
|
|
CHANNEL_OPTION_PRI_HIGH: bool = options & 0x08000000;
|
|
CHANNEL_OPTION_ENCRYPT_CS: bool = options & 0x10000000;
|
|
CHANNEL_OPTION_ENCRYPT_SC: bool = options & 0x20000000;
|
|
CHANNEL_OPTION_ENCRYPT_RDP: bool = options & 0x40000000;
|
|
CHANNEL_OPTION_INITIALIZED: bool = options & 0x80000000;
|
|
} &byteorder=littleendian;
|
|
|
|
######################################################################
|
|
# Server MCS
|
|
######################################################################
|
|
|
|
type Server_Header = record {
|
|
# We don't need this value, but it's ASN.1 integer in definite length
|
|
# so I think we can skip over it.
|
|
type_length: uint8[3];
|
|
connect_response_result: ASN1Enumerated;
|
|
connect_response_called_id: ASN1Integer;
|
|
connect_response_domain_parameters: ASN1SequenceMeta;
|
|
# Skipping over domain parameters for now.
|
|
domain_parameters: bytestring &length=connect_response_domain_parameters.encoding.length &transient;
|
|
# I think this is another definite length encoded value.
|
|
user_data_length: uint32;
|
|
gcc_connection_data: GCC_Server_Connection_Data;
|
|
gcc_create_response: GCC_Server_Create_Response;
|
|
data_blocks: Data_Block[] &until($input.length() == 0);
|
|
} &byteorder=littleendian;
|
|
|
|
type GCC_Server_Connection_Data = record {
|
|
key_object_length: uint16;
|
|
key_object: uint8[key_object_length];
|
|
connect_data_connect_pdu: uint8;
|
|
} &byteorder=bigendian;
|
|
|
|
type GCC_Server_Create_Response = record {
|
|
extension_bit: uint8;
|
|
node_id: uint16;
|
|
tag_length: uint8;
|
|
tag: uint8;
|
|
result: uint8;
|
|
number_user_data_sets: uint8;
|
|
user_data_value_present: uint8;
|
|
h221_nonstandard_length: uint8;
|
|
h221_nonstandard_key: RE/McDn/;
|
|
user_data_value_length: uint16;
|
|
} &byteorder=bigendian;
|
|
|
|
type Server_Core_Data(h: Data_Header) = record {
|
|
version_major: uint16;
|
|
version_minor: uint16;
|
|
switch1: case h.length of {
|
|
8 -> none: empty;
|
|
default -> client_requested_protocols: uint32;
|
|
};
|
|
} &byteorder=littleendian;
|
|
|
|
type Server_Network_Data = record {
|
|
mcs_channel_id: uint16;
|
|
channel_count: uint16;
|
|
} &byteorder=littleendian;
|
|
|
|
type Server_Security_Data = record {
|
|
encryption_method: uint32;
|
|
encryption_level: uint32;
|
|
server_random_length: uint32;
|
|
server_cert_length: uint32;
|
|
server_random: bytestring &length=server_random_length;
|
|
server_certificate: Server_Certificate &length=server_cert_length;
|
|
} &let {
|
|
# Seems to be encrypted after this message if
|
|
# encryption level is >0
|
|
# 0 means RDP encryption.
|
|
enc: bool = $context.connection.go_encrypted(0) &if(encryption_method > 0 && encryption_level > 0);
|
|
} &byteorder=littleendian;
|
|
|
|
type Server_Certificate = record {
|
|
version: uint32;
|
|
switch: case cert_type of {
|
|
0x01 -> proprietary: Server_Proprietary_Cert(this);
|
|
0x02 -> x509: X509;
|
|
};
|
|
} &let {
|
|
cert_type: uint32 = version & 0x7FFFFFFF;
|
|
permanently_issued: bool = (version & 0x80000000) == 0;
|
|
} &byteorder=littleendian;
|
|
|
|
type Server_Proprietary_Cert(cert: Server_Certificate) = record {
|
|
signature_algorithm: uint32;
|
|
key_algorithm: uint32;
|
|
public_key_blob_type: uint16;
|
|
public_key_blob_length: uint16;
|
|
public_key_blob: Public_Key_Blob &length=public_key_blob_length;
|
|
signature_blob_type: uint16;
|
|
signature_blob_length: uint16;
|
|
signature_blob: bytestring &length=signature_blob_length;
|
|
} &byteorder=littleendian;
|
|
|
|
type Public_Key_Blob = record {
|
|
magic: bytestring &length=4;
|
|
key_length: uint32;
|
|
bit_length: uint32;
|
|
public_exponent: uint32;
|
|
modulus: bytestring &length=key_length;
|
|
} &byteorder=littleendian;
|
|
|
|
type X509 = record {
|
|
num_of_certs: uint32;
|
|
certs: X509_Cert_Data[num_of_certs];
|
|
} &byteorder=littleendian;
|
|
|
|
type X509_Cert_Data = record {
|
|
cert_len: uint32;
|
|
cert: bytestring &length=cert_len;
|
|
} &byteorder=littleendian;
|
|
|
|
refine connection RDP_Conn += {
|
|
|
|
%member{
|
|
bool is_encrypted_;
|
|
uint32 encryption_method_;
|
|
%}
|
|
|
|
%init{
|
|
is_encrypted_ = false;
|
|
encryption_method_ = 0;
|
|
%}
|
|
|
|
function go_encrypted(method: uint32): bool
|
|
%{
|
|
is_encrypted_ = true;
|
|
encryption_method_ = method;
|
|
|
|
if ( rdp_begin_encryption )
|
|
{
|
|
BifEvent::generate_rdp_begin_encryption(bro_analyzer(),
|
|
bro_analyzer()->Conn(),
|
|
${method});
|
|
}
|
|
|
|
return is_encrypted_;
|
|
%}
|
|
|
|
function is_encrypted(): bool
|
|
%{
|
|
return is_encrypted_;
|
|
%}
|
|
|
|
function encryption_method(): uint32
|
|
%{
|
|
return encryption_method_;
|
|
%}
|
|
};
|