mirror of
https://github.com/zeek/zeek.git
synced 2025-10-08 09:38:19 +00:00
Add Teredo tunnel decapsulation.
Also fix header truncation check for IPv6 No Next header and add an "ipv6_no_next" weird for such packets that aren't tunneled over Teredo (which it calls "bubbles" and are used to create mappings in NATs).
This commit is contained in:
parent
0d7d74e11b
commit
8cd36f158b
14 changed files with 236 additions and 16 deletions
|
@ -156,6 +156,12 @@ signature dpd_ayiya {
|
||||||
enable "ayiya"
|
enable "ayiya"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signature dpd_teredo {
|
||||||
|
ip-proto = udp
|
||||||
|
payload /^(\x00\x00)|(\x00\x01)|([\x60-\x6f])/
|
||||||
|
enable "teredo"
|
||||||
|
}
|
||||||
|
|
||||||
signature dpd_socks_client {
|
signature dpd_socks_client {
|
||||||
ip-proto == tcp
|
ip-proto == tcp
|
||||||
# '32' is a rather arbitrary max length for the user name.
|
# '32' is a rather arbitrary max length for the user name.
|
||||||
|
|
|
@ -1,4 +1 @@
|
||||||
@load ./main
|
@load ./main
|
||||||
|
|
||||||
const ports = { 5072/udp } &redef;
|
|
||||||
redef dpd_config += { [ANALYZER_AYIYA] = [$ports = ports] };
|
|
||||||
|
|
|
@ -74,6 +74,14 @@ export {
|
||||||
global active: table[conn_id] of Info = table() &synchronized &read_expire=24hrs &expire_func=expire;
|
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
|
event bro_init() &priority=5
|
||||||
{
|
{
|
||||||
Log::create_stream(Tunnel::LOG, [$columns=Info]);
|
Log::create_stream(Tunnel::LOG, [$columns=Info]);
|
||||||
|
|
|
@ -4,7 +4,6 @@ AYIYA_Analyzer::AYIYA_Analyzer(Connection* conn)
|
||||||
: Analyzer(AnalyzerTag::AYIYA, conn)
|
: Analyzer(AnalyzerTag::AYIYA, conn)
|
||||||
{
|
{
|
||||||
interp = new binpac::AYIYA::AYIYA_Conn(this);
|
interp = new binpac::AYIYA::AYIYA_Conn(this);
|
||||||
did_session_done = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AYIYA_Analyzer::~AYIYA_Analyzer()
|
AYIYA_Analyzer::~AYIYA_Analyzer()
|
||||||
|
@ -15,8 +14,6 @@ AYIYA_Analyzer::~AYIYA_Analyzer()
|
||||||
void AYIYA_Analyzer::Done()
|
void AYIYA_Analyzer::Done()
|
||||||
{
|
{
|
||||||
Analyzer::Done();
|
Analyzer::Done();
|
||||||
|
|
||||||
if ( ! did_session_done )
|
|
||||||
Event(udp_session_done);
|
Event(udp_session_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,14 +16,13 @@ public:
|
||||||
{ return new AYIYA_Analyzer(conn); }
|
{ return new AYIYA_Analyzer(conn); }
|
||||||
|
|
||||||
static bool Available()
|
static bool Available()
|
||||||
|
// TODO: specific option to turn off AYIYA analysis
|
||||||
{ return BifConst::Tunnel::max_depth > 0; }
|
{ return BifConst::Tunnel::max_depth > 0; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class AnalyzerTimer;
|
friend class AnalyzerTimer;
|
||||||
void ExpireTimer(double t);
|
void ExpireTimer(double t);
|
||||||
|
|
||||||
int did_session_done;
|
|
||||||
|
|
||||||
binpac::AYIYA::AYIYA_Conn* interp;
|
binpac::AYIYA::AYIYA_Conn* interp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "SSH.h"
|
#include "SSH.h"
|
||||||
#include "SSL.h"
|
#include "SSL.h"
|
||||||
#include "Syslog-binpac.h"
|
#include "Syslog-binpac.h"
|
||||||
|
#include "Teredo.h"
|
||||||
#include "ConnSizeAnalyzer.h"
|
#include "ConnSizeAnalyzer.h"
|
||||||
|
|
||||||
// Keep same order here as in AnalyzerTag definition!
|
// Keep same order here as in AnalyzerTag definition!
|
||||||
|
@ -135,9 +136,9 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
|
||||||
{ AnalyzerTag::SOCKS, "SOCKS",
|
{ AnalyzerTag::SOCKS, "SOCKS",
|
||||||
SOCKS_Analyzer::InstantiateAnalyzer,
|
SOCKS_Analyzer::InstantiateAnalyzer,
|
||||||
SOCKS_Analyzer::Available, 0, false },
|
SOCKS_Analyzer::Available, 0, false },
|
||||||
//{ AnalyzerTag::Teredo, "Teredo",
|
{ AnalyzerTag::Teredo, "TEREDO",
|
||||||
// Teredo_Analyzer::InstantiateAnalyzer,
|
Teredo_Analyzer::InstantiateAnalyzer,
|
||||||
// Teredo_Analyzer::Available, 0, false },
|
Teredo_Analyzer::Available, 0, false },
|
||||||
|
|
||||||
{ AnalyzerTag::File, "FILE", File_Analyzer::InstantiateAnalyzer,
|
{ AnalyzerTag::File, "FILE", File_Analyzer::InstantiateAnalyzer,
|
||||||
File_Analyzer::Available, 0, false },
|
File_Analyzer::Available, 0, false },
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace AnalyzerTag {
|
||||||
// Decapsulation Analyzers
|
// Decapsulation Analyzers
|
||||||
AYIYA,
|
AYIYA,
|
||||||
SOCKS,
|
SOCKS,
|
||||||
//Teredo,
|
Teredo,
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
File, Backdoor, InterConn, SteppingStone, TCPStats,
|
File, Backdoor, InterConn, SteppingStone, TCPStats,
|
||||||
|
|
|
@ -397,6 +397,7 @@ set(bro_SRCS
|
||||||
TCP_Endpoint.cc
|
TCP_Endpoint.cc
|
||||||
TCP_Reassembler.cc
|
TCP_Reassembler.cc
|
||||||
Telnet.cc
|
Telnet.cc
|
||||||
|
Teredo.cc
|
||||||
Timer.cc
|
Timer.cc
|
||||||
Traverse.cc
|
Traverse.cc
|
||||||
Trigger.cc
|
Trigger.cc
|
||||||
|
|
|
@ -567,6 +567,19 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
return;
|
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:
|
default:
|
||||||
Weird(fmt("unknown_protocol_%d", proto), hdr, pkt);
|
Weird(fmt("unknown_protocol_%d", proto), hdr, pkt);
|
||||||
Remove(f);
|
Remove(f);
|
||||||
|
@ -682,6 +695,9 @@ bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen,
|
||||||
case IPPROTO_IPV6:
|
case IPPROTO_IPV6:
|
||||||
min_hdr_len = sizeof(struct ip6_hdr);
|
min_hdr_len = sizeof(struct ip6_hdr);
|
||||||
break;
|
break;
|
||||||
|
case IPPROTO_NONE:
|
||||||
|
min_hdr_len = 0;
|
||||||
|
break;
|
||||||
case IPPROTO_ICMP:
|
case IPPROTO_ICMP:
|
||||||
case IPPROTO_ICMPV6:
|
case IPPROTO_ICMPV6:
|
||||||
default:
|
default:
|
||||||
|
|
126
src/Teredo.cc
Normal file
126
src/Teredo.cc
Normal file
|
@ -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);
|
||||||
|
}
|
63
src/Teredo.h
Normal file
63
src/Teredo.h
Normal file
|
@ -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
|
|
@ -103,6 +103,11 @@ public:
|
||||||
return conns ? conns->size() : 0;
|
return conns ? conns->size() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BifEnum::Tunnel::Type LastType() const
|
||||||
|
{
|
||||||
|
return conns ? (*conns)[conns->size()-1].type : BifEnum::Tunnel::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
VectorVal* GetVectorVal() const
|
VectorVal* GetVectorVal() const
|
||||||
{
|
{
|
||||||
VectorVal* vv = new VectorVal(new VectorType(
|
VectorVal* vv = new VectorVal(new VectorType(
|
||||||
|
|
|
@ -765,8 +765,9 @@ event udp_reply%(u: connection%);
|
||||||
event udp_contents%(u: connection, is_orig: bool, contents: string%);
|
event udp_contents%(u: connection, is_orig: bool, contents: string%);
|
||||||
|
|
||||||
## Generated when a UDP session for a supported protocol has finished. Some of
|
## 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
|
## Bro's application-layer UDP analyzers flag the end of a session by raising
|
||||||
## event. Currently, the analyzers for DNS, NTP, Netbios, and Syslog support this.
|
## this event. Currently, the analyzers for DNS, NTP, Netbios, Syslog, AYIYA,
|
||||||
|
## and Teredo support this.
|
||||||
##
|
##
|
||||||
## u: The connection record for the corresponding UDP flow.
|
## u: The connection record for the corresponding UDP flow.
|
||||||
##
|
##
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue