Huge updates to the RDP analyzer from Josh Liburdi.

- More data pulled into scriptland.
  - Logs expanded with client screen resolution and desired color depth.
  - Values in UTF-16 on the wire are converted to UTF-8 before being
    sent to scriptland.
  - If the RDP turns into SSL records, we now pass data that appears
    to be SSL to the PIA analyzer.
  - If RDP uses native encryption with X.509 certs we pass those
    certs to the files framework and the base scripts pass them forward
    to the X.509 analyzer.
  - Lots of cleanup and adjustment to fit the documented protocol
    a bit better.
  - Cleaned up the DPD signatures.
  - Moved to flowunit instead of datagram.
  - Added tests.
This commit is contained in:
Seth Hall 2015-03-04 13:12:03 -05:00
parent a63d7307c8
commit bbedb73a45
26 changed files with 1535 additions and 346 deletions

View file

@ -5,5 +5,6 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DI
bro_plugin_begin(Bro RDP)
bro_plugin_cc(RDP.cc Plugin.cc)
bro_plugin_bif(events.bif)
bro_plugin_bif(types.bif)
bro_plugin_pac(rdp.pac rdp-analyzer.pac rdp-protocol.pac)
bro_plugin_end()

View file

@ -2,17 +2,17 @@
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
#include "Reporter.h"
#include "events.bif.h"
#include "types.bif.h"
using namespace analyzer::rdp;
RDP_Analyzer::RDP_Analyzer(Connection* c)
: tcp::TCP_ApplicationAnalyzer("RDP", c)
: tcp::TCP_ApplicationAnalyzer("RDP", c)
{
interp = new binpac::RDP::RDP_Conn(this);
had_gap = false;
pia = 0;
}
RDP_Analyzer::~RDP_Analyzer()
@ -22,12 +22,10 @@ RDP_Analyzer::~RDP_Analyzer()
void RDP_Analyzer::Done()
{
tcp::TCP_ApplicationAnalyzer::Done();
interp->FlowEOF(true);
interp->FlowEOF(false);
}
void RDP_Analyzer::EndpointEOF(bool is_orig)
@ -49,13 +47,47 @@ void RDP_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
// deliver data to the other side if the script layer can handle this.
return;
try
// If the data appears (very loosely) to be SSL/TLS
// we'll just move this over to the PIA analyzer.
// Like the comment below says, this is probably the wrong
// way to handle this.
if ( len > 0 && data[0] >= 0x14 && data[0] <= 0x17 )
{
interp->NewData(orig, data, data + len);
if ( ! pia )
{
pia = new pia::PIA_TCP(Conn());
if ( AddChildAnalyzer(pia) )
{
pia->FirstPacket(true, 0);
pia->FirstPacket(false, 0);
}
}
if ( pia )
{
ForwardStream(len, data, orig);
}
}
catch ( const binpac::Exception& e )
else if ( pia )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
// This is data that doesn't seem to match
// an SSL record, but we've moved into SSL mode.
// This is probably the wrong way to handle this
// situation but I don't know what these records
// are that don't appear to be SSL/TLS.
return;
}
else
{
try
{
interp->NewData(orig, data, data + len);
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
}
}
}

View file

@ -5,6 +5,7 @@
#include "analyzer/protocol/tcp/TCP.h"
#include "analyzer/protocol/pia/PIA.h"
#include "rdp_pac.h"
@ -21,10 +22,7 @@ public:
virtual void DeliverStream(int len, const u_char* data, bool orig);
virtual void Undelivered(uint64 seq, int len, bool orig);
// Overriden from tcp::TCP_ApplicationAnalyzer.
virtual void EndpointEOF(bool is_orig);
static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn)
{ return new RDP_Analyzer(conn); }
@ -40,7 +38,7 @@ protected:
binpac::RDP::RDP_Conn* interp;
bool had_gap;
pia::PIA_TCP *pia;
};
} } // namespace analyzer::*

View file

@ -9,14 +9,8 @@ event rdp_client_request%(c: connection, cookie: string%);
##
## c: The connection record for the underlying transport-layer session/flow.
##
## keyboard_layout: The 16-bit integer representing the keyboard layout/language of the client machine.
##
## build: The 16-bit integer representing the version of the RDP client.
##
## hostname: The hostname of the client machine (optional).
##
## product_id: The product ID of the client machine (optional).
event rdp_client_data%(c: connection, keyboard_layout: count, build: count, hostname: string, product_id: string%);
## data: The data contained in the client core data structure.
event rdp_client_core_data%(c: connection, data: RDP::ClientCoreData%);
## Generated for MCS server responses when native RDP encryption is used.
##

