Commit 34ce324019e76f6d93768d68343a0e78f464d754

Authored by Daniel Borkmann
Committed by Pablo Neira Ayuso
1 parent 9dcbe1b87c

netfilter: nf_nat: add full port randomization support

We currently use prandom_u32() for allocation of ports in tcp bind(0)
and udp code. In case of plain SNAT we try to keep the ports as is
or increment on collision.

SNAT --random mode does use per-destination incrementing port
allocation. As a recent paper pointed out in [1] that this mode of
port allocation makes it possible to an attacker to find the randomly
allocated ports through a timing side-channel in a socket overloading
attack conducted through an off-path attacker.

So, NF_NAT_RANGE_PROTO_RANDOM actually weakens the port randomization
in regard to the attack described in this paper. As we need to keep
compatibility, add another flag called NF_NAT_RANGE_PROTO_RANDOM_FULLY
that would replace the NF_NAT_RANGE_PROTO_RANDOM hash-based port
selection algorithm with a simple prandom_u32() in order to mitigate
this attack vector. Note that the lfsr113's internal state is
periodically reseeded by the kernel through a local secure entropy
source.

More details can be found in [1], the basic idea is to send bursts
of packets to a socket to overflow its receive queue and measure
the latency to detect a possible retransmit when the port is found.
Because of increasing ports to given destination and port, further
allocations can be predicted. This information could then be used by
an attacker for e.g. for cache-poisoning, NS pinning, and degradation
of service attacks against DNS servers [1]:

  The best defense against the poisoning attacks is to properly
  deploy and validate DNSSEC; DNSSEC provides security not only
  against off-path attacker but even against MitM attacker. We hope
  that our results will help motivate administrators to adopt DNSSEC.
  However, full DNSSEC deployment make take significant time, and
  until that happens, we recommend short-term, non-cryptographic
  defenses. We recommend to support full port randomisation,
  according to practices recommended in [2], and to avoid
  per-destination sequential port allocation, which we show may be
  vulnerable to derandomisation attacks.

Joint work between Hannes Frederic Sowa and Daniel Borkmann.

 [1] https://sites.google.com/site/hayashulman/files/NIC-derandomisation.pdf
 [2] http://arxiv.org/pdf/1205.5190v1.pdf

Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

Showing 3 changed files with 16 additions and 10 deletions Side-by-side Diff

include/uapi/linux/netfilter/nf_nat.h
... ... @@ -4,10 +4,14 @@
4 4 #include <linux/netfilter.h>
5 5 #include <linux/netfilter/nf_conntrack_tuple_common.h>
6 6  
7   -#define NF_NAT_RANGE_MAP_IPS 1
8   -#define NF_NAT_RANGE_PROTO_SPECIFIED 2
9   -#define NF_NAT_RANGE_PROTO_RANDOM 4
10   -#define NF_NAT_RANGE_PERSISTENT 8
  7 +#define NF_NAT_RANGE_MAP_IPS (1 << 0)
  8 +#define NF_NAT_RANGE_PROTO_SPECIFIED (1 << 1)
  9 +#define NF_NAT_RANGE_PROTO_RANDOM (1 << 2)
  10 +#define NF_NAT_RANGE_PERSISTENT (1 << 3)
  11 +#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)
  12 +
  13 +#define NF_NAT_RANGE_PROTO_RANDOM_ALL \
  14 + (NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
11 15  
12 16 struct nf_nat_ipv4_range {
13 17 unsigned int flags;
net/netfilter/nf_nat_core.c
... ... @@ -315,7 +315,7 @@
315 315 * manips not an issue.
316 316 */
317 317 if (maniptype == NF_NAT_MANIP_SRC &&
318   - !(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) {
  318 + !(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL)) {
319 319 /* try the original tuple first */
320 320 if (in_range(l3proto, l4proto, orig_tuple, range)) {
321 321 if (!nf_nat_used_tuple(orig_tuple, ct)) {
... ... @@ -339,7 +339,7 @@
339 339 */
340 340  
341 341 /* Only bother mapping if it's not already in range and unique */
342   - if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) {
  342 + if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL)) {
343 343 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
344 344 if (l4proto->in_range(tuple, maniptype,
345 345 &range->min_proto,
net/netfilter/nf_nat_proto_common.c
... ... @@ -74,22 +74,24 @@
74 74 range_size = ntohs(range->max_proto.all) - min + 1;
75 75 }
76 76  
77   - if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
  77 + if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
78 78 off = l3proto->secure_port(tuple, maniptype == NF_NAT_MANIP_SRC
79 79 ? tuple->dst.u.all
80 80 : tuple->src.u.all);
81   - else
  81 + } else if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
  82 + off = prandom_u32();
  83 + } else {
82 84 off = *rover;
  85 + }
83 86  
84 87 for (i = 0; ; ++off) {
85 88 *portptr = htons(min + off % range_size);
86 89 if (++i != range_size && nf_nat_used_tuple(tuple, ct))
87 90 continue;
88   - if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM))
  91 + if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL))
89 92 *rover = off;
90 93 return;
91 94 }
92   - return;
93 95 }
94 96 EXPORT_SYMBOL_GPL(nf_nat_l4proto_unique_tuple);
95 97