From 849875e8be73d0e0b5a6ebca74ed56fdabba464b Mon Sep 17 00:00:00 2001 From: Martin van Hensbergen Date: Mon, 11 Apr 2016 10:35:00 +0200 Subject: [PATCH] Analyzer and bro script for RFB protocol (VNC) This analyzer parses the Remote Frame Buffer protocol, usually referred to as the 'VNC protocol'. It supports several dialects (3.3, 3.7, 3.8) and also handles the Apple Remote Desktop variant. It will log such facts as client/server versions, authentication method used, authentication result, height, width and name of the shared screen. It also includes two testcases. Todo: Apple Remote Desktop seems to have some bytes prepended to the screen name. This is not interepreted correctly. --- scripts/base/init-default.bro | 1 + scripts/base/protocols/rfb/__load__.bro | 3 + scripts/base/protocols/rfb/dpd.sig | 12 ++ scripts/base/protocols/rfb/main.bro | 143 +++++++++++++ src/analyzer/protocol/CMakeLists.txt | 1 + src/analyzer/protocol/rfb/CMakeLists.txt | 11 + src/analyzer/protocol/rfb/Plugin.cc | 25 +++ src/analyzer/protocol/rfb/RFB.cc | 69 +++++++ src/analyzer/protocol/rfb/RFB.h | 45 ++++ src/analyzer/protocol/rfb/events.bif | 50 +++++ src/analyzer/protocol/rfb/rfb-analyzer.pac | 193 ++++++++++++++++++ src/analyzer/protocol/rfb/rfb-protocol.pac | 139 +++++++++++++ src/analyzer/protocol/rfb/rfb.pac | 42 ++++ .../rfb.log | 12 ++ .../rfb.log | 12 ++ .../btest/Traces/rfb/vnc-mac-to-linux.pcap | Bin 0 -> 40255 bytes testing/btest/Traces/rfb/vncmac.pcap | Bin 0 -> 8848 bytes .../rfb/rfb-apple-remote-desktop.test | 4 + .../base/protocols/rfb/vnc-mac-to-linux.test | 4 + 19 files changed, 766 insertions(+) create mode 100644 scripts/base/protocols/rfb/__load__.bro create mode 100644 scripts/base/protocols/rfb/dpd.sig create mode 100644 scripts/base/protocols/rfb/main.bro create mode 100644 src/analyzer/protocol/rfb/CMakeLists.txt create mode 100644 src/analyzer/protocol/rfb/Plugin.cc create mode 100644 src/analyzer/protocol/rfb/RFB.cc create mode 100644 src/analyzer/protocol/rfb/RFB.h create mode 100644 src/analyzer/protocol/rfb/events.bif create mode 100644 src/analyzer/protocol/rfb/rfb-analyzer.pac create mode 100644 src/analyzer/protocol/rfb/rfb-protocol.pac create mode 100644 src/analyzer/protocol/rfb/rfb.pac create mode 100644 testing/btest/Baseline/scripts.base.protocols.rfb.rfb-apple-remote-desktop/rfb.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.rfb.vnc-mac-to-linux/rfb.log create mode 100644 testing/btest/Traces/rfb/vnc-mac-to-linux.pcap create mode 100644 testing/btest/Traces/rfb/vncmac.pcap create mode 100644 testing/btest/scripts/base/protocols/rfb/rfb-apple-remote-desktop.test create mode 100644 testing/btest/scripts/base/protocols/rfb/vnc-mac-to-linux.test diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index 609ed7200c..418ccbb43e 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -55,6 +55,7 @@ @load base/protocols/pop3 @load base/protocols/radius @load base/protocols/rdp +@load base/protocols/rfb @load base/protocols/sip @load base/protocols/snmp @load base/protocols/smtp diff --git a/scripts/base/protocols/rfb/__load__.bro b/scripts/base/protocols/rfb/__load__.bro new file mode 100644 index 0000000000..9e43682d13 --- /dev/null +++ b/scripts/base/protocols/rfb/__load__.bro @@ -0,0 +1,3 @@ +# Generated by binpac_quickstart +@load ./main +@load-sigs ./dpd.sig \ No newline at end of file diff --git a/scripts/base/protocols/rfb/dpd.sig b/scripts/base/protocols/rfb/dpd.sig new file mode 100644 index 0000000000..40793ad590 --- /dev/null +++ b/scripts/base/protocols/rfb/dpd.sig @@ -0,0 +1,12 @@ +signature dpd_rfb_server { + ip-proto == tcp + payload /^RFB/ + requires-reverse-signature dpd_rfb_client + enable "rfb" +} + +signature dpd_rfb_client { + ip-proto == tcp + payload /^RFB/ + tcp-state originator +} \ No newline at end of file diff --git a/scripts/base/protocols/rfb/main.bro b/scripts/base/protocols/rfb/main.bro new file mode 100644 index 0000000000..97f194b789 --- /dev/null +++ b/scripts/base/protocols/rfb/main.bro @@ -0,0 +1,143 @@ +module Rfb; + +export { + 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; + + client_major_version: string &log &optional; + client_minor_version: string &log &optional; + server_major_version: string &log &optional; + server_minor_version: string &log &optional; + + authentication_method: string &log &optional; + auth: bool &log &optional; + + share_flag: bool &log &optional; + desktop_name: string &log &optional; + width: count &log &optional; + height: count &log &optional; + + done: bool &default=F; + }; + + global log_rfb: event(rec: Info); +} + +function friendly_auth_name(auth: count): string { + switch (auth) { + case 0: + return "Invalid"; + case 1: + return "None"; + case 2: + return "VNC"; + case 16: + return "Tight"; + case 17: + return "Ultra"; + case 18: + return "TLS"; + case 19: + return "VeNCrypt"; + case 20: + return "GTK-VNC SASL"; + case 21: + return "MD5 hash authentication"; + case 22: + return "Colin Dean xvp"; + case 30: + return "Apple Remote Desktop"; + } + return "RealVNC"; + +} + + +redef record connection += { + rfb_state: Info &optional; +}; + +event bro_init() &priority=5 + { + Log::create_stream(Rfb::LOG, [$columns=Info, $ev=log_rfb, $path="rfb"]); + } + +function write_log(c:connection) { + local state = c$rfb_state; + if ( state?$done && state$done == T) { + return; + } + Log::write(Rfb::LOG, c$rfb_state); + c$rfb_state$done = T; +} + +function set_session(c: connection) { + if ( ! c?$rfb_state ) { + local info: Info; + info$ts = network_time(); + info$uid = c$uid; + info$id = c$id; + + c$rfb_state = info; + } + } + +event rfb_event(c: connection) + { + set_session(c); + } + +event rfb_client_version(c: connection, major_version: string, minor_version: string) + { + set_session(c); + c$rfb_state$client_major_version = major_version; + c$rfb_state$client_minor_version = minor_version; + } + +event rfb_server_version(c: connection, major_version: string, minor_version: string) + { + set_session(c); + c$rfb_state$server_major_version = major_version; + c$rfb_state$server_minor_version = minor_version; + add c$service["rfb"]; + } + +event rfb_authentication_type(c: connection, authtype: count) + { + c$rfb_state$authentication_method = friendly_auth_name(authtype); + } + +event rfb_server_parameters(c: connection, name: string, width: count, height: count) + { + c$rfb_state$desktop_name = name; + c$rfb_state$width = width; + c$rfb_state$height = height; + write_log(c); + } + +event rfb_auth_result(c: connection, result: count) + { + if ( result ==0 ) { + c$rfb_state$auth = T; + } else { + c$rfb_state$auth = F; + } + } + +event rfb_share_flag(c: connection, flag: bool) + { + c$rfb_state$share_flag = flag; + } + +event connection_state_remove(c: connection) { + if ( c?$rfb_state ) { + write_log(c); + } +} diff --git a/src/analyzer/protocol/CMakeLists.txt b/src/analyzer/protocol/CMakeLists.txt index 467fce83ee..8c7a3f002e 100644 --- a/src/analyzer/protocol/CMakeLists.txt +++ b/src/analyzer/protocol/CMakeLists.txt @@ -30,6 +30,7 @@ add_subdirectory(pia) add_subdirectory(pop3) add_subdirectory(radius) add_subdirectory(rdp) +add_subdirectory(rfb) add_subdirectory(rpc) add_subdirectory(sip) add_subdirectory(snmp) diff --git a/src/analyzer/protocol/rfb/CMakeLists.txt b/src/analyzer/protocol/rfb/CMakeLists.txt new file mode 100644 index 0000000000..8131ca7362 --- /dev/null +++ b/src/analyzer/protocol/rfb/CMakeLists.txt @@ -0,0 +1,11 @@ +# Generated by binpac_quickstart + +include(BroPlugin) + +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + +bro_plugin_begin(Bro RFB) + bro_plugin_cc(RFB.cc Plugin.cc) + bro_plugin_bif(events.bif) + bro_plugin_pac(rfb.pac rfb-analyzer.pac rfb-protocol.pac) +bro_plugin_end() \ No newline at end of file diff --git a/src/analyzer/protocol/rfb/Plugin.cc b/src/analyzer/protocol/rfb/Plugin.cc new file mode 100644 index 0000000000..55704497e9 --- /dev/null +++ b/src/analyzer/protocol/rfb/Plugin.cc @@ -0,0 +1,25 @@ +// Generated by binpac_quickstart + +#include "plugin/Plugin.h" + +#include "RFB.h" + +namespace plugin { +namespace Bro_RFB { + +class Plugin : public plugin::Plugin { +public: + plugin::Configuration Configure() + { + AddComponent(new ::analyzer::Component("RFB", + ::analyzer::rfb::RFB_Analyzer::InstantiateAnalyzer)); + + plugin::Configuration config; + config.name = "Bro::RFB"; + config.description = "Parser for rfb (VNC) analyzer"; + return config; + } +} plugin; + +} +} \ No newline at end of file diff --git a/src/analyzer/protocol/rfb/RFB.cc b/src/analyzer/protocol/rfb/RFB.cc new file mode 100644 index 0000000000..c761d0bf0f --- /dev/null +++ b/src/analyzer/protocol/rfb/RFB.cc @@ -0,0 +1,69 @@ +// Generated by binpac_quickstart + +#include "RFB.h" + +#include "analyzer/protocol/tcp/TCP_Reassembler.h" + +#include "Reporter.h" + +#include "events.bif.h" + +using namespace analyzer::rfb; + +RFB_Analyzer::RFB_Analyzer(Connection* c) + +: tcp::TCP_ApplicationAnalyzer("RFB", c) + + { + interp = new binpac::RFB::RFB_Conn(this); + had_gap = false; + } + +RFB_Analyzer::~RFB_Analyzer() + { + delete interp; + } + +void RFB_Analyzer::Done() + { + tcp::TCP_ApplicationAnalyzer::Done(); + + interp->FlowEOF(true); + interp->FlowEOF(false); + + } + +void RFB_Analyzer::EndpointEOF(bool is_orig) + { + tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig); + interp->FlowEOF(is_orig); + } + +void RFB_Analyzer::DeliverStream(int len, const u_char* data, bool orig) + { + tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig); + assert(TCP()); + if ( TCP()->IsPartial() ) + return; + + if ( had_gap ) + // If only one side had a content gap, we could still try to + // deliver data to the other side if the script layer can handle this. + return; + + try + { + interp->NewData(orig, data, data + len); + } + catch ( const binpac::Exception& e ) + { + ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); + } + } + +void RFB_Analyzer::Undelivered(uint64 seq, int len, bool orig) + { + tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig); + had_gap = true; + interp->NewGap(orig, len); + } diff --git a/src/analyzer/protocol/rfb/RFB.h b/src/analyzer/protocol/rfb/RFB.h new file mode 100644 index 0000000000..cd6e7348d0 --- /dev/null +++ b/src/analyzer/protocol/rfb/RFB.h @@ -0,0 +1,45 @@ +// Generated by binpac_quickstart + +#ifndef ANALYZER_PROTOCOL_RFB_RFB_H +#define ANALYZER_PROTOCOL_RFB_RFB_H + +#include "events.bif.h" + + +#include "analyzer/protocol/tcp/TCP.h" + +#include "rfb_pac.h" + +namespace analyzer { namespace rfb { + +class RFB_Analyzer + +: public tcp::TCP_ApplicationAnalyzer { + +public: + RFB_Analyzer(Connection* conn); + virtual ~RFB_Analyzer(); + + // Overriden from Analyzer. + virtual void Done(); + + 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 RFB_Analyzer(conn); } + +protected: + binpac::RFB::RFB_Conn* interp; + + bool had_gap; + +}; + +} } // namespace analyzer::* + +#endif diff --git a/src/analyzer/protocol/rfb/events.bif b/src/analyzer/protocol/rfb/events.bif new file mode 100644 index 0000000000..a3cf5f7ad8 --- /dev/null +++ b/src/analyzer/protocol/rfb/events.bif @@ -0,0 +1,50 @@ +## Generated for RFB event +## +## c: The connection record for the underlying transport-layer session/flow. +event rfb_event%(c: connection%); + +## Generated for RFB event authentication mechanism selection +## +## c: The connection record for the underlying transport-layer session/flow. +## +## authtype: the value of the chosen authentication mechanism +event rfb_authentication_type%(c: connection, authtype: count%); + +## Generated for RFB event authentication result message +## +## c: The connection record for the underlying transport-layer session/flow. +## +## result: whether or not authentication was succesful +event rfb_auth_result%(c: connection, result: count%); + +## Generated for RFB event share flag messages +## +## c: The connection record for the underlying transport-layer session/flow. +## +## flag: whether or not the share flag was set +event rfb_share_flag%(c: connection, flag: bool%); + +## Generated for RFB event client banner message +## +## c: The connection record for the underlying transport-layer session/flow. +## +## version: of the client's rfb library +event rfb_client_version%(c: connection, major_version: string, minor_version: string%); + +## Generated for RFB event server banner message +## +## c: The connection record for the underlying transport-layer session/flow. +## +## version: of the server's rfb library +event rfb_server_version%(c: connection, major_version: string, minor_version: string%); + +## Generated for RFB event server parameter message +## +## c: The connection record for the underlying transport-layer session/flow. +## +## name: name of the shared screen +## +## width: width of the shared screen +## +## height: height of the shared screen +event rfb_server_parameters%(c: connection, name: string, width: count, height: count%); \ No newline at end of file diff --git a/src/analyzer/protocol/rfb/rfb-analyzer.pac b/src/analyzer/protocol/rfb/rfb-analyzer.pac new file mode 100644 index 0000000000..4233a423f7 --- /dev/null +++ b/src/analyzer/protocol/rfb/rfb-analyzer.pac @@ -0,0 +1,193 @@ +# Generated by binpac_quickstart + +refine flow RFB_Flow += { + function proc_rfb_message(msg: RFB_PDU): bool + %{ + BifEvent::generate_rfb_event(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn()); + return true; + %} + + function proc_rfb_client_version(major: bytestring, minor: bytestring) : bool + %{ + BifEvent::generate_rfb_client_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), bytestring_to_val(major), bytestring_to_val(minor)); + return true; + %} + + function proc_rfb_version(client: bool, major: bytestring, minor: bytestring) : bool + %{ + if (client) { + BifEvent::generate_rfb_client_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), bytestring_to_val(major), bytestring_to_val(minor)); + } else { + BifEvent::generate_rfb_server_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), bytestring_to_val(major), bytestring_to_val(minor)); + } + return true; + %} + + function proc_rfb_share_flag(shared: bool) : bool + %{ + BifEvent::generate_rfb_share_flag(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), shared); + return true; + %} + + function proc_security_types(msg: RFBSecurityTypes) : bool + %{ + BifEvent::generate_rfb_authentication_type(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${msg.sectype}); + return true; + %} + + function proc_security_types37(msg: RFBAuthTypeSelected) : bool + %{ + BifEvent::generate_rfb_authentication_type(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${msg.type}); + return true; + %} + + function proc_handle_server_params(msg:RFBServerInit) : bool + %{ + BifEvent::generate_rfb_server_parameters(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), bytestring_to_val(${msg.name}), ${msg.width}, ${msg.height}); + return true; + %} + + function proc_handle_security_result(result : uint32) : bool + %{ + BifEvent::generate_rfb_auth_result(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), result); + return true; + %} +}; + +refine connection RFB_Conn += { + %member{ + enum states { + AWAITING_SERVER_BANNER = 0, + AWAITING_CLIENT_BANNER = 1, + AWAITING_SERVER_AUTH_TYPES = 2, + AWAITING_SERVER_CHALLENGE = 3, + AWAITING_CLIENT_RESPONSE = 4, + AWAITING_SERVER_AUTH_RESULT = 5, + AWAITING_CLIENT_SHARE_FLAG = 6, + AWAITING_SERVER_PARAMS = 7, + AWAITING_CLIENT_AUTH_METHOD = 8, + AWAITING_SERVER_ARD_CHALLENGE = 9, + AWAITING_CLIENT_ARD_RESPONSE = 10, + AWAITING_SERVER_AUTH_TYPES37 = 11, + AWAITING_CLIENT_AUTH_TYPE_SELECTED37 = 12, + RFB_MESSAGE = 13 + }; + %} + + function get_state(client: bool) : int + %{ + return state; + %} + + function handle_banners(client: bool, msg: RFBProtocolVersion) : bool + %{ + if ( client ) { + // Set protocol version on client's version + int minor_version = bytestring_to_int(${msg.minor},10); + + // Apple specifies minor version "889" but talks v37 + if ( minor_version >= 7 ) { + state = AWAITING_SERVER_AUTH_TYPES37; + } else { + state = AWAITING_SERVER_AUTH_TYPES; + } + } else { + if ( !client ) { + state = AWAITING_CLIENT_BANNER; + } + } + return true; + %} + + function handle_ard_challenge() : bool + %{ + state = AWAITING_CLIENT_ARD_RESPONSE; + return true; + %} + + function handle_ard_response() : bool + %{ + state = AWAITING_SERVER_AUTH_RESULT; + return true; + %} + + function handle_auth_request() : bool + %{ + state = AWAITING_CLIENT_RESPONSE; + return true; + %} + + function handle_auth_response() : bool + %{ + state = AWAITING_SERVER_AUTH_RESULT; + return true; + %} + + function handle_security_result(msg: RFBSecurityResult) : bool + %{ + if ( ${msg.result} == 0 ) //FIXME + { + state = AWAITING_CLIENT_SHARE_FLAG; + } + return true; + %} + + function handle_client_init(msg: RFBClientInit) : bool + %{ + state = AWAITING_SERVER_PARAMS; + + return true; + %} + + function handle_server_init(msg: RFBServerInit) : bool + %{ + state = RFB_MESSAGE; + return true; + %} + + function handle_security_types(msg: RFBSecurityTypes): bool + %{ + if ( msg->sectype() == 0 ) { // No auth + state = AWAITING_CLIENT_SHARE_FLAG; + return true; + } + if ( msg->sectype() == 2 ) { //VNC + state = AWAITING_SERVER_CHALLENGE; + } + return false; + %} + + function handle_security_types37(msg: RFBSecurityTypes37): bool + %{ + if ( ${msg.count} == 0 ) { // No auth + state = AWAITING_CLIENT_SHARE_FLAG; + return true; + } + state = AWAITING_CLIENT_AUTH_TYPE_SELECTED37; + return true; + %} + + function handle_auth_type_selected(msg: RFBAuthTypeSelected): bool + %{ + if ( ${msg.type} == 30 ) { // Apple Remote Desktop + state = AWAITING_SERVER_ARD_CHALLENGE; + return true; + } + + if ( ${msg.type} == 1 ) { // No Auth + state = AWAITING_SERVER_AUTH_RESULT; + } else { + // Assume VNC + state = AWAITING_SERVER_CHALLENGE; + } + return true; + %} + + %member{ + uint8 state = AWAITING_SERVER_BANNER; + %} +}; + +refine typeattr RFB_PDU += &let { + proc: bool = $context.flow.proc_rfb_message(this); +}; diff --git a/src/analyzer/protocol/rfb/rfb-protocol.pac b/src/analyzer/protocol/rfb/rfb-protocol.pac new file mode 100644 index 0000000000..0eb5542001 --- /dev/null +++ b/src/analyzer/protocol/rfb/rfb-protocol.pac @@ -0,0 +1,139 @@ +enum states { + AWAITING_SERVER_BANNER = 0, + AWAITING_CLIENT_BANNER = 1, + AWAITING_SERVER_AUTH_TYPES = 2, + AWAITING_SERVER_CHALLENGE = 3, + AWAITING_CLIENT_RESPONSE = 4, + AWAITING_SERVER_AUTH_RESULT = 5, + AWAITING_CLIENT_SHARE_FLAG = 6, + AWAITING_SERVER_PARAMS = 7, + AWAITING_CLIENT_AUTH_METHOD = 8, + AWAITING_SERVER_ARD_CHALLENGE = 9, + AWAITING_CLIENT_ARD_RESPONSE = 10, + AWAITING_SERVER_AUTH_TYPES37 = 11, + AWAITING_CLIENT_AUTH_TYPE_SELECTED37 = 12, + RFB_MESSAGE = 13 + }; + +type RFBProtocolVersion (client: bool) = record { + header : "RFB "; + major :bytestring &length=3; + dot: "."; + minor: bytestring &length=3; + pad: uint8; +} &let { + proc: bool = $context.connection.handle_banners(client, this); + proc2: bool = $context.flow.proc_rfb_version(client, major, minor); +} + +type RFBSecurityTypes = record { + sectype: uint32; +} &let { + proc: bool = $context.connection.handle_security_types(this); + proc2: bool = $context.flow.proc_security_types(this); +}; + +type RFBSecurityTypes37 = record { + count: uint8; + types: uint8[count]; +} &let { + proc: bool = $context.connection.handle_security_types37(this); +}; + +type RFBAuthTypeSelected = record { + type: uint8; +} &let { + proc: bool = $context.connection.handle_auth_type_selected(this); + proc2: bool = $context.flow.proc_security_types37(this); +}; + +type RFBSecurityResult = record { + result: uint32; +} &let { + proc: bool = $context.connection.handle_security_result(this); + proc2: bool = $context.flow.proc_handle_security_result(result); +}; + +type RFBSecurityResultReason = record { + len: uint32; + reason: bytestring &length=len; +}; + +type RFBVNCAuthenticationRequest = record { + challenge: bytestring &length=16; +} &let { + proc: bool = $context.connection.handle_auth_request(); +}; + +type RFBVNCAuthenticationResponse = record { + response: bytestring &length= 16; +} &let { + proc: bool = $context.connection.handle_auth_response(); +}; + +type RFBSecurityARDChallenge = record { + challenge: bytestring &restofdata; +} &let { + proc: bool = $context.connection.handle_ard_challenge(); +} + +type RFBSecurityARDResponse = record { + response: bytestring &restofdata; +} &let { + proc: bool = $context.connection.handle_ard_response(); +} + +type RFBClientInit = record { + shared_flag: uint8; +} &let { + proc: bool = $context.connection.handle_client_init(this); + proc2: bool = $context.flow.proc_rfb_share_flag(shared_flag); +} + +type RFBServerInit = record { + width: uint16; + height: uint16; + pixel_format: bytestring &length= 16; + len : uint32; + name: bytestring &length = len; +} &let { + proc: bool = $context.connection.handle_server_init(this); + proc2: bool = $context.flow.proc_handle_server_params(this); +}; + +type RFB_PDU_request = record { + request: case state of { + AWAITING_CLIENT_BANNER -> version: RFBProtocolVersion(true); + AWAITING_CLIENT_RESPONSE -> response: RFBVNCAuthenticationResponse; + AWAITING_CLIENT_SHARE_FLAG -> shareflag: RFBClientInit; + AWAITING_CLIENT_AUTH_TYPE_SELECTED37 -> authtype: RFBAuthTypeSelected; + AWAITING_CLIENT_ARD_RESPONSE -> ard_response: RFBSecurityARDResponse; + RFB_MESSAGE -> ignore: bytestring &restofdata; + default -> data: bytestring &restofdata; + } &requires(state); + } &let { + state: uint8 = $context.connection.get_state(true); +}; + +type RFB_PDU_response = record { + request: case rstate of { + AWAITING_SERVER_BANNER -> version: RFBProtocolVersion(false); + AWAITING_SERVER_AUTH_TYPES -> auth_types: RFBSecurityTypes; + AWAITING_SERVER_AUTH_TYPES37 -> auth_types37: RFBSecurityTypes37; + AWAITING_SERVER_CHALLENGE -> challenge: RFBVNCAuthenticationRequest; + AWAITING_SERVER_AUTH_RESULT -> authresult : RFBSecurityResult; + AWAITING_SERVER_ARD_CHALLENGE -> ard_challenge: RFBSecurityARDChallenge; + AWAITING_SERVER_PARAMS -> serverinit: RFBServerInit; + RFB_MESSAGE -> ignore: bytestring &restofdata; + default -> data: bytestring &restofdata; + } &requires(rstate); + } &let { + rstate: uint8 = $context.connection.get_state(false); +}; + +type RFB_PDU(is_orig: bool) = record { + payload: case is_orig of { + true -> request: RFB_PDU_request; + false -> response: RFB_PDU_response; + }; +} &byteorder = bigendian; diff --git a/src/analyzer/protocol/rfb/rfb.pac b/src/analyzer/protocol/rfb/rfb.pac new file mode 100644 index 0000000000..310ad38893 --- /dev/null +++ b/src/analyzer/protocol/rfb/rfb.pac @@ -0,0 +1,42 @@ +# Generated by binpac_quickstart + +# Analyzer for Parser for rfb (VNC) +# - rfb-protocol.pac: describes the rfb protocol messages +# - rfb-analyzer.pac: describes the rfb analyzer code + +%include binpac.pac +%include bro.pac + +%extern{ + #include "events.bif.h" +%} + +analyzer RFB withcontext { + connection: RFB_Conn; + flow: RFB_Flow; +}; + +# Our connection consists of two flows, one in each direction. +connection RFB_Conn(bro_analyzer: BroAnalyzer) { + upflow = RFB_Flow(true); + downflow = RFB_Flow(false); +}; + +%include rfb-protocol.pac + +# Now we define the flow: +flow RFB_Flow(is_orig: bool) { + + # ## TODO: Determine if you want flowunit or datagram parsing: + + # Using flowunit will cause the anlayzer to buffer incremental input. + # This is needed for &oneline and &length. If you don't need this, you'll + # get better performance with datagram. + + # flowunit = RFB_PDU(is_orig) withcontext(connection, this); + + datagram = RFB_PDU(is_orig) withcontext(connection, this); + +}; + +%include rfb-analyzer.pac \ No newline at end of file diff --git a/testing/btest/Baseline/scripts.base.protocols.rfb.rfb-apple-remote-desktop/rfb.log b/testing/btest/Baseline/scripts.base.protocols.rfb.rfb-apple-remote-desktop/rfb.log new file mode 100644 index 0000000000..6f8a2e987d --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.rfb.rfb-apple-remote-desktop/rfb.log @@ -0,0 +1,12 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path rfb +#open 2016-04-11-08-25-48 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p client_major_version client_minor_version server_major_version server_minor_version authentication_method auth share_flag desktop_name width height +#types time string addr port addr port string string string string string bool bool string count count +1459148054.031382 CCvvfg3TEfuqmmG4bh 192.168.2.115 52353 192.168.2.16 5900 003 889 003 889 Apple Remote Desktop T T \x00\x00\x00\x00\x00\x02\xbf\xfe\xe7\x03\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00MacMini SSD 1920 1080 +1459148050.685932 CjhGID4nQcgTWjvg4c 192.168.2.115 52352 192.168.2.16 5900 003 889 003 889 Apple Remote Desktop F - - - - +1459148047.738043 CXWv6p3arKYeMETxOg 192.168.2.115 52351 192.168.2.16 5900 003 889 003 889 - - - - - - +#close 2016-04-11-08-25-48 diff --git a/testing/btest/Baseline/scripts.base.protocols.rfb.vnc-mac-to-linux/rfb.log b/testing/btest/Baseline/scripts.base.protocols.rfb.vnc-mac-to-linux/rfb.log new file mode 100644 index 0000000000..de1d70ec63 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.rfb.vnc-mac-to-linux/rfb.log @@ -0,0 +1,12 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path rfb +#open 2016-04-06-13-48-56 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p client_major_version client_minor_version server_major_version server_minor_version authentication_method auth share_flag desktop_name width height +#types time string addr port addr port string string string string string bool bool string count count +1459093553.334734 CsRx2w45OKnoww6xl4 192.168.2.115 49259 192.168.2.125 5901 003 003 003 008 VNC T T root's X desktop (martin-VirtualBox:1) 1024 768 +1459093548.745805 CjhGID4nQcgTWjvg4c 192.168.2.115 49256 192.168.2.125 5901 003 003 003 008 VNC - - - - - +1459093551.559391 CCvvfg3TEfuqmmG4bh 192.168.2.115 49257 192.168.2.125 5901 003 003 003 008 VNC F - - - - +#close 2016-04-06-13-48-56 diff --git a/testing/btest/Traces/rfb/vnc-mac-to-linux.pcap b/testing/btest/Traces/rfb/vnc-mac-to-linux.pcap new file mode 100644 index 0000000000000000000000000000000000000000..3856b94caebf0764b7f7eab92fea8a8e24caffa9 GIT binary patch literal 40255 zcmdU&2UHbT`>zKOu%KY?Jr)#HEU1VjMzMe)Hms;vP?|(VjPzK*ib_X75Ct^~*igZO zC3cVqA_fqQ0*ZhrAV@v;eK(oIna!O2{lE2J>)tC_vk2`s&wgglyJz<7b9`~{;&DZ; z8Q1vHj8lLwiX@dLqp~>eXZU~Insb9ZS5^fqEj(aR-JF}waZ~1KPT{7gtpEA;X~iwD zIp1E>x%C*6fh#fTyrM+%_!h@0HdDT=proX%pwPUfPV^2<{8=-_hVt_xq|#^>Tb zNc!N&iJ0DI9!Z8hbZ&in4gNLkVbw1kIIdy?$7yljOvbEi#bDf;fhm(hq>LLnx9*W( zI3JUc@mUeY81riY{tOv0eHNt8hOcpJ!l(}tApNqV4C99B*B+gOJuC=ox2cc2Z9{r9 z#pW#~5`#55kPbFfG5I#)qArX@?aoMUy z6HQpBcs{_y0T(FN2xN8Z!o73f(vZk=&NGpJRoIjl81>?>rgaCN@TUJ|s47P&O`rja6evi4dZQscTlVyk7Cf)VdPN!Yb$ovBc{& zp#@B|g2Z_ck6RPYX`YV>=N}YjENMA%ZWD4w?9D%1GWKGicib<9qqJIv;2u=gu$bp} z!abPTh%ny=<_>4h2$_dN;tTiYjI$v@9}~YCh$gnx=nx7ur{QdHc#FLY6FYb`XY{Uq z-!Sort2EJ~bL+0^8{w!!UAQZeoOGt^g2P+<8P)|%9|GxE6L4$dSgbBs?5=3527A~L zHjl0gv-;`8IK0KWfXOkC%-4k`$)i1=kfRqz9liT-^vvAa%J$oaR80IRli0M63)pKXHU{{LG7B=AI_=Mg}x8Qx<^vbX!bRTP2#=F`+AM_&z`?C{~tvM+{58O=FuBPtjwmYXoHnq3|F*wo{N z3QSZyNO>Fy9(Uu}TJyFcQ6ib0trO$+6%)@wqMYi&aZ@sh$5&sZt+wjiI`L*eHXK(n zcHM@9D!A^3RQwr^U6_6q(*LWm%gVFNcn0htr|H<$z>H%TCKq&NxgC{@Cfq_P?i@&; ziS^*2m#kYzhQ#;X8ck4c6}c(OewY}YBjXlXXCkX`q-o%6th3`Z%O!rb1 z;axy@rDrbog$7jc`k&$v6vjWtZ*{~GrLc>VYa26vYTqS9A*cKv9 z{DzE6j@B*T(&otL*DBqfoc?wGHni{z_Fw>xackmvobplP`5onX19+|xjtjGMpvQfK z5KGC=sDQUGJ?GKRF?qNG&6EQJlyfU{LAaH9UZSC*Fu!W=gizar_Xm_TMFcE5hD~?kt zgY6>(s=%fpkx1IYu<#3PM)3cA;qx5W=EC+f5{ye~Y!l@e%MH14h{hWS{xTGc?;nXb z1GT7f^AR0mVl5;dhF#;DZ=ApM-4+)FPIkO;*40ZTh`0964+U*Qby0sQtI? zRKgIL{!$`cztuBli$h4S$`@&|L(>-j-WOY(o1)A?)g~L|P*J(H@-a;Oqorsfl*0W} z8d{94)rt8Xju6+CQ-%LD8jcrysI^L;wGO%AkK=>}z=$V!tc)z5mVM%lljY`(pd!zsLX1fBMY- zzU*hQzu|w*`WgO?*a!c&{Abqh&_Cn4x_4Tf`lfU);td9F@21OD+VZ{&pZAH;Nh1MLWb}y`^^Fo_Y zjhUy1E&PA_kp?$?P)e{=ZcS}0zSQ6V!%a^|ES3Jvslma)vuwdmHx2c|J5kcXp&u!i zkZAovnKOcX+^9rFZqmf3m}q}aG_i{YcXhWgQAq(}q(jBgbY(6NzQ&C%>|DBXC>bhb zXDVQDT(A=cM{-Z@(Na*@`$0$_nFHlrbWf)ReD8Kxeblz7-8=sHfBOObsNoPbd!@{o z02gjegw9+&78^ZJmlC?mvuxo&w=U|{=Tf9X`{N;M6x#TI*bz2nDRT~L9NB2KYSzkO zxR>xV!ZCBg1{X(L?Cs!Vuv&AvnU}$H<)Qtf1`jv= zrmo*Rc|uO&_Gg8akDpg-y995^Ps|HkpFc6t)%@9Z(obNwf{#_TaUa6>bDaA?hb!a< zxBvcW^et6xLw|<{SBySeOrP|k6tdyb7EKmKd@^MGDlSgYrk zun=%u^?+`vN}OF^hnM8bDGAfoR9^~gHT=eixz{Hx8*yEK?$LSGgR>=jx7;7J{>if| zleR`aIUpN0X;6Ci0X;{W+{`pFUlguywQkxM%L==L6V^TP88K&Bb!u#t^&7k5q|~in zopTQOhs>=@JLQxxq^++`FWCl`oUv#XGr1^hRHxP{Ci%IKz1y33nyR00bWfYTy{BP8 zX>H=LO?Q+#tG(*-_rkO3BXld178iYW8CgJzhQ; z98osuueuu>V=s0Xwk$E)aQIETKl88H#hFHoK9Uqy99vQTC8M;1EGn^d1AhyZ*_Ry6UfIw0wsSoO9I2?w4qZ;kvwkBBLhNe|wkz;rWTE zrH_j)Y@QfVt(jPOp&<3tp+74fqRmgGoZT2PI)2fm@yRhS{31pg)}ML(^j>D^9;f9` z7kzVh(s_2Fb9mYEW&WjQTW!y-Px8ESE#mWy;u|4juJ=n=Qs*3cRM#eBX4mRD-*SGh z4yxRK=8&KDcvD&Dl}Be)o4tm=ic_JmmhfzFifes zn&UDd+;q0p`^YV&j(XYlnKnr|u9DY9S#g)*KK^m`s8w`aWL(*^3997@&wLX+@1Clx z$}<}93I2#K~-S^vlFI#J_cwC<`vG=ie z?;e?31(_8m)oHI>+`Z=f((bucX&*ytUcX1? zosMHgQGU|c9BY@luj%LYJ?x{}>ezwx3D$ghX^|qWj z=~uHI3f;^fk8zClj=uJGpLtn+Qj%>#-OHlPtHYC~53a4N%(3FB* zgV`HnVz-rKzjpe3N43AhoZP(X?XB;pWvk{z)wMi&{quH1?>vX1CqrX;tUFbDqaZTq zX!2x^;L(iUuj`QI6Op=DjNY2LGT6*qOitX$8*s4$0OAIo8F^jO*{u;4t zV_Zar{l)RgnK5^s`kU67T(>RjZ)34l0v*}-efmmQXFV5?^lqf8Y}L9Ms&x40=hC*L0gL}p)S>Ds;b_Hw%1z} z@M+dI%};RB>Ysn`aqIEYXJd{e8E(j)GQ;A>7?1M$9iAy>aqc7MB&<(9Hd^(U)Dx4U zuT^ANd52o(+^esvQ7!dLa9Wxcv-Z@do6x%omL5$>c~PG5cVFn#qn|vym{gcJFL3UJ z7b*IZ+OmS``kE43^Q5B-(?TQSpJvayWo~=s*zEV28QGJM)K`?vji}W!PCD}9qeayp z$yOUr7+$Zw^+We;mzrX`;>>61E1j3*rIouT*|to6AHO*vf6K3NCAr2)hX!kfzPqs9 z>%$VadQ~jEq#?uba2Lp6g|(SLo_n2c7D`^8N1~9#a1>WI}q@?(|tzlPpr}Bfh#GO0k%@ zq(l$uNN>#|5Zvw2^^*@R?LUUtJWcGtw#tujBK zVGqV;$@*nRdeKWRsnn56&d-zkbW8Owm$Wz5KVrAD!m;q0eQ`$ku=!3QR>R|?Bc^YS z`fA)WqNjGqXuk;4xXpPpuT`uVYGie0h5w8~iLPzf7z@YF@sS7ZqDwk{%z9aN{ex@F znO((|x2iv$J)8IKvWv^T%rn2HrVYtS$*s=Gw04NkeYae)EYRQ7{_mOF^HV)~Ih=mg zCi~}@pJFB1Peye=xihNg>`4`69pkiDbYJHBG&QX}#&%n1Xu;AP538@2N0fM*Wh{Ij zab;+MBr9^5OLr5AztgSXzP#-aIZN`7> zPuK;%wJ^T+?CYd>yT>qq9**w4J@<&+prKfY?+?D z@%5$H6*p~*w;mc^_;^sLQN{wt%Vkw9T7=te2RfTl-u9lMndi=X9jdFo`mAhZtjCIfs)tR9 z>S<|z>&t`gn#&z0uHAXJJbTyO{N&J;UoVtzf7~hJar(zHJC`;CUALX^3r)|+kc6hs zzfx0gW^;DUi|Fm$GbePp1vNi*{N8Anr-io8mjB${eIozPZ0BhO>b)lg-(Bcxx@=|d zr@h={uT09ubE!iz)V1rvY&`-^+Eq&?N3A)gw{?hfp;1nY%O$l1%j`0*+8Wm=*cZKe zo)|QC`0j-A6hqD7#R})0xt61%E;@Yu=o52mLfGc9)~^nKDp>fDJgsD#IKVzV(qP;CYQbl zo_XfjibXXO+t0n8ue+gFUTpe`?i(ibewgvx`r`I6QTMie35s#eK2@6Y5ud zubvlL5$i{fmYq&wtXtbknh0xg}QP4TZ@m@g{O+c)%N+2pER?{di=~POAvlTF7%w6^pX5)QRJ~Q#!WK696>Ay)_@oJFy?2p{pA2#JF2UFE6ydJwomFc}c=ErRljzKi>I$ zcymRL%bshjK4NYZ?9)CiRmo@p-0--Wel6$Pr@@`ksznng-kufWM>lbWv?m$ZD70TE0*N18nq09ZKkJ&$6{0 zQRu02QlW!DXpyk@&-~03dWr}gD-^o6BM5yWB6JW4ee)d%JqeALFZ78LHu_`Zf-LfI zIPeJwy@o;~>!m^mHx)XuoGJ7)5sLGbGWB`wH4yqjMCf4H6Zahm^@m2w7kY*~zi)Yy z>Y*<@%hoL;Lg8`nzco~|snAj1m_pAGp<{$X*DV8~*&;$UVNZR&1EJ;6X!$~u{jt%x zxClvRAxB^~2(3q<5#dsyLz)WptYr%Qg9sf>39SdAA1I;4P(z2no~C>ULhGQ>@`Y}H zjg8i8T#wsu%75@IGe3?(Pr@RGe|zXq5Lzs(p?|}ZF2-}2vqb19p-}VVAhc9O=up_x zs_#JPYy}mD&|bT+(Y|F=4=wd9TR)8moh}us)l}$1Wv0+`MCeGN(Dla3q!)l#Xh6mRV>Mp?XrG!$IgLVGXrt#}pbv zgbo)9wa^Bk-9&^AhdsUd4urmkM#~p^hm6FfiImW8AoMR3di;P?=!m95w{~I*Jx_!V zqlEqiLVpkuIs#VNa^Hc_=g?^RLKADS(eLrKld?VZ2hTFgEhscBTPjqisn84Em_jcQ zq1r;BmRmsRFiL0%oboy#w9j`Sv~72m&~0z9(fah>K7^%~3)T1r-#-ASi}8i7y=5jsFBbWBsB z8?~51V~J2rN~i>cnovSNLrH1XJi0B6P4&s7*KsT_qw^7lhva4un32M#~qvcOf=5utb&CNn5)`~X6!7Z%U1hEskVK69M%fJHmM025bv zQnYprv>IH{8!7E#psg03Pce`^m1}rHDnc7aXrGACx=^piK-$Gf`*x_R^rer~Ggn1U zKVTCk_Mny+A9dkr!!Jr{Hz6%_CisxF#^kwLsw`GA=^BkMnxf6R3t0_0VBItl5*4~O z<7PrWZcSMA<{Uy+b9}L%>{#l~vmS+Ou!erHYmG!=$SHG*q1+eD9SOM%tJjIuM;stnC!6*Lp?xSq>nD%4=NgvR zH)QfLHH4z|<7opIN@=%(*GJ&htHJ9pkjwCTlaM|TAq}QT6T$0NYi<|knke)-0J0ghw+QWh5!y(3w0(}Tye0==V(wdtHj<|eoF=8+0bUcq>rU=7 zS{Mbn4AKNbic@j)nRSjL{R_PAz(l1OmRB9}#A&A;MG6m@vBGL_{-*_`e+j*6KsJN+ zHle*ILVJaxea_SF$zgf@=7ovZKTx#r1h;`UaH5p<2zY%CUN>-`&_W>OGDzD zTBBadjQV;~6B9M)dzA+~Exe$Uudkut6<%nfID;!h3o9X)L3)pn-WDNEq)1UX#N$Uy)TUl_aWs)9^W{wU zX#;0VY0raKc)f{Y5AHo$NQGPm>3u?aON2CwB7F^B&tu~L)hw@-Co%Cx3M9&oTUk7* zf8&{jq_2fuk3u$s_5q>2DMFhskM_z5mec3sqnOcGo-Yy!7I2_xW#3oh4nBG#2|f2NUt^Z8l*;%7J%2+n3xBN^2aSj zQdurs6nYI(<4OHq2uKTrUUgvthe6xWyJX)qj`!M09u>EfjP_&(R+Cb?n z@U9~8S^!=zbHu_R3npn2@p?tXYY&PPo}%q4La(uqDBo)=xs$D@-u5A=2T$sEUg{M| zWgbysc3Vp(?K46fD?&R^9_?#*Wt{x`trIgbvF#|z>p-41u#1$o4r#&bX)XgvO|CLY zlL_f%5mGIRv=qG7VdBC#me9aE;c0inI%2~z@EXSbjk(9aGD)8kQao#^`WmE9kyhx$ z>~4vPd9^IBlgT~29gf*D*8@TNJgJ|Bl(ZF)RtUYC)-h>Q2(9d!bMke-fTFDg+Ez&G zr>n~7uTNAk@gsE?6J)^C2INa=y8vw^csBTc;~?wRh$r=%E+y>-q%}gXlO{81Ul3Xxz3EmW=_a$$M(A@#L4HB%V0oG10` zCnbf2eufgES7%oyExDJAjuN3Y7opWe+U9XAuj%B0SQgIY$o2wro;Ki=lon>*4JF{! zmwSP^9pjm#>BQ?f5mIX*sgV+R)y2fKkSKrLN|}O*Z|VAK&6D~m$dVc<3B9UaXVShR zv}Z+V?d8$-tz&teM~;+P1m)G9rwwqI(i%u4MoQq-n@dFtf50mg8CNcU6Vg9KNS!Fs z=HS%;6IEdzQ@+=}_L%6cK#@A}q`sL_(iuS7Tv6QudK#1T9U;Y8Xu7{9QlveA^e`s+LZW=HyNoe$#9)dvktg-V6{Td|)ovv1A@o|$ zrp+a^p(3;|<H%JTxInbf=OEK-9w9v@LYhU9 z_64tGUi-Nx%WE@BOg!J3@|wky`nHggh5>0`q1UQIOjkPBPQv4;x$-=w1^@d0A5LD`2!N=_t(2UF|l+B zMOws@`h1XjMbZI6ul7%vv>ymgw#)jvLWs`MUm=)SMr2+VG7G@{%uT5xv0 zu@$4fwur>UKhh{#U7psjNJ?7+UUk9iiw3V6of`iU7?IUkUkIs}2&q0rss~6qv^k|NdTNqxo$NcDtX)jBh2O9|}}5n2Ozv}PtOuQ$kAx=LI>Av2#9Y{1j{ z-QsE8VP4xv54`3yc>Myoj2=L);-e3XkQz~>lXPO-$-K7fB9_-Al9<1fA~oVkecA~~ zCked(cd^m`rHQ{Ofq)#{8yMotA;PnG{0DD)T04C{I zLW(OD>58?SBK--xcE!XGkSM>uhU~?}PSg|5;N?82_a~`WB>hR~bxBr`>-*K)V3Ew&VU3GF!qP7=59RFuRK|N6KS< zmdol3;}>J1BV7?9d1hbflUaA1iACm)Tr#@on8zfoC#1VYNY7EEi@`3LGrkCk^2Y?% z)0j9qnX-G1C-t5rwTq;SgigNLPVZvKIP?CClqZCrq4PLy;!(q+Z_yq^pEpb0C{Rt3+tsL}*{iqn#GT^7^AG zCT{6P(Z1wqee$HVuol{A6{%gt=yeC=GDw>dQoQ%1`)d|Ox(>XOwa~qySzcRi#Kb>Z zP^4Krsn-hu={lj;*^tekRVK79BDDGPX!F0Yyw+!7Vl2+>%3c}d^RzypJk- zlKYBYhm|r($r_btbV_?IqDU>kE1BI7fkgRrpwkm14Z(E@vZO^ksn>Y{sfEz%eKxHs z@#-W(TOp6OU}PIceck&5CJtRh(N^%ZJ|R-t3*glPymA~_Q`L7=8&(f!PDmX^NNXul z8}NDok5mXG%J*tZp3P6Ep3Mi>@}yn|1f(`XuPJQW7KGNJDQ$>~JlacJSzZtH!^FYV z40nhMPun=N<$ede+Q2n;g@Q6#FtcZRZAnPCi;${Oq?^F&9ZcNd#PT|GAtqY)6ZU`* zHJ;RKgVZaMZW4OUf^0?|Xhmp$6QS)y(Qf8x+h1jQeN5)cKH}Yx%-I#viKq2hFQxqp zXg7n`w+dtiVjko&NYx1GHWAVu6sbLs{)Jv!USoOfOP=<7QLEuYdhn!P(*&gULa!?z zn?c)}(Ata84wOf0_>JXtqYfq-%%;2!JUJ70v!0QQx)|l(jy5Uu)NZWL7Q$>T2Hk<=+E_7~v z``^`?A^H@lD|ju%MB_FM{|pV**d}*flg4erL?;!BRG%k3QYH0@q^^x#F?&B`GrYDX zw3|h|8pxxy)n|F_br%zT<0x7Kp4Mlhl(rUW!RuB9RV0mrTm~swOA)z}hNaU+aA^F*cu~wdZMl-biU-g|X3oWsZBxC7^{m$YqdrA*5C!q)rs+ zA@Hh+iF=wUF#2oNSxk(7N|8G8q(?RjNDm3UUW04~ZC65TDMITmk9PEQmeE{YXErN#+jpq0Z*8uy$I<#5z=!MX)t(Qgo$q; zQU18)M8=kJt0}MNc+w-P0@7fiSM{e%+TMf~uQlki>xw+unwE-;`udrS27glXSs_<= zT3@)D`nSKDgV$j2x{jNN7Jh1_$m#)o2J22y_i9V#o~PlxrtI~= z4W9IHu7EU5=(U#`leRCR#bZW$eISoE!ieSd1$oAkL#-tVdBD^9&XLmMTIewFYQ&90 zuU{dTac1=+q*%9U(nN~%BzU#M#DF<0uLWV4_~a$!HIXMhEPdALf%DoYg)TdJ8x3ABg4e;E8hTv_xeU@FgcRp>sUC1l zjUtT&uhE!T0EzPZYbo)1m-2c{jVC=kRzMmn^xAellXfVfT`WS|NgnM;UzXQ&TTFc2 zit^ftr}Zh5(&Ad^SnxUs{%tj|-~_o0uUdo@pF_}Idr+it;PnP3cJX6*byC8JJ*LHz9{M04O%QsW0oe@N;e>X9 z2(7L>T8(C`_f8qR0~4L9C|X^f)@LhEyQdv^O(0%dpaoaRWsr^_r1M2c^(j(#7p*<* zFtNQd>%CL*%rG&zBSosulOFOAkUkfBoekLxS{*_=PlVP$9_`OPS?`&!fSj+Zj44_J zp4O+mly*LNh4=h|{@NTZwCKfp7Xp=$gcM)fPS*h=iu4_LosWr&AW?pO9kdz~57J(Z zc+x{Z2uR-vy}n}8jv}D%*+RLMT%&}hEuVsvh zd$cH8d!E)iOiJqkUW>tNA5H;DTPUzz+pj|Ij-m}kNS!FsFW}V!6Zb=+e6L#-kaRLW zGnMt~#FKhn6p(%q)`6#N+VR9IK0T$+E_ZpfHB(t$CzfEM{Q-*Bou~DN=NAq0E_-~z z>lfmcLklZ@VtUmhq_adw4^X7=o-})WF)P-c~2V5 zR-4A8oj_=3iqQIr(E5#r#39!jXUM3hobdj_DOH&G53T~1?FD{>wyg%|H-e|#htCZw zFwtKoQRGRiK4F|8!szeTR`z`#=lJTx?5o8@wRjez(jH7aUqdna17m@khPuS}h%DnA G-~R*9_Hix% literal 0 HcmV?d00001 diff --git a/testing/btest/Traces/rfb/vncmac.pcap b/testing/btest/Traces/rfb/vncmac.pcap new file mode 100644 index 0000000000000000000000000000000000000000..026078185de32f2f9651a6f265dc966e06795324 GIT binary patch literal 8848 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