diff --git a/scripts/base/frameworks/dpd/dpd.sig b/scripts/base/frameworks/dpd/dpd.sig index b1fb9e6f19..305383809d 100644 --- a/scripts/base/frameworks/dpd/dpd.sig +++ b/scripts/base/frameworks/dpd/dpd.sig @@ -156,6 +156,12 @@ signature dpd_ayiya { enable "ayiya" } +signature dpd_teredo { + ip-proto = udp + payload /^(\x00\x00)|(\x00\x01)|([\x60-\x6f])/ + enable "teredo" +} + signature dpd_socks_client { ip-proto == tcp # '32' is a rather arbitrary max length for the user name. diff --git a/scripts/base/frameworks/tunnels/__load__.bro b/scripts/base/frameworks/tunnels/__load__.bro index 3def3511f5..a10fe855df 100644 --- a/scripts/base/frameworks/tunnels/__load__.bro +++ b/scripts/base/frameworks/tunnels/__load__.bro @@ -1,4 +1 @@ @load ./main - -const ports = { 5072/udp } &redef; -redef dpd_config += { [ANALYZER_AYIYA] = [$ports = ports] }; diff --git a/scripts/base/frameworks/tunnels/main.bro b/scripts/base/frameworks/tunnels/main.bro index 743098cd6d..4076e79cd5 100644 --- a/scripts/base/frameworks/tunnels/main.bro +++ b/scripts/base/frameworks/tunnels/main.bro @@ -74,6 +74,14 @@ export { global active: table[conn_id] of Info = table() &synchronized &read_expire=24hrs &expire_func=expire; } +const ayiya_ports = { 5072/udp }; +redef dpd_config += { [ANALYZER_AYIYA] = [$ports = ayiya_ports] }; + +const teredo_ports = { 3544/udp }; +redef dpd_config += { [ANALYZER_TEREDO] = [$ports = teredo_ports] }; + +redef likely_server_ports += { ayiya_ports, teredo_ports }; + event bro_init() &priority=5 { Log::create_stream(Tunnel::LOG, [$columns=Info]); diff --git a/src/AYIYA.cc b/src/AYIYA.cc index ef845a5368..c525a73b6c 100644 --- a/src/AYIYA.cc +++ b/src/AYIYA.cc @@ -4,7 +4,6 @@ AYIYA_Analyzer::AYIYA_Analyzer(Connection* conn) : Analyzer(AnalyzerTag::AYIYA, conn) { interp = new binpac::AYIYA::AYIYA_Conn(this); - did_session_done = 0; } AYIYA_Analyzer::~AYIYA_Analyzer() @@ -15,9 +14,7 @@ AYIYA_Analyzer::~AYIYA_Analyzer() void AYIYA_Analyzer::Done() { Analyzer::Done(); - - if ( ! did_session_done ) - Event(udp_session_done); + Event(udp_session_done); } void AYIYA_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, int seq, const IP_Hdr* ip, int caplen) diff --git a/src/AYIYA.h b/src/AYIYA.h index bf1fb0bf2c..2122cafee6 100644 --- a/src/AYIYA.h +++ b/src/AYIYA.h @@ -16,14 +16,13 @@ public: { return new AYIYA_Analyzer(conn); } static bool Available() + // TODO: specific option to turn off AYIYA analysis { return BifConst::Tunnel::max_depth > 0; } protected: friend class AnalyzerTimer; void ExpireTimer(double t); - int did_session_done; - binpac::AYIYA::AYIYA_Conn* interp; }; diff --git a/src/Analyzer.cc b/src/Analyzer.cc index c72af2a44a..9e30da0066 100644 --- a/src/Analyzer.cc +++ b/src/Analyzer.cc @@ -38,6 +38,7 @@ #include "SSH.h" #include "SSL.h" #include "Syslog-binpac.h" +#include "Teredo.h" #include "ConnSizeAnalyzer.h" // Keep same order here as in AnalyzerTag definition! @@ -135,9 +136,9 @@ const Analyzer::Config Analyzer::analyzer_configs[] = { { AnalyzerTag::SOCKS, "SOCKS", SOCKS_Analyzer::InstantiateAnalyzer, SOCKS_Analyzer::Available, 0, false }, - //{ AnalyzerTag::Teredo, "Teredo", - // Teredo_Analyzer::InstantiateAnalyzer, - // Teredo_Analyzer::Available, 0, false }, + { AnalyzerTag::Teredo, "TEREDO", + Teredo_Analyzer::InstantiateAnalyzer, + Teredo_Analyzer::Available, 0, false }, { AnalyzerTag::File, "FILE", File_Analyzer::InstantiateAnalyzer, File_Analyzer::Available, 0, false }, diff --git a/src/AnalyzerTags.h b/src/AnalyzerTags.h index 05de68f2b3..c77c229458 100644 --- a/src/AnalyzerTags.h +++ b/src/AnalyzerTags.h @@ -36,7 +36,7 @@ namespace AnalyzerTag { // Decapsulation Analyzers AYIYA, SOCKS, - //Teredo, + Teredo, // Other File, Backdoor, InterConn, SteppingStone, TCPStats, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 50875cbcca..7d74aee1ce 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -397,6 +397,7 @@ set(bro_SRCS TCP_Endpoint.cc TCP_Reassembler.cc Telnet.cc + Teredo.cc Timer.cc Traverse.cc Trigger.cc diff --git a/src/Sessions.cc b/src/Sessions.cc index d3d5d294bc..704bb62a25 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -567,6 +567,19 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, return; } + case IPPROTO_NONE: + { + if ( encapsulation.LastType() == BifEnum::Tunnel::TEREDO ) + { + // TODO: raise bubble packet event + } + else + Weird("ipv6_no_next", hdr, pkt); + + Remove(f); + return; + } + default: Weird(fmt("unknown_protocol_%d", proto), hdr, pkt); Remove(f); @@ -682,6 +695,9 @@ bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen, case IPPROTO_IPV6: min_hdr_len = sizeof(struct ip6_hdr); break; + case IPPROTO_NONE: + min_hdr_len = 0; + break; case IPPROTO_ICMP: case IPPROTO_ICMPV6: default: diff --git a/src/Teredo.cc b/src/Teredo.cc new file mode 100644 index 0000000000..39ecef286f --- /dev/null +++ b/src/Teredo.cc @@ -0,0 +1,126 @@ +#include "Teredo.h" +#include "IP.h" +#include "Reporter.h" + +void Teredo_Analyzer::Done() + { + Analyzer::Done(); + Event(udp_session_done); + } + +bool TeredoEncapsulation::DoParse(const u_char* data, int& len, + bool found_origin, bool found_auth) + { + if ( len < 2 ) + { + reporter->Weird(conn, "truncated_Teredo"); + } + + uint16 tag = ntohs((*((const uint16*)data))); + + if ( tag == 0 ) + { + // Origin Indication + if ( found_origin ) + // can't have multiple origin indications + return false; + + if ( len < 8 ) + { + reporter->Weird(conn, "truncated_Teredo_origin_indication"); + return false; + } + + origin_indication = data; + len -= 8; + data += 8; + return DoParse(data, len, true, found_auth); + } + else if ( tag == 1 ) + { + // Authentication + if ( found_origin || found_auth ) + // can't have multiple authentication headers and can't come after + // an origin indication + return false; + + if ( len < 4 ) + { + reporter->Weird(conn, "truncated_Teredo_authentication"); + return false; + } + + uint8 id_len = data[2]; + uint8 au_len = data[3]; + uint16 tot_len = 4 + id_len + au_len + 8 + 1; + + if ( len < tot_len ) + { + reporter->Weird(conn, "truncated_Teredo_authentication"); + return false; + } + + auth = data; + len -= tot_len; + data += tot_len; + return DoParse(data, len, found_origin, true); + } + else if ( ((tag & 0xf000)>>12) == 6 ) + { + // IPv6 + if ( len < 40 ) + { + reporter->Weird(conn, "truncated_IPv6_in_Teredo"); + return false; + } + + if ( len - 40 != ntohs(((const struct ip6_hdr*)data)->ip6_plen) ) + { + reporter->Weird(conn, "Teredo_payload_len_mismatch"); + return false; + } + + inner_ip = data; + return true; + } + + return false; + } + +void Teredo_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, + int seq, const IP_Hdr* ip, int caplen) + { + Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); + + if ( Conn()->GetEncapsulation().Depth() >= BifConst::Tunnel::max_depth ) + { + reporter->Weird(Conn(), "tunnel_depth"); + return; + } + + TeredoEncapsulation te(Conn()); + + if ( ! te.Parse(data, len) ) + { + ProtocolViolation("Invalid Teredo encapsulation", (const char*)data, + len); + return; + } + + IP_Hdr inner_ip((const struct ip6_hdr*) te.InnerIP(), false, len); + + ProtocolConfirmation(); + + // TODO: raise Teredo-specific events + + struct pcap_pkthdr fake_hdr; + fake_hdr.caplen = fake_hdr.len = len; + fake_hdr.ts.tv_sec = fake_hdr.ts.tv_usec = 0; + + Encapsulation encap(Conn()->GetEncapsulation()); + EncapsulatingConn ec(Conn(), BifEnum::Tunnel::TEREDO); + encap.Add(ec); + + sessions->DoNextPacket(network_time, &fake_hdr, &inner_ip, te.InnerIP(), 0, + encap); + } diff --git a/src/Teredo.h b/src/Teredo.h new file mode 100644 index 0000000000..0662099233 --- /dev/null +++ b/src/Teredo.h @@ -0,0 +1,63 @@ +#ifndef Teredo_h +#define Teredo_h + +#include "Analyzer.h" +#include "NetVar.h" + +class Teredo_Analyzer : public Analyzer { +public: + Teredo_Analyzer(Connection* conn) : Analyzer(AnalyzerTag::Teredo, conn) + {} + + virtual ~Teredo_Analyzer() + {} + + virtual void Done(); + + virtual void DeliverPacket(int len, const u_char* data, bool orig, + int seq, const IP_Hdr* ip, int caplen); + + static Analyzer* InstantiateAnalyzer(Connection* conn) + { return new Teredo_Analyzer(conn); } + + static bool Available() + //TODO: specific option to turn off Teredo analysis? + { return BifConst::Tunnel::max_depth > 0; } + +protected: + friend class AnalyzerTimer; + void ExpireTimer(double t); +}; + +class TeredoEncapsulation { +public: + TeredoEncapsulation(Connection* c) + : inner_ip(0), origin_indication(0), auth(0), conn(c) + {} + + /** + * Returns whether input data parsed as a valid Teredo encapsulation type. + * If it was valid, the len argument is decremented appropriately. + */ + bool Parse(const u_char* data, int& len) + { return DoParse(data, len, false, false); } + + const u_char* InnerIP() const + { return inner_ip; } + + const u_char* OriginIndication() const + { return origin_indication; } + + const u_char* Authentication() const + { return auth; } + +protected: + bool DoParse(const u_char* data, int& len, bool found_orig, bool found_au); + + const u_char* inner_ip; + const u_char* origin_indication; + const u_char* auth; + Connection* conn; +}; + +#endif diff --git a/src/Tunnels.h b/src/Tunnels.h index b8d693ea59..0f9c4f4107 100644 --- a/src/Tunnels.h +++ b/src/Tunnels.h @@ -103,6 +103,11 @@ public: return conns ? conns->size() : 0; } + BifEnum::Tunnel::Type LastType() const + { + return conns ? (*conns)[conns->size()-1].type : BifEnum::Tunnel::NONE; + } + VectorVal* GetVectorVal() const { VectorVal* vv = new VectorVal(new VectorType( diff --git a/src/ayiya-protocol.pac b/src/ayiya-protocol.pac index 7801708c7d..328d44ece7 100644 --- a/src/ayiya-protocol.pac +++ b/src/ayiya-protocol.pac @@ -13,4 +13,4 @@ type PDU = record { signature_len = (signature_byte >> 4) * 4; auth = auth_and_op >> 4; op = auth_and_op & 0xF; -} &byteorder = littleendian; \ No newline at end of file +} &byteorder = littleendian; diff --git a/src/event.bif b/src/event.bif index 94ee923240..7e428aabdd 100644 --- a/src/event.bif +++ b/src/event.bif @@ -765,8 +765,9 @@ event udp_reply%(u: connection%); event udp_contents%(u: connection, is_orig: bool, contents: string%); ## Generated when a UDP session for a supported protocol has finished. Some of -## Bro's application-layer UDP analyzers flag the end of a session by raising this -## event. Currently, the analyzers for DNS, NTP, Netbios, and Syslog support this. +## Bro's application-layer UDP analyzers flag the end of a session by raising +## this event. Currently, the analyzers for DNS, NTP, Netbios, Syslog, AYIYA, +## and Teredo support this. ## ## u: The connection record for the corresponding UDP flow. ##