diff --git a/CHANGES b/CHANGES index dea3e4b4a9..074ecf46b7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +3.2.0-dev.338 | 2020-04-02 18:29:23 -0700 + + * Add RDP over UDP analyzer (Anthony Kasza, Corelight) + 3.2.0-dev.336 | 2020-04-02 15:03:04 -0700 * Fix uses of GetBool in bifs to use GetTrue/GetFalse (Tim Wojtulewicz, Corelight) diff --git a/NEWS b/NEWS index 6cc946381b..34c42b7ca7 100644 --- a/NEWS +++ b/NEWS @@ -12,16 +12,22 @@ New Functionality - X509 Certificate caching: Zeek now caches certificates if they have (by default) been encountered - more than 10 times in 62 seconds. Information for cached certificates is - retained; if the certificate is encountered again it does not have to - be re-parsed and already existing information is used to raise the events. + more than 10 times in 62 seconds. Information for cached certificates is + retained; if the certificate is encountered again it does not have to + be re-parsed and already existing information is used to raise the events. - This should especially help with performance in environments where the - same certificates are seen very often. + This should especially help with performance in environments where the + same certificates are seen very often. - Certificate caching is very configureable; it is possible to disable the - feature, change the time intervals or even suppress X509 events. - For details see ``scripts/base/files/x509/main.zeek``. + Certificate caching is very configureable; it is possible to disable the + feature, change the time intervals or even suppress X509 events. + For details see ``scripts/base/files/x509/main.zeek``. + +- Add parsing support for Remote Desktop Protocol UDP Transport Extension + (RDPEUDP versions 1 and 2). This primarily only adds "rdpeudp" to + connection record service fields when an RDPEUDP session handhake is + detected, but also provides a few other events related to the RDPEUDP + connection establishment. Changed Functionality --------------------- diff --git a/VERSION b/VERSION index 715ee26f4d..60829bcd08 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.0-dev.336 +3.2.0-dev.338 diff --git a/doc b/doc index fbbcdd345d..26b923f841 160000 --- a/doc +++ b/doc @@ -1 +1 @@ -Subproject commit fbbcdd345d275036384f20e2f11f7a9d6d26f9d2 +Subproject commit 26b923f841f98238f68756e91083bef7a3004b0c diff --git a/scripts/base/protocols/rdp/dpd.sig b/scripts/base/protocols/rdp/dpd.sig index f8ebff34b9..a4e6f237bb 100644 --- a/scripts/base/protocols/rdp/dpd.sig +++ b/scripts/base/protocols/rdp/dpd.sig @@ -10,3 +10,12 @@ signature dpd_rdp_server { ip-proto == tcp payload /(.{5}\xd0|.*McDn)/ } + +signature dpd_rdpeudp_syn { + ip-proto == udp + payload-size <= 1232 + payload-size >= 1132 + payload /^\xff{4}.{2}.{1}\x01/ + enable "rdpeudp" +} + diff --git a/scripts/base/protocols/rdp/main.zeek b/scripts/base/protocols/rdp/main.zeek index 2d6fd833f7..993b6e1eb7 100644 --- a/scripts/base/protocols/rdp/main.zeek +++ b/scripts/base/protocols/rdp/main.zeek @@ -85,13 +85,15 @@ redef record connection += { rdp: Info &optional; }; -const ports = { 3389/tcp }; -redef likely_server_ports += { ports }; +const rdp_ports = { 3389/tcp }; +const rdpeudp_ports = { 3389/udp }; +redef likely_server_ports += { rdp_ports, rdpeudp_ports }; event zeek_init() &priority=5 { Log::create_stream(RDP::LOG, [$columns=RDP::Info, $ev=log_rdp, $path="rdp"]); - Analyzer::register_for_ports(Analyzer::ANALYZER_RDP, ports); + Analyzer::register_for_ports(Analyzer::ANALYZER_RDP, rdp_ports); + Analyzer::register_for_ports(Analyzer::ANALYZER_RDPEUDP, rdpeudp_ports); } function write_log(c: connection) diff --git a/src/analyzer/protocol/rdp/CMakeLists.txt b/src/analyzer/protocol/rdp/CMakeLists.txt index 67ad09c18c..7011373593 100644 --- a/src/analyzer/protocol/rdp/CMakeLists.txt +++ b/src/analyzer/protocol/rdp/CMakeLists.txt @@ -3,8 +3,9 @@ include(ZeekPlugin) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) zeek_plugin_begin(Zeek RDP) - zeek_plugin_cc(RDP.cc Plugin.cc) + zeek_plugin_cc(RDPEUDP.cc RDP.cc Plugin.cc) zeek_plugin_bif(events.bif) zeek_plugin_bif(types.bif) zeek_plugin_pac(rdp.pac rdp-analyzer.pac rdp-protocol.pac ../asn1/asn1.pac) + zeek_plugin_pac(rdpeudp.pac rdpeudp-analyzer.pac rdpeudp-protocol.pac) zeek_plugin_end() diff --git a/src/analyzer/protocol/rdp/Plugin.cc b/src/analyzer/protocol/rdp/Plugin.cc index c4771d6a5e..7d07d4c564 100644 --- a/src/analyzer/protocol/rdp/Plugin.cc +++ b/src/analyzer/protocol/rdp/Plugin.cc @@ -1,4 +1,5 @@ #include "RDP.h" +#include "RDPEUDP.h" #include "plugin/Plugin.h" #include "analyzer/Component.h" @@ -10,6 +11,7 @@ public: plugin::Configuration Configure() override { AddComponent(new ::analyzer::Component("RDP", ::analyzer::rdp::RDP_Analyzer::InstantiateAnalyzer)); + AddComponent(new ::analyzer::Component("RDPEUDP", ::analyzer::rdpeudp::RDP_Analyzer::InstantiateAnalyzer)); plugin::Configuration config; config.name = "Zeek::RDP"; diff --git a/src/analyzer/protocol/rdp/RDP.h b/src/analyzer/protocol/rdp/RDP.h index a991e06921..99bad567a2 100644 --- a/src/analyzer/protocol/rdp/RDP.h +++ b/src/analyzer/protocol/rdp/RDP.h @@ -1,11 +1,8 @@ #pragma once #include "events.bif.h" - - #include "analyzer/protocol/tcp/TCP.h" #include "analyzer/protocol/pia/PIA.h" - #include "rdp_pac.h" namespace analyzer { namespace rdp { diff --git a/src/analyzer/protocol/rdp/RDPEUDP.cc b/src/analyzer/protocol/rdp/RDPEUDP.cc new file mode 100644 index 0000000000..3cedd1eaa2 --- /dev/null +++ b/src/analyzer/protocol/rdp/RDPEUDP.cc @@ -0,0 +1,37 @@ +#include "RDPEUDP.h" +#include "Reporter.h" +#include "events.bif.h" +#include "rdpeudp_pac.h" + +using namespace analyzer::rdpeudp; + +RDP_Analyzer::RDP_Analyzer(Connection* c) + : analyzer::Analyzer("RDPEUDP", c) + { + interp = new binpac::RDPEUDP::RDPEUDP_Conn(this); + } + +RDP_Analyzer::~RDP_Analyzer() + { + delete interp; + } + +void RDP_Analyzer::Done() + { + Analyzer::Done(); + } + +void RDP_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, + uint64_t seq, const IP_Hdr* ip, int caplen) + { + Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); + + try + { + interp->NewData(orig, data, data + len); + } + catch ( const binpac::Exception& e ) + { + ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); + } + } diff --git a/src/analyzer/protocol/rdp/RDPEUDP.h b/src/analyzer/protocol/rdp/RDPEUDP.h new file mode 100644 index 0000000000..dc874ca783 --- /dev/null +++ b/src/analyzer/protocol/rdp/RDPEUDP.h @@ -0,0 +1,24 @@ +#pragma once + +#include "events.bif.h" +#include "analyzer/protocol/udp/UDP.h" +#include "rdpeudp_pac.h" + +namespace analyzer { namespace rdpeudp { +class RDP_Analyzer : public analyzer::Analyzer { + +public: + explicit RDP_Analyzer(Connection* conn); + ~RDP_Analyzer() override; + + void Done() override; + void DeliverPacket(int len, const u_char* data, bool orig, + uint64_t seq, const IP_Hdr* ip, int caplen) override; + static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn) + { return new RDP_Analyzer(conn); } + +protected: + binpac::RDPEUDP::RDPEUDP_Conn* interp; +}; + +} } diff --git a/src/analyzer/protocol/rdp/events.bif b/src/analyzer/protocol/rdp/events.bif index 178860bd42..7ba5a6a35b 100644 --- a/src/analyzer/protocol/rdp/events.bif +++ b/src/analyzer/protocol/rdp/events.bif @@ -1,3 +1,31 @@ +## Generated for RDPEUDP SYN UDP Datagram +## +## c: The connection record for the underlying transport-layer session/flow. +event rdpeudp_syn%(c: connection%); + +## Generated for RDPEUDP SYNACK UDP Datagram +## +## c: The connection record for the underlying transport-layer session/flow. +event rdpeudp_synack%(c: connection%); + +## Generated when RDPEUDP connections are established (both sides SYN) +## +## c: The connection record for the underlying transport-layer session/flow. +## +## version: Whether the connection is RDPEUDP1 or RDPEUDP2 +event rdpeudp_established%(c: connection, version: count%); + +## Generated when for data messages exchanged after a RDPEUDP connection establishes +## +## c: The connection record for the underlying transport-layer session/flow. +## +## is_orig: Whether the data was sent by the originator or responder of the connection. +## +## version: Whether the connection is RDPEUDP1 or RDPEUDP2 +## +## data: The payload of the packet. This is probably very non-performant. +event rdpeudp_data%(c: connection, is_orig: bool, version: count, data: string%); + ## Generated for each packet after RDP native encryption begins ## ## c: The connection record for the underlying transport-layer session/flow. diff --git a/src/analyzer/protocol/rdp/rdpeudp-analyzer.pac b/src/analyzer/protocol/rdp/rdpeudp-analyzer.pac new file mode 100644 index 0000000000..561107c012 --- /dev/null +++ b/src/analyzer/protocol/rdp/rdpeudp-analyzer.pac @@ -0,0 +1,119 @@ +refine connection RDPEUDP_Conn += { + %member{ + enum RDPEUDP_STATE { + NEED_SYN = 0x1, + NEED_SYNACK = 0x2, + NED_ACK = 0x3, + ESTABLISHED = 0x4, + }; + + uint8 state_ = NEED_SYN; + bool orig_rdpeudp2_ = false; + bool resp_rdpeudp2_ = false; + bool orig_lossy_ = false; + bool resp_lossy_ = false; + %} + + function get_state(): uint8 + %{ + return state_; + %} + + function is_version2(): bool + %{ + return (orig_rdpeudp2_ && resp_rdpeudp2_); + %} + + function proc_rdpeudp_syn(is_orig: bool, uFlags: uint16, snSourceAck: uint32): bool + %{ + if ( ! is_orig ) + return false; + + if ( (uFlags & 0x01) == 0 ) + return false; + + if ( snSourceAck != 0xffffffff ) + return false; + + if ( uFlags >= 0x1000 ) + orig_rdpeudp2_ = true; + + if ( (uFlags & 0x0200) == 0x0200 ) + orig_lossy_ = true; + + if ( rdpeudp_syn ) + BifEvent::generate_rdpeudp_syn(bro_analyzer(), bro_analyzer()->Conn()); + + state_ = NEED_SYNACK; + return true; + %} + + function proc_rdpeudp_synack(is_orig: bool, uFlags: uint16): bool + %{ + if ( is_orig ) + return false; + + if ( (uFlags & 0x05) == 0 ) + return false; + + if ( rdpeudp_synack ) + BifEvent::generate_rdpeudp_synack(bro_analyzer(), bro_analyzer()->Conn()); + + bro_analyzer()->ProtocolConfirmation(); + state_ = NEED_ACK; + + if ( uFlags >= 0x1000 ) + resp_rdpeudp2_ = true; + + if ( (uFlags & 0x0200) == 0x0200 ) + resp_lossy_ = true; + + return true; + %} + + function proc_rdpeudp1_ack(is_orig: bool, data: bytestring): bool + %{ + if ( state_ == NEED_ACK ) + { + state_ = ESTABLISHED; + + if ( rdpeudp_established ) + BifEvent::generate_rdpeudp_established(bro_analyzer(), bro_analyzer()->Conn(), 1); + } + + if ( state_ == ESTABLISHED && rdpeudp_data ) + BifEvent::generate_rdpeudp_data(bro_analyzer(), + bro_analyzer()->Conn(), + is_orig, + 1, + bytestring_to_val(data) + ); + + return true; + %} + + function proc_rdpeudp2_ack(is_orig: bool, pkt_type: uint8, data: bytestring): bool + %{ + if ( pkt_type == 8 ) + // This is a "dummy packet". + return false; + + if ( state_ == NEED_ACK ) + { + if ( rdpeudp_established ) + BifEvent::generate_rdpeudp_established(bro_analyzer(), bro_analyzer()->Conn(), 2); + + state_ = ESTABLISHED; + } + + if ( state_ == ESTABLISHED && rdpeudp_data ) + BifEvent::generate_rdpeudp_data(bro_analyzer(), + bro_analyzer()->Conn(), + is_orig, + 2, + bytestring_to_val(data) + ); + + return true; + %} +}; diff --git a/src/analyzer/protocol/rdp/rdpeudp-protocol.pac b/src/analyzer/protocol/rdp/rdpeudp-protocol.pac new file mode 100644 index 0000000000..4e3b175330 --- /dev/null +++ b/src/analyzer/protocol/rdp/rdpeudp-protocol.pac @@ -0,0 +1,221 @@ +# There are basically 5 PDU types for RDUEUDP1 +# 1. SYN - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeudp/066f9acf-fd57-4f95-ab3f-334e748bab10 +# 2. SYNACK - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeudp/96eaa81a-ff42-40a2-884c-96b3834db6c8 +# 3. ACK (bare) - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeudp/facb0b31-63c6-44f4-aeec-03b5163aedae +# 4. ACK + FEC - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeudp/7eaacd17-7012-468f-aa00-6e629cb88df8 +# 5. ACK + Source - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeudp/427c5d29-6b08-4cdb-bbdf-a1ed09e76e2d +# There is basically 1 PDU type for RDPEUDP2. It has a bunch of optional fields indicated by flags. + + +enum RDPEUDP_STATE { + NEED_SYN = 0x1, + NEED_SYNACK = 0x2, + NEED_ACK = 0x3, + ESTABLISHED = 0x4, +}; + +type RDPEUDP_PDU(is_orig: bool) = record { + state: case $context.connection.get_state() of { + NEED_SYN -> need_syn: RDPEUDP_SYN(this, is_orig); + NEED_SYNACK -> need_synack: RDPEUDP_SYNACK(this, is_orig); + NEED_ACK -> need_ack: RDPEUDP_ACK(this, is_orig); + default -> established: RDPEUDP_ACK(this, is_orig); + }; +} &byteorder=bigendian; + +type RDPEUDP_SYN(pdu: RDPEUDP_PDU, is_orig: bool) = record { + fec_header: RDPUDP_FEC_HEADER; + syndata_payload: RDPUDP_SYNDATA_PAYLOAD; +# TODO: parse the rest of the SYN pkt fields +# RDPUDP_CORRELATION_ID_PAYLOAD +# RDPUDP_SYNEX_PAYLOAD +} &let { + proc_rdpeudp_syn: bool = $context.connection.proc_rdpeudp_syn(is_orig, fec_header.uFlags, fec_header.snSourceAck); +}; + +type RDPUDP_SYNDATA_PAYLOAD = record { + snInitialSequenceNumber: uint32; + uUpStreamMtu: uint16; + uDownStreamMtu: uint16; +}; + +type RDPEUDP_SYNACK(pdu: RDPEUDP_PDU, is_orig: bool) = record { + fec_header: RDPUDP_FEC_HEADER; +} &let { + proc_rdpeudp_synack: bool = $context.connection.proc_rdpeudp_synack(is_orig, fec_header.uFlags); +}; + +enum RDPUDP_FLAG { + RDPUDP_FLAG_SYN = 0x0001, + RDPUDP_FLAG_FIN = 0x0002, + RDPUDP_FLAG_ACK = 0x0004, + RDPUDP_FLAG_DATA = 0x0008, + RDPUDP_FLAG_FEC = 0x0010, + RDPUDP_FLAG_CN = 0x0020, + RDPUDP_FLAG_CWR = 0x0040, + RDPUDP_FLAG_SACK_OPTION = 0x0080, + RDPUDP_FLAG_ACK_OF_ACKS = 0x0100, + RDPUDP_FLAG_SYNLOSSY = 0x0200, + RDPUDP_FLAG_ACKDELAYED = 0x0400, + RDPUDP_FLAG_CORRELATION_ID = 0x800, + RDPUDP_FLAG_SYNEX = 0x1000 +}; + +type RDPEUDP_ACK(pdu: RDPEUDP_PDU, is_orig: bool) = record { + version: case ($context.connection.is_version2()) of { + true -> version2: RDPEUDP2_ACK(pdu, is_orig); + false -> version1: RDPEUDP1_ACK(pdu, is_orig); + }; +}; + +type RDPEUDP1_ACK(pdu: RDPEUDP_PDU, is_orig: bool) = record { + fec_header: RDPUDP_FEC_HEADER; + ack_vec_header: RDPUDP_ACK_VECTOR_HEADER; + ack_of_ackvec_header: case fec_header.uFlags & RDPUDP_FLAG_ACK_OF_ACKS of { + RDPUDP_FLAG_ACK_OF_ACKS -> some: RDPUDP_ACK_OF_ACKVECTOR_HEADER; + default -> no_ack_of_ackvec_header: empty; + }; + # This doesn't handle ACK+FEC packets, which have both RDPUDP_FLAG_DATA and RDPUDP_FLAG_FEC set + source_payload_header: case (fec_header.uFlags & RDPUDP_FLAG_DATA) of { + RDPUDP_FLAG_DATA -> some_source_payload: RDPUDP_SOURCE_PAYLOAD_HEADER; + default -> no_source_payload_header: empty; + }; + data: bytestring &restofdata; +} &let { + proc_rdpeudp1_ack: bool = $context.connection.proc_rdpeudp1_ack(is_orig, data); +}; + +type RDPUDP_SOURCE_PAYLOAD_HEADER = record { + snCoded: uint32; + snSourceStart: uint32; +}; + +type RDPUDP_ACK_OF_ACKVECTOR_HEADER = record { + snAckOfAcksSeqNum: uint32; +}; + +type RDPUDP_FEC_HEADER = record { + snSourceAck: uint32; + uReceiveWindowSize: uint16; + uFlags: uint16; +}; + +type RDPUDP_ACK_VECTOR_HEADER = record { + uAckVectorSize: uint16; + AckVectorElement: uint8[uAckVectorSize]; + pad: padding align 4; +}; + +# version 2 +type RDPEUDP2_ACK(pdu: RDPEUDP_PDU, is_orig: bool) = record { + PacketPrefixByte: uint8; + header: RDPUDP2_PACKET_HEADER; +# TODO: +# ack_payload: case ((header.Flags & ACK) > 0) of { +# true -> has_ack_p: RDPUDP2_ACK_PAYLOAD; +# false -> has_no_ack_p: empty; +# }; +# oversize_payload: case ((header.Flags & 0x040) > OVERHEADSIZE) of { +# true -> has_oversize: RDPUDP2_OVERSIZE_PAYLOAD; +# false -> has_no_oversize: empty; +# }; +# delay_ack_info_payload: case ((header.Flags & DELAYACKINFO) > 0) of { +# true -> has_ack_info_p: RDPUDP2_DELAYACKINFO_PAYLOAD; +# false -> has_no_ack_info_p: empty; +# }; +# ack_of_acks_payload: case ((header.Flags & AOA) > 0) of { +# true -> has_aoa_p: RDPUDP2_ACKOFACKS_PAYLOAD; +# false -> has_no_aoa_p: empty; +# }; +# data_header_payload: case ((header.Flags & DATA) > 0) of { +# true -> has_data_h: RDPUDP2_DATAHEADER_PAYLOAD; +# false -> has_no_data_h: empty; +# }; +# ack_vector_payload: case ((header.Flags & ACKVEC) > 0) of { +# true -> has_av_p: RDPUDP2_ACKVECTOR_PAYLOAD; +# false -> has_no_av_p: empty; +# }; +# data_body_payload: case ((header.Flags & DATA) > 0) of { +# true -> has_data_p: RDPUDP2_DATABODY_PAYLOAD; +# false -> has_no_data_p: empty; +# }; +# data_body_payload: RDPUDP2_DATABODY_PAYLOAD; + data: bytestring &restofdata; +} &let { + Reserved: uint8 = PacketPrefixByte & 0x80; + Packet_Type_Index: uint8 = PacketPrefixByte & 0x78; + Short_Packet_Length: uint8 = PacketPrefixByte & 0x07; +# proc_rdpeudp2_ack: bool = $context.connection.proc_rdpeudp2_ack(is_orig, Packet_Type_Index, data_body_payload.Data); + proc_rdpeudp2_ack: bool = $context.connection.proc_rdpeudp2_ack(is_orig, Packet_Type_Index, data); +}; + +type RDPUDP2_PACKET_HEADER = record { + everything: uint16; +} &let { + Flags: uint16 = everything & 0xfff0; # The high 12 + LogWindowSize: uint8 = everything & 0x000f; # The low 4 +}; + +enum RDPUDP2_PACKET_HEADER_FLAGS { + ACK = 0x001, + DATA = 0x004, + ACKVEC = 0x008, + AOA = 0x010, + OVERHEADSIZE = 0x040, + DELAYACKINFO = 0x100 +}; + +type RDPUDP2_ACK_PAYLOAD = record { + SeqNum: uint16; + tmp1: uint32; + tmp2: uint8; + delayAckTimeAdditions: uint8[numDelayedAcks]; +} &let { + receivedTS: uint8 = tmp1 & 0xffffff00; + sendAckTimeGap: uint8 = tmp1 & 0xff; + numDelayedAcks: uint8 = tmp2 & 0xf0; # top 4 bits + delayAckTimeScale: uint8 = tmp2 & 0x0f; # bottom 4 bits +}; + +type RDPUDP2_OVERSIZE_PAYLOAD = record{ + OverheadSize: uint8; +}; + +type RDPUDP2_DATABODY_PAYLOAD = record { + ChannelSeqNum: uint16; + Data: bytestring &restofdata; +}; + +type RDPUDP2_ACKVECTOR_PAYLOAD = record { + BaseSeqNum: uint16; + tmp1: uint8; + TimeStamp_or_not: case TimeStampPresent of { + 0 -> none: empty; + default -> some: RDPUDP2_ACKVECTOR_PAYLOAD_TimeStamp; + } &requires(TimeStampPresent); + codedAckVector: uint8[codedAckVecSize]; +} &let { + codedAckVecSize: uint8 = tmp1 & 0xfe; + TimeStampPresent: uint8 = tmp1 & 0x01; +}; + +type RDPUDP2_ACKVECTOR_PAYLOAD_TimeStamp = record { + tmp1: uint8; + tmp2: uint8; + tmp3: uint8; +} &let { + TimeStamp: uint32 = tmp3 | (tmp2 << 8) | (tmp1 << 16); +}; + +type RDPUDP2_DATAHEADER_PAYLOAD = record { + DataSeqNum: uint16; +}; + +type RDPUDP2_ACKOFACKS_PAYLOAD = record { + AckOfAcksSeqNum: uint16; +}; + +type RDPUDP2_DELAYACKINFO_PAYLOAD = record { + MaxDelayedAcks: uint8; + DelayedAckTimeoutInMs: uint16; +}; diff --git a/src/analyzer/protocol/rdp/rdpeudp.pac b/src/analyzer/protocol/rdp/rdpeudp.pac new file mode 100644 index 0000000000..525c3b05ee --- /dev/null +++ b/src/analyzer/protocol/rdp/rdpeudp.pac @@ -0,0 +1,24 @@ +%include binpac.pac +%include bro.pac + +%extern{ + #include "events.bif.h" +%} + +analyzer RDPEUDP withcontext { + connection: RDPEUDP_Conn; + flow: RDPEUDP_Flow; +}; + +connection RDPEUDP_Conn(bro_analyzer: BroAnalyzer) { + upflow = RDPEUDP_Flow(true); + downflow = RDPEUDP_Flow(false); +}; + +%include rdpeudp-protocol.pac + +flow RDPEUDP_Flow(is_orig: bool) { + datagram = RDPEUDP_PDU(is_orig) withcontext(connection, this); +}; + +%include rdpeudp-analyzer.pac diff --git a/testing/btest/Baseline/core.print-bpf-filters/output2 b/testing/btest/Baseline/core.print-bpf-filters/output2 index 9f2e8a5002..7869f2a0fb 100644 --- a/testing/btest/Baseline/core.print-bpf-filters/output2 +++ b/testing/btest/Baseline/core.print-bpf-filters/output2 @@ -17,7 +17,7 @@ 1 2811 1 3128 1 3306 -1 3389 +2 3389 1 3544 1 4011 2 443 @@ -56,8 +56,8 @@ 1 992 1 993 1 995 -63 and -62 or -63 port +64 and +63 or +64 port 42 tcp -21 udp +22 udp diff --git a/testing/btest/Baseline/plugins.hooks/output b/testing/btest/Baseline/plugins.hooks/output index 032c79f025..3bb2f44cec 100644 --- a/testing/btest/Baseline/plugins.hooks/output +++ b/testing/btest/Baseline/plugins.hooks/output @@ -38,6 +38,7 @@ 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_NTP, 123/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_RADIUS, 1812/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_RDP, 3389/tcp)) -> +0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_RDPEUDP, 3389/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_SIP, 5060/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_SMB, 139/tcp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_SMB, 445/tcp)) -> @@ -103,6 +104,7 @@ 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_NTP, 123/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_RADIUS, 1812/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_RDP, 3389/tcp)) -> +0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_RDPEUDP, 3389/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_SIP, 5060/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_SMB, 139/tcp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_SMB, 445/tcp)) -> @@ -146,6 +148,7 @@ 0.000000 MetaHookPost CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_NTP, {123/udp})) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_RADIUS, {1812/udp})) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_RDP, {3389/tcp})) -> +0.000000 MetaHookPost CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_RDPEUDP, {3389/udp})) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_SIP, {5060/udp})) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_SMB, {139<...>/tcp})) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_SMTP, {587<...>/tcp})) -> @@ -279,7 +282,7 @@ 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql])) -> -0.000000 MetaHookPost CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1584045167.489534, node=zeek, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1585876138.391469, node=zeek, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Broker::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Cluster::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Config::LOG)) -> @@ -460,7 +463,7 @@ 0.000000 MetaHookPost CallFunction(Log::create_stream, , (Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql])) -> -0.000000 MetaHookPost CallFunction(Log::write, , (PacketFilter::LOG, [ts=1584045167.489534, node=zeek, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::write, , (PacketFilter::LOG, [ts=1585876138.391469, node=zeek, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(NetControl::check_plugins, , ()) -> 0.000000 MetaHookPost CallFunction(NetControl::init, , ()) -> 0.000000 MetaHookPost CallFunction(Notice::want_pp, , ()) -> @@ -951,6 +954,7 @@ 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_NTP, 123/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_RADIUS, 1812/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_RDP, 3389/tcp)) +0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_RDPEUDP, 3389/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_SIP, 5060/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_SMB, 139/tcp)) 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_SMB, 445/tcp)) @@ -1016,6 +1020,7 @@ 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_NTP, 123/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_RADIUS, 1812/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_RDP, 3389/tcp)) +0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_RDPEUDP, 3389/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_SIP, 5060/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_SMB, 139/tcp)) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_SMB, 445/tcp)) @@ -1059,6 +1064,7 @@ 0.000000 MetaHookPre CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_NTP, {123/udp})) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_RADIUS, {1812/udp})) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_RDP, {3389/tcp})) +0.000000 MetaHookPre CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_RDPEUDP, {3389/udp})) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_SIP, {5060/udp})) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_SMB, {139<...>/tcp})) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_ports, , (Analyzer::ANALYZER_SMTP, {587<...>/tcp})) @@ -1192,7 +1198,7 @@ 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql])) -0.000000 MetaHookPre CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1584045167.489534, node=zeek, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1585876138.391469, node=zeek, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Broker::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Cluster::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Config::LOG)) @@ -1373,7 +1379,7 @@ 0.000000 MetaHookPre CallFunction(Log::create_stream, , (Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql])) -0.000000 MetaHookPre CallFunction(Log::write, , (PacketFilter::LOG, [ts=1584045167.489534, node=zeek, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::write, , (PacketFilter::LOG, [ts=1585876138.391469, node=zeek, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(NetControl::check_plugins, , ()) 0.000000 MetaHookPre CallFunction(NetControl::init, , ()) 0.000000 MetaHookPre CallFunction(Notice::want_pp, , ()) @@ -1864,6 +1870,7 @@ 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_NTP, 123/udp) 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_RADIUS, 1812/udp) 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_RDP, 3389/tcp) +0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_RDPEUDP, 3389/udp) 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_SIP, 5060/udp) 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_SMB, 139/tcp) 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_SMB, 445/tcp) @@ -1929,6 +1936,7 @@ 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_NTP, 123/udp) 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_RADIUS, 1812/udp) 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_RDP, 3389/tcp) +0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_RDPEUDP, 3389/udp) 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_SIP, 5060/udp) 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_SMB, 139/tcp) 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_SMB, 445/tcp) @@ -1972,6 +1980,7 @@ 0.000000 | HookCallFunction Analyzer::register_for_ports(Analyzer::ANALYZER_NTP, {123/udp}) 0.000000 | HookCallFunction Analyzer::register_for_ports(Analyzer::ANALYZER_RADIUS, {1812/udp}) 0.000000 | HookCallFunction Analyzer::register_for_ports(Analyzer::ANALYZER_RDP, {3389/tcp}) +0.000000 | HookCallFunction Analyzer::register_for_ports(Analyzer::ANALYZER_RDPEUDP, {3389/udp}) 0.000000 | HookCallFunction Analyzer::register_for_ports(Analyzer::ANALYZER_SIP, {5060/udp}) 0.000000 | HookCallFunction Analyzer::register_for_ports(Analyzer::ANALYZER_SMB, {139<...>/tcp}) 0.000000 | HookCallFunction Analyzer::register_for_ports(Analyzer::ANALYZER_SMTP, {587<...>/tcp}) @@ -2104,7 +2113,7 @@ 0.000000 | HookCallFunction Log::__create_stream(Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird]) 0.000000 | HookCallFunction Log::__create_stream(X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509]) 0.000000 | HookCallFunction Log::__create_stream(mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql]) -0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1584045167.489534, node=zeek, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1585876138.391469, node=zeek, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Log::add_default_filter(Broker::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Cluster::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Config::LOG) @@ -2285,7 +2294,7 @@ 0.000000 | HookCallFunction Log::create_stream(Weird::LOG, [columns=Weird::Info, ev=Weird::log_weird, path=weird]) 0.000000 | HookCallFunction Log::create_stream(X509::LOG, [columns=X509::Info, ev=X509::log_x509, path=x509]) 0.000000 | HookCallFunction Log::create_stream(mysql::LOG, [columns=MySQL::Info, ev=MySQL::log_mysql, path=mysql]) -0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1584045167.489534, node=zeek, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1585876138.391469, node=zeek, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction NetControl::check_plugins() 0.000000 | HookCallFunction NetControl::init() 0.000000 | HookCallFunction Notice::want_pp() @@ -2732,7 +2741,7 @@ 0.000000 | HookLoadFile base<...>/xmpp 0.000000 | HookLoadFile base<...>/zeek.bif.zeek 0.000000 | HookLogInit packet_filter 1/1 {ts (time), node (string), filter (string), init (bool), success (bool)} -0.000000 | HookLogWrite packet_filter [ts=1584045167.489534, node=zeek, filter=ip or not ip, init=T, success=T] +0.000000 | HookLogWrite packet_filter [ts=1585876138.391469, node=zeek, filter=ip or not ip, init=T, success=T] 0.000000 | HookQueueEvent NetControl::init() 0.000000 | HookQueueEvent filter_change_tracking() 0.000000 | HookQueueEvent zeek_init() diff --git a/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-fail/conn.log b/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-fail/conn.log new file mode 100644 index 0000000000..1765803c64 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-fail/conn.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path conn +#open 2020-04-03-01-14-11 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig local_resp missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents +#types time string addr port addr port enum string interval count count string bool bool count string count count count count set[string] +1580420028.819704 CHhAvVGS1DHFjwGM9 192.168.38.1 63568 192.168.38.102 3389 udp - 6.226782 3696 0 S0 - - 0 D 3 3780 0 0 - +#close 2020-04-03-01-14-11 diff --git a/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-fail/out b/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-fail/out new file mode 100644 index 0000000000..ac74d8ba7c --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-fail/out @@ -0,0 +1 @@ +rdpeudp_syn, [orig_h=192.168.38.1, orig_p=63568/udp, resp_h=192.168.38.102, resp_p=3389/udp] diff --git a/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-success/conn.log b/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-success/conn.log new file mode 100644 index 0000000000..4e23c22e09 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-success/conn.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path conn +#open 2020-04-03-01-14-12 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig local_resp missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents +#types time string addr port addr port enum string interval count count string bool bool count string count count count count set[string] +1564522833.059088 CHhAvVGS1DHFjwGM9 ::1 61291 ::1 3389 udp rdpeudp 0.122551 1738 2655 SF - - 0 Dd 5 1978 5 2895 - +#close 2020-04-03-01-14-12 diff --git a/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-success/out b/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-success/out new file mode 100644 index 0000000000..f825baa964 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.rdp.rdpeudp-handshake-success/out @@ -0,0 +1,2 @@ +rdpeudp_syn, [orig_h=::1, orig_p=61291/udp, resp_h=::1, resp_p=3389/udp] +rdpeudp_synack, [orig_h=::1, orig_p=61291/udp, resp_h=::1, resp_p=3389/udp] diff --git a/testing/btest/Traces/rdp/rdpeudp-handshake-fail.pcap b/testing/btest/Traces/rdp/rdpeudp-handshake-fail.pcap new file mode 100644 index 0000000000..253367ac54 Binary files /dev/null and b/testing/btest/Traces/rdp/rdpeudp-handshake-fail.pcap differ diff --git a/testing/btest/Traces/rdp/rdpeudp-handshake-success.pcap b/testing/btest/Traces/rdp/rdpeudp-handshake-success.pcap new file mode 100644 index 0000000000..b6a984f1f5 Binary files /dev/null and b/testing/btest/Traces/rdp/rdpeudp-handshake-success.pcap differ diff --git a/testing/btest/scripts/base/protocols/rdp/rdpeudp-handshake-fail.zeek b/testing/btest/scripts/base/protocols/rdp/rdpeudp-handshake-fail.zeek new file mode 100644 index 0000000000..39c355e849 --- /dev/null +++ b/testing/btest/scripts/base/protocols/rdp/rdpeudp-handshake-fail.zeek @@ -0,0 +1,25 @@ +# @TEST-EXEC: zeek -r $TRACES/rdp/rdpeudp-handshake-fail.pcap %INPUT >out +# @TEST-EXEC: btest-diff conn.log +# @TEST-EXEC: btest-diff out + +@load base/protocols/rdp + +event rdpeudp_syn(c: connection) + { + print "rdpeudp_syn", c$id; + } + +event rdpeudp_synack(c: connection) + { + print "rdpeudp_synack", c$id; + } + +event rdpeudp_established(c: connection, version: count) + { + print "rdpeudp_established", c$id, version; + } + +event rdpeudp_data(c: connection, is_orig: bool, version: count, data: string) + { + print fmt("rdpeudp_data is_orig: %s, version %d, data: %s", is_orig, version, data); + } diff --git a/testing/btest/scripts/base/protocols/rdp/rdpeudp-handshake-success.zeek b/testing/btest/scripts/base/protocols/rdp/rdpeudp-handshake-success.zeek new file mode 100644 index 0000000000..1ab87bd5bc --- /dev/null +++ b/testing/btest/scripts/base/protocols/rdp/rdpeudp-handshake-success.zeek @@ -0,0 +1,25 @@ +# @TEST-EXEC: zeek -r $TRACES/rdp/rdpeudp-handshake-success.pcap %INPUT >out +# @TEST-EXEC: btest-diff conn.log +# @TEST-EXEC: btest-diff out + +@load base/protocols/rdp + +event rdpeudp_syn(c: connection) + { + print "rdpeudp_syn", c$id; + } + +event rdpeudp_synack(c: connection) + { + print "rdpeudp_synack", c$id; + } + +event rdpeudp_established(c: connection, version: count) + { + print "rdpeudp_established", c$id, version; + } + +event rdpeudp_data(c: connection, is_orig: bool, version: count, data: string) + { + print fmt("rdpeudp_data is_orig: %s, version %d, data: %s", is_orig, version, data); + }