mirror of
https://github.com/zeek/zeek.git
synced 2025-10-15 21:18:20 +00:00
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:
parent
a63d7307c8
commit
bbedb73a45
26 changed files with 1535 additions and 346 deletions
|
@ -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()
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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::*
|
||||
|
|
|
@ -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.
|
||||
##
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
5
src/analyzer/protocol/rdp/types.bif
Normal file
5
src/analyzer/protocol/rdp/types.bif
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
module RDP;
|
||||
|
||||
type EarlyCapabilityFlags: record;
|
||||
type ClientCoreData: record;
|
Loading…
Add table
Add a link
Reference in a new issue