Add more sanity checks before recursing on encapsulated IP packets.

i.e. the IP protocol version and payload length get checked for
consistency before attempting further analysis.
This commit is contained in:
Jon Siwek 2012-06-05 09:58:44 -05:00
parent ae85bd1b95
commit 8540c4d0cd
4 changed files with 92 additions and 18 deletions

View file

@ -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,

View file

@ -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();

View file

@ -101,8 +101,6 @@ void Teredo_Analyzer::DeliverPacket(int len, const u_char* data, bool orig,
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(),
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;
}

View file

@ -33,18 +33,45 @@ flow AYIYA_Flow
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;
%}
};