Add some extra length checking when parsing mobile ipv6 packets

Credit to OSS-Fuzz for discovery
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34263
(Link to details becomes public 30 days after patch release)
This commit is contained in:
Tim Wojtulewicz 2021-05-13 16:03:51 -07:00
parent 2c27f1bf34
commit 54271657a8
4 changed files with 57 additions and 22 deletions

View file

@ -619,6 +619,12 @@ void IPv6_Hdr_Chain::ProcessRoutingHeader(const struct ip6_rthdr* r, uint16_t le
#ifdef ENABLE_MOBILE_IPV6 #ifdef ENABLE_MOBILE_IPV6
void IPv6_Hdr_Chain::ProcessDstOpts(const struct ip6_dest* d, uint16_t len) void IPv6_Hdr_Chain::ProcessDstOpts(const struct ip6_dest* d, uint16_t len)
{ {
// Skip two bytes to get the beginning of the first option structure. These
// two bytes are the protocol for the next header and extension header length,
// already known to exist before calling this method. See header format:
// https://datatracker.ietf.org/doc/html/rfc8200#section-4.6
assert(len >= 2);
const u_char* data = (const u_char*) d; const u_char* data = (const u_char*) d;
len -= 2 * sizeof(uint8_t); len -= 2 * sizeof(uint8_t);
data += 2* sizeof(uint8_t); data += 2* sizeof(uint8_t);
@ -627,34 +633,45 @@ void IPv6_Hdr_Chain::ProcessDstOpts(const struct ip6_dest* d, uint16_t len)
{ {
const struct ip6_opt* opt = (const struct ip6_opt*) data; const struct ip6_opt* opt = (const struct ip6_opt*) data;
switch ( opt->ip6o_type ) { switch ( opt->ip6o_type ) {
case 201: // Home Address Option, Mobile IPv6 RFC 6275 section 6.3 case 0:
{ // If option type is zero, it's a Pad0 and can be just a single
if ( opt->ip6o_len == 16 ) // byte in width. Skip over it.
if ( homeAddr )
reporter->Weird(SrcAddr(), DstAddr(), "multiple_home_addr_opts");
else
homeAddr = new IPAddr(*((const in6_addr*)(data + 2)));
else
reporter->Weird(SrcAddr(), DstAddr(), "bad_home_addr_len");
}
break;
default:
break;
}
if ( opt->ip6o_type == 0 )
{
data += sizeof(uint8_t); data += sizeof(uint8_t);
len -= sizeof(uint8_t); len -= sizeof(uint8_t);
} break;
else default:
{ {
data += 2 * sizeof(uint8_t) + opt->ip6o_len; // Double-check that the len can hold the whole option structure.
len -= 2 * sizeof(uint8_t) + opt->ip6o_len; // Otherwise we get a buffer-overflow when we check the option_len.
// Also check that it holds everything for the option itself.
if ( len < sizeof(struct ip6_opt) ||
len < sizeof(struct ip6_opt) + opt->ip6o_len )
{
reporter->Weird(SrcAddr(), DstAddr(), "bad_ipv6_dest_opt_len");
len = 0;
break;
}
if ( opt->ip6o_type == 201 ) // Home Address Option, Mobile IPv6 RFC 6275 section 6.3
{
if ( opt->ip6o_len == sizeof(struct in6_addr) )
{
if ( homeAddr )
reporter->Weird(SrcAddr(), DstAddr(), "multiple_home_addr_opts");
else
homeAddr = new IPAddr(*((const in6_addr*)(data + sizeof(struct ip6_opt))));
}
else
reporter->Weird(SrcAddr(), DstAddr(), "bad_home_addr_len");
}
data += sizeof(struct ip6_opt) + opt->ip6o_len;
len -= sizeof(struct ip6_opt) + opt->ip6o_len;
} }
break;
} }
} }
}
#endif #endif
VectorValPtr IPv6_Hdr_Chain::ToVal() const VectorValPtr IPv6_Hdr_Chain::ToVal() const

View file

@ -0,0 +1,14 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path weird
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p name addl notice peer source
#types time string addr port addr port string string bool string string
XXXXXXXXXX.XXXXXX - 2001:4f8:4:7:2e0:81ff:fe52:ffff 0 2001:4f8:4:7:2e0:81ff:fe52:9a6b 0 bad_ipv6_dest_opt_len - F zeek -
XXXXXXXXXX.XXXXXX - 2001:78:1:32::1 0 2001:4f8:4:7:2e0:81ff:fe52:9a6b 0 multiple_home_addr_opts - F zeek -
XXXXXXXXXX.XXXXXX - 2001:78:1:32::1 0 2001:4f8:4:7:2e0:81ff:fe52:9a6b 0 invalid_IP_header_size - F zeek IP
XXXXXXXXXX.XXXXXX - 2001:4f8:4:7:2e0:81ff:fe52:ffff 0 2001:4f8:4:7:2e0:81ff:fe52:9a6b 0 bad_home_addr_len - F zeek -
#close XXXX-XX-XX-XX-XX-XX

Binary file not shown.

View file

@ -0,0 +1,4 @@
# @TEST-REQUIRES: grep -q "#define ENABLE_MOBILE_IPV6" $BUILD/zeek-config.h
#
# @TEST-EXEC: zeek -C -r $TRACES/ipv6-mobility-dst-opts.trace
# @TEST-EXEC: btest-diff weird.log