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

Reply via email to