View file

@ -1,63 +1,147 @@
%extern{
#include "ConvertUTF.h"
#include "file_analysis/Manager.h"
#include "types.bif.h"
%}
refine flow RDP_Flow += {
function proc_rdp_client_request(client_request: Client_Request): bool
%{
connection()->bro_analyzer()->ProtocolConfirmation();
BifEvent::generate_rdp_client_request(connection()->bro_analyzer(),
connection()->bro_analyzer()->Conn(),
bytestring_to_val(${client_request.cookie_value}));
function utf16_to_utf8_val(utf16: bytestring): StringVal
%{
size_t utf8size = 3 * utf16.length() + 1;
char* utf8stringnative = new char[utf8size];
const UTF16* sourcestart = reinterpret_cast<const UTF16*>(utf16.begin());
const UTF16* sourceend = sourcestart + utf16.length();
UTF8* targetstart = reinterpret_cast<UTF8*>(utf8stringnative);
UTF8* targetend = targetstart + utf8size;
return true;
%}
ConversionResult res = ConvertUTF16toUTF8(&sourcestart,
sourceend,
&targetstart,
targetend,
strictConversion);
*targetstart = 0;
function proc_rdp_result(gcc_response: GCC_Server_Create_Response): bool
%{
connection()->bro_analyzer()->ProtocolConfirmation();
BifEvent::generate_rdp_result(connection()->bro_analyzer(),
connection()->bro_analyzer()->Conn(),
${gcc_response.result});
if ( res != conversionOK )
{
connection()->bro_analyzer()->Weird("Failed UTF-16 to UTF-8 conversion");
return new StringVal(utf16.length(), (const char *) utf16.begin());
}
// We're relying on no nulls being in the string.
return new StringVal(utf8stringnative);
%}
function proc_rdp_client_request(client_request: Client_Request): bool
%{
connection()->bro_analyzer()->ProtocolConfirmation();
BifEvent::generate_rdp_client_request(connection()->bro_analyzer(),
connection()->bro_analyzer()->Conn(),
bytestring_to_val(${client_request.cookie_value}));
return true;
%}
function proc_rdp_result(gcc_response: GCC_Server_Create_Response): bool
%{
connection()->bro_analyzer()->ProtocolConfirmation();
BifEvent::generate_rdp_result(connection()->bro_analyzer(),
connection()->bro_analyzer()->Conn(),
${gcc_response.result});
return true;
%}
function proc_rdp_client_data(ccore: Client_Core_Data): bool
%{
connection()->bro_analyzer()->ProtocolConfirmation();
BifEvent::generate_rdp_client_data(connection()->bro_analyzer(),
connection()->bro_analyzer()->Conn(),
${ccore.keyboard_layout},
${ccore.client_build},
bytestring_to_val(${ccore.client_name}),
bytestring_to_val(${ccore.dig_product_id}));
function proc_rdp_client_core_data(ccore: Client_Core_Data): bool
%{
connection()->bro_analyzer()->ProtocolConfirmation();
return true;
%}
RecordVal* ec_flags = new RecordVal(BifType::Record::RDP::EarlyCapabilityFlags);
ec_flags->Assign(0, new Val(${ccore.SUPPORT_ERRINFO_PDU}, TYPE_BOOL));
ec_flags->Assign(1, new Val(${ccore.WANT_32BPP_SESSION}, TYPE_BOOL));
ec_flags->Assign(2, new Val(${ccore.SUPPORT_STATUSINFO_PDU}, TYPE_BOOL));
ec_flags->Assign(3, new Val(${ccore.STRONG_ASYMMETRIC_KEYS}, TYPE_BOOL));
ec_flags->Assign(4, new Val(${ccore.SUPPORT_MONITOR_LAYOUT_PDU}, TYPE_BOOL));
ec_flags->Assign(5, new Val(${ccore.SUPPORT_NETCHAR_AUTODETECT}, TYPE_BOOL));
ec_flags->Assign(6, new Val(${ccore.SUPPORT_DYNVC_GFX_PROTOCOL}, TYPE_BOOL));
ec_flags->Assign(7, new Val(${ccore.SUPPORT_DYNAMIC_TIME_ZONE}, TYPE_BOOL));
ec_flags->Assign(8, new Val(${ccore.SUPPORT_HEARTBEAT_PDU}, TYPE_BOOL));
function proc_rdp_server_security(ssd: Server_Security_Data): bool
%{
connection()->bro_analyzer()->ProtocolConfirmation();
BifEvent::generate_rdp_server_security(connection()->bro_analyzer(),
connection()->bro_analyzer()->Conn(),
${ssd.encryption_method},
${ssd.encryption_level});
RecordVal* ccd = new RecordVal(BifType::Record::RDP::ClientCoreData);
ccd->Assign(0, new Val(${ccore.version_major}, TYPE_COUNT));
ccd->Assign(1, new Val(${ccore.version_minor}, TYPE_COUNT));
ccd->Assign(2, new Val(${ccore.desktop_width}, TYPE_COUNT));
ccd->Assign(3, new Val(${ccore.desktop_height}, TYPE_COUNT));
ccd->Assign(4, new Val(${ccore.color_depth}, TYPE_COUNT));
ccd->Assign(5, new Val(${ccore.sas_sequence}, TYPE_COUNT));
ccd->Assign(6, new Val(${ccore.keyboard_layout}, TYPE_COUNT));
ccd->Assign(7, new Val(${ccore.client_build}, TYPE_COUNT));
ccd->Assign(8, utf16_to_utf8_val(${ccore.client_name}));
ccd->Assign(9, new Val(${ccore.keyboard_type}, TYPE_COUNT));
ccd->Assign(10, new Val(${ccore.keyboard_sub}, TYPE_COUNT));
ccd->Assign(11, new Val(${ccore.keyboard_function_key}, TYPE_COUNT));
ccd->Assign(12, utf16_to_utf8_val(${ccore.ime_file_name}));
ccd->Assign(13, new Val(${ccore.post_beta2_color_depth}, TYPE_COUNT));
ccd->Assign(14, new Val(${ccore.client_product_id}, TYPE_COUNT));
ccd->Assign(15, new Val(${ccore.serial_number}, TYPE_COUNT));
ccd->Assign(16, new Val(${ccore.high_color_depth}, TYPE_COUNT));
ccd->Assign(17, new Val(${ccore.supported_color_depths}, TYPE_COUNT));
ccd->Assign(18, ec_flags);
ccd->Assign(19, utf16_to_utf8_val(${ccore.dig_product_id}));
return true;
%}
BifEvent::generate_rdp_client_core_data(connection()->bro_analyzer(),
connection()->bro_analyzer()->Conn(),
ccd);
return true;
%}
function proc_rdp_server_security(ssd: Server_Security_Data): bool
%{
connection()->bro_analyzer()->ProtocolConfirmation();
BifEvent::generate_rdp_server_security(connection()->bro_analyzer(),
connection()->bro_analyzer()->Conn(),
${ssd.encryption_method},
${ssd.encryption_level});
return true;
%}
function proc_x509_cert(x509: X509): bool
%{
const bytestring& cert = ${x509.cert};
ODesc file_handle;
file_handle.AddRaw("Analyzer::ANALYZER_RDP");
file_handle.Add(connection()->bro_analyzer()->Conn()->StartTime());
connection()->bro_analyzer()->Conn()->IDString(&file_handle);
string file_id = file_mgr->HashHandle(file_handle.Description());
file_mgr->DataIn(reinterpret_cast<const u_char*>(cert.data()),
cert.length(),
connection()->bro_analyzer()->GetAnalyzerTag(),
connection()->bro_analyzer()->Conn(),
false, // It seems there are only server certs?
file_id);
file_mgr->EndOfFile(file_id);
return true;
%}
};
refine typeattr Client_Request += &let {
proc: bool = $context.flow.proc_rdp_client_request(this);
proc: bool = $context.flow.proc_rdp_client_request(this);
};
refine typeattr Client_Core_Data += &let {
proc: bool = $context.flow.proc_rdp_client_data(this);
proc: bool = $context.flow.proc_rdp_client_core_data(this);
};
refine typeattr GCC_Server_Create_Response += &let {
proc: bool = $context.flow.proc_rdp_result(this);
proc: bool = $context.flow.proc_rdp_result(this);
};
refine typeattr Server_Security_Data += &let {
proc: bool = $context.flow.proc_rdp_server_security(this);
proc: bool = $context.flow.proc_rdp_server_security(this);
};
refine typeattr X509 += &let {
proc: bool = $context.flow.proc_x509_cert(this);
};

View file

@ -1,45 +1,74 @@
type RDP_PDU(is_orig: bool) = record {
type: uint8;
switch: case type of {
0x16 -> ssl_encryption: bytestring &restofdata &transient; # send to SSL analyzer in the future
default -> native_encryption: Native_Encryption; # TPKT version
};
} &byteorder=bigendian;
######################################################################
# Native Encryption
######################################################################
type TPKT(is_orig: bool) = record {
version: uint8;
reserved: uint8;
tpkt_len: uint16;
type Native_Encryption = record {
tpkt_reserved: uint8;
tpkt_length: uint16;
cotp: COTP;
};
# 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;
} &byteorder=bigendian &length=tpkt_len;
type COTP = record {
length: uint8;
pdu: uint8;
switch: case pdu of {
0xe0 -> cRequest: Client_Request;
0xf0 -> hdr: COTP_Header;
default -> data: bytestring &restofdata &transient;
};
cotp_len: uint8;
pdu: uint8;
# Probably should do something with this eventually.
#cotp_crap: padding[cotp_len-2];
switch: case pdu of {
#0xd0 -> cConfirm: Connect_Confirm;
0xe0 -> c_request: Client_Request;
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 COTP_Header = record {
tpdu_number: uint8;
application_defined_type: uint8; # this begins a BER encoded multiple octet variant, but can be safely skipped
application_type: uint8; # this is value for the BER encoded octet variant above
switch: case application_type of { # this seems to cause a binpac exception error
0x65 -> cHeader: Client_Header; # 0x65 is a client
0x66 -> sHeader: Server_Header; # 0x66 is a server
default -> data: bytestring &restofdata;
};
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;
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;
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;
######################################################################
@ -47,11 +76,11 @@ type Data_Header = record {
######################################################################
type Client_Request = record {
destination_reference: uint16;
source_reference: uint16;
flow_control: uint8;
cookie_mstshash: RE/Cookie: mstshash\=/; # &check would be better here, but it is not implemented
cookie_value: RE/[^\x0d]*/; # the value is anything up to \x0d
destination_reference: uint16;
source_reference: uint16;
flow_control: uint8;
cookie_mstshash: RE/Cookie: mstshash\=/;
cookie_value: RE/[^\x0d]*/;
};
######################################################################
@ -59,158 +88,173 @@ type Client_Request = record {
######################################################################
type Client_Header = record {
type_length: uint8[3]; # BER encoded long variant, can be safely skipped for now
calling_domain_selector: ASN1OctetString;
called_domain_selector: ASN1OctetString;
upward_flag: ASN1Boolean;
target_parameters: ASN1SequenceMeta;
targ_parameters_pad: padding[target_parameters.encoding.length];
minimum_parameters: ASN1SequenceMeta;
min_parameters_pad: padding[minimum_parameters.encoding.length];
maximum_parameters: ASN1SequenceMeta;
max_parameters_pad: padding[maximum_parameters.encoding.length];
user_data_length: uint32; # BER encoded OctetString and long variant, can be safely skipped for now
gcc_connection_data: GCC_Client_Connection_Data;
gcc_client_create_request: GCC_Client_Create_Request;
core_header: Data_Header;
core_data: Client_Core_Data;
remainder: bytestring &restofdata &transient; # everything after core_data can be discarded
type_length: ASN1Integer;
calling_domain_selector: ASN1OctetString;
called_domain_selector: ASN1OctetString;
upward_flag: ASN1Boolean;
target_parameters: ASN1SequenceMeta;
targ_parameters_pad: padding[target_parameters.encoding.length];
minimum_parameters: ASN1SequenceMeta;
min_parameters_pad: padding[minimum_parameters.encoding.length];
maximum_parameters: ASN1SequenceMeta;
max_parameters_pad: padding[maximum_parameters.encoding.length];
# 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;
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/; # &check would be better here, but it is not implemented
user_data_value_length: uint16;
};
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;
post_beta_color_depth: uint16;
product_id: uint16;
serial_number: uint32;
high_color_depth: uint16;
supported_color_depth: uint16;
early_capability_flags: uint16;
dig_product_id: bytestring &length=64;
};
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;
######################################################################
# Server MCS
######################################################################
type Server_Header = record {
type_length: uint8[3]; # BER encoded long variant, can be safely skipped for now
connect_response_result: ASN1Enumerated;
connect_response_called_id: ASN1Integer;
connect_response_domain_parameters: ASN1SequenceMeta;
domain_parameters_pad: padding[connect_response_domain_parameters.encoding.length]; # skip this data
user_data_length: uint32; # BER encoded OctetString and long variant, can be safely skipped for now
gcc_connection_data: GCC_Server_Connection_Data;
gcc_create_response: GCC_Server_Create_Response;
core_header: Data_Header;
core_data: padding[core_header.length - 4]; # skip this data
network_header: Data_Header;
net_data: padding[network_header.length - 4]; # skip this data
security_header: Data_Header;
security_data: Server_Security_Data;
};
# 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: padding[connect_response_domain_parameters.encoding.length];
# 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;
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: uint8[2];
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/; # &check would be better here, but it is not implemented
user_data_value_length: uint16;
};
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 = record {
version_major: uint16;
version_minor: uint16;
client_requested_protocols: uint32;
};
version_major: uint16;
version_minor: uint16;
client_requested_protocols: uint32;
} &byteorder=littleendian;
type Server_Network_Data = record {
mcs_channel_id: uint16;
channel_count: uint16;
};
mcs_channel_id: uint16;
channel_count: uint16;
} &byteorder=littleendian;
type Server_Security_Data = record {
encryption_method: uint32;
encryption_level: uint32;
server_random_length: uint32 &byteorder=littleendian;
server_cert_length: uint32 &byteorder=littleendian;
server_random: bytestring &length=server_random_length;
server_certificate: Server_Certificate;
};
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;
} &byteorder=littleendian;
type Server_Certificate = record {
cert_type: uint8;
switch: case cert_type of {
0x01 -> proprietary: Server_Proprietary;
0x02 -> ssl: SSL;
};
version: uint32;
switch: case cert_type of {
0x01 -> proprietary: Server_Proprietary;
0x02 -> x509: X509;
};
} &let {
cert_type: uint32 = version & 0x7FFFFFFF;
permanent_issue: bool = (version & 0x80000000) == 0;
} &byteorder=littleendian;
type Server_Proprietary = record {
cert_type: uint8[3]; # remainder of cert_type value
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;
};
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;
};
magic: bytestring &length=4;
key_length: uint32;
bit_length: uint32;
public_exponent: uint32;
modulus: bytestring &length=key_length;
} &byteorder=littleendian;
type SSL = record {
pad1: padding[11];
x509_cert: bytestring &restofdata &transient; # send to x509 analyzer
};
type X509 = record {
pad1: padding[8];
cert: bytestring &restofdata;
} &byteorder=littleendian;
######################################################################
# ASN.1 Encodings
@ -226,7 +270,7 @@ type ASN1EncodingMeta = record {
len: uint8;
more_len: bytestring &length = long_len ? len & 0x7f : 0;
} &let {
long_len: bool = len & 0x80;
long_len: bool = (len & 0x80) > 0;
length: uint64 = long_len ? binary_to_int64(more_len) : len & 0x7f;
};
@ -251,7 +295,7 @@ type ASN1Boolean = record {
};
type ASN1Enumerated = record {
encoding: ASN1Encoding;
encoding: ASN1Encoding;
};
######################################################################
@ -261,7 +305,6 @@ type ASN1Enumerated = record {
function binary_to_int64(bs: bytestring): int64
%{
int64 rval = 0;
for ( int i = 0; i < bs.length(); ++i )
{
uint64 byte = bs[i];

View file

@ -19,8 +19,7 @@ connection RDP_Conn(bro_analyzer: BroAnalyzer) {
%include rdp-protocol.pac
flow RDP_Flow(is_orig: bool) {
#flowunit = RDP_PDU(is_orig) withcontext(connection, this);
datagram = RDP_PDU(is_orig) withcontext(connection, this);
flowunit = TPKT(is_orig) withcontext(connection, this);
};
%include rdp-analyzer.pac

View file

@ -0,0 +1,5 @@
module RDP;
type EarlyCapabilityFlags: record;
type ClientCoreData: record;