Hello, During a review of OpenBSD Packet Filter, we discovered a minor security issue that also impacts netfilter.
When handling ICMP[v6] errors that encapsulate the original IP datagram, there is no correlation check between the inner and outer IP layers. So one can encapsulate an error with an inner layer matching a known connection, while its outer layer is directed to a filtered host. In this case the whole packet will be tagged with the IP_CT_RELATED flag. This has various implications from a rule bypass (if a rule allows related trafic), to a known state oracle. Unfortunately, we could not find a real statement in a RFC on how this case should be filtered. The closest we found is RFC5927 (Section 4.3) but it is not very clear. A possible fix would be to check that the inner IP source is the same than the outer destination. For information here is what OpenBSD applied: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/net/pf.c?rev=1.1081&content-type=text/x-cvsweb-markup http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/net/pf.c.diff?r1=1.1080&r2=1.1081&f=h We are totally disposed to discuss the vulnerability and help in the fix. If possible, we would also appreciate a citation for the discovery. Here is the relevant code: // /net/netfilter/nf_conntrack_proto_icmp.c > icmp_error_message(struct nf_conn *tmpl, struct sk_buff *skb, > const struct nf_hook_state *state) > { > // ... > > /* Are they talking about one of our connections? */ > if (!nf_ct_get_tuplepr(skb, > skb_network_offset(skb) + ip_hdrlen(skb) > + sizeof(struct icmphdr), > PF_INET, state->net, &origtuple)) { > pr_debug("icmp_error_message: failed to get tuple\n"); > return -NF_ACCEPT; > } > > /* rcu_read_lock()ed by nf_hook_thresh */ > innerproto = __nf_ct_l4proto_find(origtuple.dst.protonum); > > /* Ordinarily, we'd expect the inverted tupleproto, but it's > been preserved inside the ICMP. */ > if (!nf_ct_invert_tuple(&innertuple, &origtuple, innerproto)) { > pr_debug("icmp_error_message: no match\n"); > return -NF_ACCEPT; > } > > ctinfo = IP_CT_RELATED; > > h = nf_conntrack_find_get(state->net, zone, &innertuple); > if (!h) { > pr_debug("icmp_error_message: no match\n"); > return -NF_ACCEPT; > } > > if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) > ctinfo += IP_CT_IS_REPLY; > > /* Update skb to refer to this connection */ > nf_ct_set(skb, nf_ct_tuplehash_to_ctrack(h), ctinfo); > return NF_ACCEPT; > } Best Regards. Luca Moro Synacktiv