Hide Forgot
The tcpmss_mangle_packet function in net/netfilter/xt_TCPMSS.c in the Linux kernel before 4.11, and 4.9.x before 4.9.36, allows remote attackers to cause a denial of service (use-after-free and memory corruption) or possibly have unspecified other impact by leveraging the presence of xt_TCPMSS in an iptables action. Due to the nature of the flaw, privilege escalation cannot be fully ruled out, although we believe it is unlikely. References: https://lkml.org/lkml/2017/4/2/13 https://marc.info/?t=149111917600001&r=1&w=2 http://patchwork.ozlabs.org/patch/746618/ An upsteam patch: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2638fd0f92d4397884fd991d8f4925cb3f081901
Created kernel tracking bugs for this issue: Affects: fedora-all [bug 1531136]
This was fixed for all Fedora releases with the 4.11 kernel series.
*** Bug 1542309 has been marked as a duplicate of this bug. ***
Statement: This issue affects the Linux kernel packages as shipped with Red Hat Enterprise Linux 5. This is not currently planned to be addressed in future updates of the product due to its life cycle. For additional information, refer to the Red Hat Enterprise Linux Life Cycle: https://access.redhat.com/support/policy/updates/errata/. This issue affects the versions of the Linux kernel as shipped with Red Hat Enterprise Linux 6, 7, its real-time kernel, Red Hat Enterprise MRG 2, Red Hat Enterprise Linux 7 for ARM 64 and Red Hat Enterprise Linux 7 for Power 9 LE. Future Linux kernel updates for the respective releases may address this issue.
This issue has been addressed in the following products: Red Hat Enterprise Linux 7 Via RHSA-2018:0676 https://access.redhat.com/errata/RHSA-2018:0676
This issue has been addressed in the following products: Red Hat Enterprise Linux 7 Via RHSA-2018:1062 https://access.redhat.com/errata/RHSA-2018:1062
This issue has been addressed in the following products: Red Hat Enterprise MRG 2 Via RHSA-2018:1170 https://access.redhat.com/errata/RHSA-2018:1170
This issue has been addressed in the following products: Red Hat Enterprise Linux 7.4 Extended Update Support Via RHSA-2018:1130 https://access.redhat.com/errata/RHSA-2018:1130
This issue has been addressed in the following products: Red Hat Enterprise Linux 6 Via RHSA-2018:1319 https://access.redhat.com/errata/RHSA-2018:1319
the detailed security impact analysis of the flaw a packet from a network can get to net/netfilter/xt_TCPMSS.c:tcpmss_mangle_packet() which contains this flaw. the simple way to do this is to set an iptables rule like this: # iptables -t mangle -A PREROUTING -i <intf> -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1380 doff is a 4-bit field in the tcp header, is measured in quad-words (4 bytes) and stands for the "data offset": struct tcphdr { __be16 source; ... __u16 res1:4, doff:4, ... tcphdr->doff*4 points to an end of TCP options i.e. beginning of the packet data. the data offset is counted from the beginning of a tcp header, so normally it should be >= 5, as the size of a mandatory tcp header is 20 bytes. a packet from a network forged by an attacker can have the doff field set to zero, and this is the reason of the flaw. exactly this case is handled by the first change in the patch: it is checked that tcp_hdrlen (which is set to tcph->doff*4) is more or equal than a size of a mandatory tcp header: - if (len < tcp_hdrlen) + if (len < tcp_hdrlen || tcp_hdrlen < sizeof(struct tcphdr)) return -1; without this patch the flaw itself is a use-after-free due to an out-of-bound-access due to a missing input validation (i.e. without the tcp_hdrlen check above) and erroneous signed/unsigned compare. it happens in the following code when a forged packet with doff=0 gets to the tcpmss_mangle_packet(): static int tcpmss_mangle_packet(struct sk_buff *skb, ... { ... int len, tcp_hdrlen; unsigned int i; ... tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); // this is a tcp header tcp_hdrlen = tcph->doff * 4; // forged tcp_hdrlen may be 0 here ... opt = (u_int8_t *)tcph; // opt also points to a tcp header for (i = sizeof(struct tcphdr); i <= tcp_hdrlen - TCPOLEN_MSS; i += optlen(opt, i)) { if (opt[i] == TCPOPT_MSS && opt[i+1] == TCPOLEN_MSS) { ... } in this loop i starts with 20, sizeof(struct tcphdr). in case forged tcph->doff is 0, tcp_hdrlen is also zero and so signed subtraction "tcp_hdrlen - TCPOLEN_MSS" is -4 (32 bits 0xfffffffc). but i is unsigned, and so i is less than (2^32-4). thus the loop keeps looping until i reaches (2^32-4) or until it finds a 0x02/0x04 (TCPOPT_MSS/TCPOLEN_MSS) byte sequence. in the unlucky case depending on memory content i can overflow and the loop can run forever. after i exceeds size of a socket buffer (tcp header + data), opt[i] and opt[i+1] will start to access out-of-bound data beyond a socket buffer. these reads would lead to use-after-free errors detected by KASAN in case there were kernel memory which was freed previously. also this loop can corrupt a memory content outside of a socket buffer by writing a new MSS value to a place considered as TCPOPT_MSS tcp option: oldmss = (opt[i+2] << 8) | opt[i+3]; if (oldmss <= newmss) return 0; opt[i+2] = (newmss & 0xff00) >> 8; opt[i+3] = newmss & 0x00ff; this corruption is quite restricted: - there should be bytes 0x02/0x04 (TCPOPT_MSS/TCPOLEN_MSS) in a memory after a socket buffer - 2 bytes after them are set to the newmss which is not controlled by an attacker, but is set by a system administration in the iptables rule - this newmss should be less than oldmss (previous value of these 2 bytes) while theoretically this overwrite may lead to some significant security impact, the overwrite is not controlled by the attacker and conditions for it are quite restricted and so we assume that privileges escalation is unlikely. a system denial-of-service is very likely due to a system crash or a kernel execution thread stuck on looping for 4G of memory for tens of seconds. also a silent memory corruption is likely and we consider this as a biggest real security impact. it also can be that tcph->doff is 15 in a forged or normal tcp packet processed by tcpmss_mangle_packet(). in this case doff can wrap and become zero by: tcph->doff += TCPOLEN_MSS/4; the later case is handled by the second change in the patch: + /* tcph->doff has 4 bits, do not wrap it to 0 */ + if (tcp_hdrlen >= 15 * 4) + return 0; without this patch a packet with wrapped doff=0 or a forged packet with doff=0 from the previous case can get further down the execution path. this condition is checked on the entry to the tcp part of the network stack in net/ipv4/tcp_ipv4.c:tcp_v4_rcv() or in net/ipv6/tcp_ipv6.c:tcp_v6_rcv(): int tcp_v4_rcv(struct sk_buff *skb) { ... if (th->doff < sizeof(struct tcphdr) / 4) goto bad_packet; static int tcp_v6_rcv(struct sk_buff *skb) { ... if (th->doff < sizeof(struct tcphdr)/4) goto bad_packet; such erroneous packets are dropped and so cannot cause a security impact.
This issue has been addressed in the following products: Red Hat Enterprise Linux 7.3 Extended Update Support Via RHSA-2018:1737 https://access.redhat.com/errata/RHSA-2018:1737