zeek/src/analyzer/protocol/rdp/rdp-protocol.pac
Jon Siwek d72f5458f1 Merge branch 'rdp_client_cluster_data' of https://github.com/neslog/zeek
* '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
2019-06-20 20:41:16 -07:00

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_;
%}
};