diff --git a/src/Sessions.cc b/src/Sessions.cc index 5c825218d5..d873b269fe 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -554,7 +554,13 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, else outer->Add(it->second); - DoNextInnerPacket(t, hdr, caplen, data, proto, outer); + int result = DoNextInnerPacket(t, hdr, caplen, data, proto, outer); + if ( result < 0 ) + reporter->Weird(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), + "truncated_inner_IP"); + else if ( result > 0 ) + reporter->Weird(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), + "inner_IP_payload_mismatch"); delete outer; Remove(f); @@ -675,19 +681,33 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, } } -void NetSessions::DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, - int caplen, const u_char* pkt, int proto, - const Encapsulation* outer_encap) +int NetSessions::DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, + int caplen, const u_char* const pkt, int proto, + const Encapsulation* outer) { IP_Hdr* inner_ip = 0; if ( proto == IPPROTO_IPV6 ) + { + if ( caplen < (int)sizeof(struct ip6_hdr) ) + return -1; inner_ip = new IP_Hdr((const struct ip6_hdr*) pkt, false, caplen); + } else if ( proto == IPPROTO_IPV4 ) + { + if ( caplen < (int)sizeof(struct ip) ) + return -1; inner_ip = new IP_Hdr((const struct ip*) pkt, false); + } else reporter->InternalError("Bad IP protocol version in DoNextInnerPacket"); + if ( (uint32)caplen != inner_ip->TotalLen() ) + { + delete inner_ip; + return (uint32)caplen < inner_ip->TotalLen() ? -1 : 1; + } + struct pcap_pkthdr fake_hdr; fake_hdr.caplen = fake_hdr.len = caplen; if ( hdr ) @@ -695,9 +715,10 @@ void NetSessions::DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, else fake_hdr.ts.tv_sec = fake_hdr.ts.tv_usec = 0; - DoNextPacket(t, &fake_hdr, inner_ip, pkt, 0, outer_encap); + DoNextPacket(t, &fake_hdr, inner_ip, pkt, 0, outer); delete inner_ip; + return 0; } bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen, diff --git a/src/Sessions.h b/src/Sessions.h index 45c1e0750a..c374dcb667 100644 --- a/src/Sessions.h +++ b/src/Sessions.h @@ -138,8 +138,29 @@ public: const IP_Hdr* ip_hdr, const u_char* const pkt, int hdr_size, const Encapsulation* encapsulation); - void DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, int caplen, - const u_char* pkt, int proto, const Encapsulation* outer_encap); + /** + * Wrapper that recurses on DoNextPacket for encapsulated IP packets, if + * they appear to be valid based on whether \a pkt is long enough to be an + * IP header and also that the payload length field of that header matches + * matches the actual length of \a pkt given by \a caplen. + * + * @param t Network time. + * @param hdr If the outer pcap header is available, this pointer can be set + * so that the fake pcap header passed to DoNextPacket will use + * the same timeval. The caplen and len fields of the fake pcap + * header are always set to \a caplen. + * @param caplen The length of \a pkt in bytes. + * @param pkt The inner IP packet data. + * @param proto Either IPPROTO_IPV6 or IPPROTO_IPV4 to indicate which IP + * protocol \a pkt corresponds to. + * @param outer_encap The encapsulation information for the inner IP packet. + * @return 0 If the inner IP packet was valid and passed to DoNextPacket, + * else -1 if the \a caplen was greater than the supposed IP + * packet's payload length field or 1 if \a caplen was less than + * the supposed IP packet's payload length. + */ + int DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, int caplen, + const u_char* const pkt, int proto, const Encapsulation* outer); unsigned int ConnectionMemoryUsage(); unsigned int ConnectionMemoryUsageConnVals(); diff --git a/src/Teredo.cc b/src/Teredo.cc index c97d4fb8af..c7131866f9 100644 --- a/src/Teredo.cc +++ b/src/Teredo.cc @@ -97,12 +97,10 @@ void Teredo_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, if ( ! te.Parse(data, len) ) { - ProtocolViolation("Bad Teredo encapsulation", (const char*)data, len); + ProtocolViolation("Bad Teredo encapsulation", (const char*) data, len); return; } - ProtocolConfirmation(); - const Encapsulation* e = Conn()->GetEncapsulation(); if ( e && e->Depth() >= BifConst::Tunnel::max_depth ) @@ -117,7 +115,14 @@ void Teredo_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, EncapsulatingConn ec(Conn(), BifEnum::Tunnel::TEREDO); outer->Add(ec); - sessions->DoNextInnerPacket(network_time, 0, len, te.InnerIP(), - IPPROTO_IPV6, outer); + int result = sessions->DoNextInnerPacket(network_time, 0, len, te.InnerIP(), + IPPROTO_IPV6, outer); + if ( result == 0 ) + ProtocolConfirmation(); + else if ( result < 0 ) + ProtocolViolation("Truncated Teredo", (const char*) data, len); + else + ProtocolViolation("Teredo payload length", (const char*) data, len); + delete outer; } diff --git a/src/ayiya-analyzer.pac b/src/ayiya-analyzer.pac index 2fb787a4e5..361d5f8c66 100644 --- a/src/ayiya-analyzer.pac +++ b/src/ayiya-analyzer.pac @@ -32,19 +32,46 @@ flow AYIYA_Flow reporter->Weird(c, "ayiya_tunnel_non_ip"); return false; } - - connection()->bro_analyzer()->ProtocolConfirmation(); + + if ( ${pdu.packet}.length() < (int)sizeof(struct ip) ) + { + connection()->bro_analyzer()->ProtocolViolation( + "Truncated AYIYA", (const char*) ${pdu.packet}.data(), + ${pdu.packet}.length()); + return false; + } + + const struct ip* ip = (const struct ip*) ${pdu.packet}.data(); + + if ( ( ${pdu.next_header} == IPPROTO_IPV6 && ip->ip_v != 6 ) || + ( ${pdu.next_header} == IPPROTO_IPV4 && ip->ip_v != 4) ) + { + connection()->bro_analyzer()->ProtocolViolation( + "AYIYA next header mismatch", (const char*)${pdu.packet}.data(), + ${pdu.packet}.length()); + return false; + } Encapsulation* outer = new Encapsulation(e); EncapsulatingConn ec(c, BifEnum::Tunnel::AYIYA); outer->Add(ec); - sessions->DoNextInnerPacket(network_time(), 0, ${pdu.packet}.length(), - ${pdu.packet}.data(), ${pdu.next_header}, - outer); + int result = sessions->DoNextInnerPacket(network_time(), 0, + ${pdu.packet}.length(), ${pdu.packet}.data(), + ${pdu.next_header}, outer); + if ( result == 0 ) + connection()->bro_analyzer()->ProtocolConfirmation(); + else if ( result < 0 ) + connection()->bro_analyzer()->ProtocolViolation( + "Truncated AYIYA", (const char*) ${pdu.packet}.data(), + ${pdu.packet}.length()); + else + connection()->bro_analyzer()->ProtocolViolation( + "AYIYA payload length", (const char*) ${pdu.packet}.data(), + ${pdu.packet}.length()); delete outer; - return true; + return (result == 0) ? true : false; %} };