mirror of
https://github.com/zeek/zeek.git
synced 2025-10-04 23:58: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
|
@ -35,6 +35,21 @@ export {
|
|||
[4] = "FIPS"
|
||||
} &default = function(n: count): string { return fmt("encryption_level-%d", n); };
|
||||
|
||||
const high_color_depths = {
|
||||
[0x0004] = "4bit",
|
||||
[0x0008] = "8bit",
|
||||
[0x000F] = "15bit",
|
||||
[0x0010] = "16bit",
|
||||
[0x0018] = "24bit"
|
||||
} &default = function(n: count): string { return fmt("high_color_depth-%d", n); };
|
||||
|
||||
const color_depths = {
|
||||
[0x0001] = "24bit",
|
||||
[0x0002] = "16bit",
|
||||
[0x0004] = "15bit",
|
||||
[0x0008] = "32bit"
|
||||
} &default = function(n: count): string { return fmt("color_depth-%d", n); };
|
||||
|
||||
const results = {
|
||||
[0] = "Success",
|
||||
[1] = "User rejected",
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
signature dpd_rdp_client_request {
|
||||
ip-proto == tcp
|
||||
payload /.*Cookie: mstshash\=.*/
|
||||
enable "rdp"
|
||||
signature dpd_rdp_client {
|
||||
ip-proto == tcp
|
||||
# Client request
|
||||
payload /.*(Cookie: mstshash\=|Duca.*(rdpdr|rdpsnd|drdynvc|cliprdr))/
|
||||
requires-reverse-signature dpd_rdp_server
|
||||
enable "rdp"
|
||||
}
|
||||
|
||||
signature dpd_rdp_client_header {
|
||||
ip-proto == tcp
|
||||
payload /.*Duca.*(rdpdr|rdpsnd|drdynvc|cliprdr).*/
|
||||
enable "rdp"
|
||||
}
|
||||
|
||||
signature dpd_rdp_server_response {
|
||||
ip-proto == tcp
|
||||
payload /.*McDn.*/
|
||||
enable "rdp"
|
||||
signature dpd_rdp_server {
|
||||
ip-proto == tcp
|
||||
payload /(.{5}\xd0|.*McDn)/
|
||||
}
|
||||
|
|
|
@ -3,155 +3,180 @@
|
|||
module RDP;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { LOG };
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
type Info: record {
|
||||
## Timestamp for when the event happened.
|
||||
ts: time &log;
|
||||
## Unique ID for the connection.
|
||||
uid: string &log;
|
||||
## The connection's 4-tuple of endpoint addresses/ports.
|
||||
id: conn_id &log;
|
||||
## Cookie value used by the client machine.
|
||||
## This is typically a username.
|
||||
cookie: string &log &optional;
|
||||
## Keyboard layout (language) of the client machine.
|
||||
keyboard_layout: string &log &optional;
|
||||
type Info: record {
|
||||
## Timestamp for when the event happened.
|
||||
ts: time &log;
|
||||
## Unique ID for the connection.
|
||||
uid: string &log;
|
||||
## The connection's 4-tuple of endpoint addresses/ports.
|
||||
id: conn_id &log;
|
||||
## Cookie value used by the client machine.
|
||||
## This is typically a username.
|
||||
cookie: string &log &optional;
|
||||
## Keyboard layout (language) of the client machine.
|
||||
keyboard_layout: string &log &optional;
|
||||
## RDP client version used by the client machine.
|
||||
client_build: string &log &optional;
|
||||
## Hostname of the client machine.
|
||||
client_hostname: string &log &optional;
|
||||
## Product ID of the client machine.
|
||||
client_product_id: string &log &optional;
|
||||
## GCC result for the connection.
|
||||
result: string &log &optional;
|
||||
## Encryption level of the connection.
|
||||
encryption_level: string &log &optional;
|
||||
## Encryption method of the connection.
|
||||
encryption_method: string &log &optional;
|
||||
|
||||
## The analyzer ID used for the analyzer instance attached
|
||||
## to each connection. It is not used for logging since it's a
|
||||
## meaningless arbitrary number.
|
||||
analyzer_id: count &optional;
|
||||
## Track status of logging RDP connections.
|
||||
done: bool &default=F;
|
||||
};
|
||||
client_build: string &log &optional;
|
||||
## Name of the client machine.
|
||||
client_name: string &log &optional;
|
||||
## Product ID of the client machine.
|
||||
client_dig_product_id: string &log &optional;
|
||||
## Desktop width of the client machine.
|
||||
desktop_width: count &log &optional;
|
||||
## Desktop height of the client machine.
|
||||
desktop_height: count &log &optional;
|
||||
## The color depth requested by the client in
|
||||
## the high_color_depth field.
|
||||
requested_color_depth: string &log &optional;
|
||||
## GCC result for the connection.
|
||||
result: string &log &optional;
|
||||
## Encryption level of the connection.
|
||||
encryption_level: string &log &optional;
|
||||
## Encryption method of the connection.
|
||||
encryption_method: string &log &optional;
|
||||
};
|
||||
|
||||
## If true, detach the RDP analyzer from the connection to prevent
|
||||
## continuing to process encrypted traffic. Helps with performance
|
||||
## (especially with large file transfers).
|
||||
const disable_analyzer_after_detection = T &redef;
|
||||
## continuing to process encrypted traffic.
|
||||
const disable_analyzer_after_detection = F &redef;
|
||||
|
||||
## The amount of time to monitor an RDP session from when it is first
|
||||
## identified. When this interval is reached, the session is logged.
|
||||
const rdp_interval = 10secs &redef;
|
||||
const rdp_check_interval = 10secs &redef;
|
||||
|
||||
## Event that can be handled to access the rdp record as it is sent on
|
||||
## to the logging framework.
|
||||
global log_rdp: event(rec: Info);
|
||||
## Event that can be handled to access the rdp record as it is sent on
|
||||
## to the logging framework.
|
||||
global log_rdp: event(rec: Info);
|
||||
}
|
||||
|
||||
# Internal fields that aren't useful externally
|
||||
redef record Info += {
|
||||
## The analyzer ID used for the analyzer instance attached
|
||||
## to each connection. It is not used for logging since it's a
|
||||
## meaningless arbitrary number.
|
||||
analyzer_id: count &optional;
|
||||
## Track status of logging RDP connections.
|
||||
done: bool &default=F;
|
||||
};
|
||||
|
||||
redef record connection += {
|
||||
rdp: Info &optional;
|
||||
};
|
||||
rdp: Info &optional;
|
||||
};
|
||||
|
||||
const ports = { 3389/tcp };
|
||||
redef likely_server_ports += { ports };
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(RDP::LOG, [$columns=Info, $ev=log_rdp]);
|
||||
Analyzer::register_for_ports(Analyzer::ANALYZER_RDP, ports);
|
||||
}
|
||||
|
||||
# Verify that the RDP session contains
|
||||
# RDP data before writing it to the log.
|
||||
function verify_rdp(c: connection)
|
||||
{
|
||||
local info = c$rdp;
|
||||
if ( info?$cookie || info?$keyboard_layout || info?$result )
|
||||
Log::write(RDP::LOG,info);
|
||||
else
|
||||
Reporter::error("RDP analyzer was initialized but no data was found");
|
||||
Log::create_stream(RDP::LOG, [$columns=RDP::Info, $ev=log_rdp]);
|
||||
Analyzer::register_for_ports(Analyzer::ANALYZER_RDP, ports);
|
||||
}
|
||||
|
||||
event log_record(c: connection, remove_analyzer: bool)
|
||||
{
|
||||
function write_log(c: connection)
|
||||
{
|
||||
local info = c$rdp;
|
||||
if ( info$done )
|
||||
return;
|
||||
|
||||
# Mark this record as fully logged and finished.
|
||||
info$done = T;
|
||||
|
||||
# Verify that the RDP session contains
|
||||
# RDP data before writing it to the log.
|
||||
if ( info?$cookie || info?$keyboard_layout || info?$result )
|
||||
Log::write(RDP::LOG, info);
|
||||
}
|
||||
|
||||
event check_record(c: connection)
|
||||
{
|
||||
# If the record was logged, then stop processing.
|
||||
if ( c$rdp$done )
|
||||
return;
|
||||
if ( c$rdp$done )
|
||||
return;
|
||||
|
||||
# If the analyzer is no longer attached, then
|
||||
# log the record and stop processing.
|
||||
if ( ! remove_analyzer )
|
||||
{
|
||||
c$rdp$done = T;
|
||||
verify_rdp(c);
|
||||
return;
|
||||
}
|
||||
|
||||
# If the value rdp_interval has passed since the
|
||||
# If the value rdp_check_interval has passed since the
|
||||
# RDP session was started, then log the record.
|
||||
local diff = network_time() - c$rdp$ts;
|
||||
if ( diff > rdp_interval )
|
||||
{
|
||||
c$rdp$done = T;
|
||||
verify_rdp(c);
|
||||
local diff = network_time() - c$rdp$ts;
|
||||
if ( diff > rdp_check_interval )
|
||||
{
|
||||
write_log(c);
|
||||
|
||||
# Remove the analyzer if it is still attached.
|
||||
if ( remove_analyzer && disable_analyzer_after_detection && connection_exists(c$id) && c$rdp?$analyzer_id )
|
||||
{
|
||||
disable_analyzer(c$id, c$rdp$analyzer_id);
|
||||
delete c$rdp$analyzer_id;
|
||||
}
|
||||
# Remove the analyzer if it is still attached.
|
||||
if ( disable_analyzer_after_detection &&
|
||||
connection_exists(c$id) &&
|
||||
c$rdp?$analyzer_id )
|
||||
{
|
||||
disable_analyzer(c$id, c$rdp$analyzer_id);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
# If the analyzer is attached and the duration
|
||||
# to monitor the RDP session was not met, then
|
||||
# reschedule the logging event.
|
||||
else
|
||||
schedule +rdp_interval { log_record(c,remove_analyzer) };
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
# If the analyzer is attached and the duration
|
||||
# to monitor the RDP session was not met, then
|
||||
# reschedule the logging event.
|
||||
schedule rdp_check_interval { check_record(c) };
|
||||
}
|
||||
}
|
||||
|
||||
function set_session(c: connection)
|
||||
{
|
||||
if ( ! c?$rdp )
|
||||
{
|
||||
c$rdp = [$ts=network_time(),$id=c$id,$uid=c$uid];
|
||||
# The RDP session is scheduled to be logged from
|
||||
# the time it is first initiated.
|
||||
schedule +rdp_interval { log_record(c,T) };
|
||||
}
|
||||
}
|
||||
{
|
||||
if ( ! c?$rdp )
|
||||
{
|
||||
c$rdp = [$ts=network_time(),$id=c$id,$uid=c$uid];
|
||||
# The RDP session is scheduled to be logged from
|
||||
# the time it is first initiated.
|
||||
schedule rdp_check_interval { check_record(c) };
|
||||
}
|
||||
}
|
||||
|
||||
event rdp_client_request(c: connection, cookie: string) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
|
||||
c$rdp$cookie = cookie;
|
||||
}
|
||||
|
||||
event rdp_client_data(c: connection, keyboard_layout: count, build: count, hostname: string, product_id: string) &priority=5
|
||||
event rdp_client_core_data(c: connection, data: RDP::ClientCoreData) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
c$rdp$keyboard_layout = languages[keyboard_layout];
|
||||
c$rdp$client_build = builds[build];
|
||||
c$rdp$client_hostname = gsub(cat(hostname),/\\0/,"");
|
||||
c$rdp$client_product_id = gsub(cat(product_id),/\\0/,"");
|
||||
|
||||
c$rdp$keyboard_layout = RDP::languages[data$keyboard_layout];
|
||||
c$rdp$client_build = RDP::builds[data$client_build];
|
||||
c$rdp$client_name = data$client_name;
|
||||
c$rdp$client_dig_product_id = data$dig_product_id;
|
||||
c$rdp$desktop_width = data$desktop_width;
|
||||
c$rdp$desktop_height = data$desktop_height;
|
||||
if ( data?$ec_flags && data$ec_flags$want_32bpp_session )
|
||||
c$rdp$requested_color_depth = "32-bit";
|
||||
else
|
||||
c$rdp$requested_color_depth = RDP::high_color_depths[data$high_color_depth];
|
||||
}
|
||||
|
||||
event rdp_result(c: connection, result: count) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
c$rdp$result = results[result];
|
||||
set_session(c);
|
||||
|
||||
c$rdp$result = RDP::results[result];
|
||||
}
|
||||
|
||||
event rdp_server_security(c: connection, encryption_method: count, encryption_level: count) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
c$rdp$encryption_method = encryption_methods[encryption_method];
|
||||
c$rdp$encryption_level = encryption_levels[encryption_level];
|
||||
|
||||
c$rdp$encryption_method = RDP::encryption_methods[encryption_method];
|
||||
c$rdp$encryption_level = RDP::encryption_levels[encryption_level];
|
||||
}
|
||||
|
||||
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool)
|
||||
{
|
||||
Files::add_analyzer(f, Files::ANALYZER_X509);
|
||||
# always calculate hashes. They are not necessary for base scripts
|
||||
# but very useful for identification, and required for policy scripts
|
||||
Files::add_analyzer(f, Files::ANALYZER_MD5);
|
||||
Files::add_analyzer(f, Files::ANALYZER_SHA1);
|
||||
}
|
||||
|
||||
event protocol_confirmation(c: connection, atype: Analyzer::Tag, aid: count) &priority=5
|
||||
|
@ -167,12 +192,14 @@ event protocol_violation(c: connection, atype: Analyzer::Tag, aid: count, reason
|
|||
{
|
||||
# If a protocol violation occurs, then log the record immediately.
|
||||
if ( c?$rdp )
|
||||
schedule +0secs { log_record(c,F) };
|
||||
write_log(c);
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
{
|
||||
# If the connection is removed, then log the record immediately.
|
||||
if ( c?$rdp )
|
||||
schedule +0secs { log_record(c,F) };
|
||||
}
|
||||
if ( c?$rdp )
|
||||
{
|
||||
write_log(c);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue