Skip to content

Commit 34ce324

Browse files
Daniel Borkmannummakynes
authored andcommitted
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 <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 9dcbe1b commit 34ce324

File tree

3 files changed

+16
-10
lines changed

3 files changed

+16
-10
lines changed

include/uapi/linux/netfilter/nf_nat.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
#include <linux/netfilter.h>
55
#include <linux/netfilter/nf_conntrack_tuple_common.h>
66

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)
1115

1216
struct nf_nat_ipv4_range {
1317
unsigned int flags;

net/netfilter/nf_nat_core.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
315315
* manips not an issue.
316316
*/
317317
if (maniptype == NF_NAT_MANIP_SRC &&
318-
!(range->flags & NF_NAT_RANGE_PROTO_RANDOM)) {
318+
!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL)) {
319319
/* try the original tuple first */
320320
if (in_range(l3proto, l4proto, orig_tuple, range)) {
321321
if (!nf_nat_used_tuple(orig_tuple, ct)) {
@@ -339,7 +339,7 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
339339
*/
340340

341341
/* 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)) {
343343
if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
344344
if (l4proto->in_range(tuple, maniptype,
345345
&range->min_proto,

net/netfilter/nf_nat_proto_common.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,24 @@ void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto,
7474
range_size = ntohs(range->max_proto.all) - min + 1;
7575
}
7676

77-
if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
77+
if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
7878
off = l3proto->secure_port(tuple, maniptype == NF_NAT_MANIP_SRC
7979
? tuple->dst.u.all
8080
: tuple->src.u.all);
81-
else
81+
} else if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
82+
off = prandom_u32();
83+
} else {
8284
off = *rover;
85+
}
8386

8487
for (i = 0; ; ++off) {
8588
*portptr = htons(min + off % range_size);
8689
if (++i != range_size && nf_nat_used_tuple(tuple, ct))
8790
continue;
88-
if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM))
91+
if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL))
8992
*rover = off;
9093
return;
9194
}
92-
return;
9395
}
9496
EXPORT_SYMBOL_GPL(nf_nat_l4proto_unique_tuple);
9597

0 commit comments

Comments
 (0)