Jul 20, 20189 min read ☕ (Last updated: Jul 20, 2018)

Modern Linux Kernel 0,1-day Unkind-Exploitations Review

TL;DR

Last time, I posted about 1-day vulnerability CVE-2017-5123, waitid() arbitrary R/W with null-deref on LK v4.13.x/~v4.14.0-rc4. It just happened because there's no any sanity check whether input space (*infop exactly) is kernel-land or user-land.

Also, you can find other good payloads that include sandbox-bypass like chrome-sandbox (actually, it's kinda different vulnerability, but...), fully-chained sth, etc...

Anyway, recently, I've been spending some time fuzzing network and fs-related Linux kernel interfaces with syzkaller and hand :).

Actually, I didn't have enough time to fully analyze vulnerabilities & complete fully-working payloads. So, this post describes how the bug was discovered and suggesting a possible way to exploit for EoP or leak info.

Founds

Overally, there're some bugs at (DCCP, XDP, SCTP)-socket, (ext4, 9p)-fs, mm stuffs. Information Disclosure which can lead to KASLR bypass at dccp_feat_xxx and hugetlb stuff, xdp_umem_create, p9_client_rpc, etc... And ext4_xattr_set_entry [CVE-2018-10879] (6/25/2018, found by syzkaller), etc...

Targets

Most of the bugs can be worked on LK v4.16.x ~ v4.18.x-rc5 (latest).

1-day bug

Most of the bugs found by syzkaller and already patched by other people :).

ext4_xattr_set_entry

Currently, it was reported by Laura Pardo on 6/29/2018. CVE-2018-10879.

When renaming a file in a crafted ext4 image, UAF triggered (dangling last is accessed). It just fixed by checking last & ext4 magic number whether it is valid.

file /fs/ext4/xattr.c L1598

Before

    // LK v4.17.0+ // Fixed at LK v4.17.6
	for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
		if (!last->e_value_inum && last->e_value_size) {
			size_t offs = le16_to_cpu(last->e_value_offs);
			if (offs < min_offs)
				min_offs = offs;
		}
	}

After

    // LK v4.18.0-rc5
    for (; !IS_LAST_ENTRY(last); last = next) {
        next = EXT4_XATTR_NEXT(last);
        if ((void *)next >= s->end) {
            EXT4_ERROR_INODE(inode, "corrupted xattr entries");
            ret = -EFSCORRUPTED;
            goto out;
        }
        if (!last->e_value_inum && last->e_value_size) {
            size_t offs = le16_to_cpu(last->e_value_offs);
            if (offs < min_offs)
                min_offs = offs;
        }
    }

file /fs/ext4/xattr.c L230

Before

	if (buffer_verified(bh))
		return 0;
	if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||
	    BHDR(bh)->h_blocks != cpu_to_le32(1))
		goto errout;

After

	if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||
	    BHDR(bh)->h_blocks != cpu_to_le32(1))
		goto errout;
	if (buffer_verified(bh))
		return 0;

Syzkaller found this bug on 6/25/2018. Here's my syzkaller's report.

BUG: KASAN: use-after-free in ext4_xattr_set_entry+0x34fc/0x3a30 fs/ext4/xattr.c:1598
Read of size 4 at addr ffff88002f02b046 by task syz-executor14/14011

CPU: 0 PID: 14011 Comm: syz-executor14 Not tainted 4.17.0+ #9
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
Call Trace:

The buggy address belongs to the page:
page:ffffea0000bc0ac0 count:0 mapcount:-128 mapping:0000000000000000 index:0x1
flags: 0x100000000000000()
raw: 0100000000000000 ffffea0001174ec8 ffffea0001561e08 0000000000000000
raw: 0000000000000001 0000000000000000 00000000ffffff7f 0000000000000000
page dumped because: kasan: bad access detected

Memory state around the buggy address:
 ffff88002f02af00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ffff88002f02af80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff88002f02b000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
                                           ^
 ffff88002f02b080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
 ffff88002f02b100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff

p9_client_rpc

syz-bot reported this bug on 7/11/2018. Bug type is an uninitialized variable.

In short, we need to validate p9pdu_readf return-value before assigning -ecode to err. Because if p9pdu_readf returns -EFAULT, then err remains uninitalized.

file /net/9p/client.c L560

Before

...
// LK v4.17.8
} else {
		err = p9pdu_readf(req->rc, c->proto_version, "d", &ecode);
		err = -ecode;

		p9_debug(P9_DEBUG_9P, "<<< RLERROR (%d)\n", -ecode);
	}
...

After

I think it's not fixed yet but reported.

Here's my syzkaller report.

[  155.012783] kmemleak: 8 new suspected memory leaks (see /sys/kernel/debug/kmemleak)
BUG: memory leak
ferenced object 0xffff88006695ad80 (size 96):
  comm "syz-executor15", pid 11123, jiffies 4294812752 (age 17.158s)
  hex dump (first 32 bytes):
    00 00 00 00 ad 4e ad de ff ff ff ff 00 00 00 00  .....N..........
    ff ff ff ff ff ff ff ff a0 14 ba a1 ff ff ff ff  ................
  backtrace:
    [<00000000d71df12d>] p9_client_prepare_req net/9p/client.c:757 [inline]
    [<00000000d71df12d>] p9_client_rpc+0x1bd/0x1410 net/9p/client.c:757
    [<0000000062b42e0d>] p9_client_version net/9p/client.c:976 [inline]
    [<0000000062b42e0d>] p9_client_create+0xd0b/0x16ce net/9p/client.c:1069
    [<000000006460f58e>] v9fs_session_init+0x21a/0x1960 fs/9p/v9fs.c:400
    [<00000000066f7e92>] v9fs_mount+0x79/0x860 fs/9p/vfs_super.c:135
    [<0000000057fd6665>] mount_fs+0xa7/0x323 fs/super.c:1277
    [<0000000008124149>] vfs_kern_mount.part.33+0xc9/0x4c0 fs/namespace.c:1037
    [<00000000110ad253>] vfs_kern_mount fs/namespace.c:1027 [inline]
    [<00000000110ad253>] do_new_mount fs/namespace.c:2518 [inline]
    [<00000000110ad253>] do_mount+0x552/0x2ee0 fs/namespace.c:2848
    [<000000005eddec36>] ksys_mount+0x125/0x140 fs/namespace.c:3064
    [<00000000d4c47900>] __do_sys_mount fs/namespace.c:3078 [inline]
    [<00000000d4c47900>] __se_sys_mount fs/namespace.c:3075 [inline]
    [<00000000d4c47900>] __x64_sys_mount+0xba/0x150 fs/namespace.c:3075
    [<0000000052f04bfc>] do_syscall_64+0x165/0x670 arch/x86/entry/common.c:290
    [<000000005d846763>] entry_SYSCALL_64_after_hwframe+0x49/0xbe
    [<000000005c780d9d>] 0xffffffffffffffff

mm stuff

These bugs are what I found on LK v4.16.x. Here's my post.

I dunno whether it was patched :). But, as I remembered, it's not a perfectly reliable bug, and there's kinda restriction. So, I'll skip this bug :).

Console Demo

zero@zer0day:/tmp$ uname -a
Linux zer0day 4.16.0-rc5+ #19 SMP Sun Mar 18 20:44:40 KST 2018 x86_64 GNU/Linux
zero@zer0day:/tmp$ ./leak
zero@zer0day:/tmp$ ./leak 1
[+] Found Kernel Base Address!
[+] kbase : 0xffffffff89e00000
zero@zer0day:/tmp$ su
root@zer0day:/tmp# cat /proc/kallsyms | grep _text | head -n 1
ffffffff89e00000 T _text
root@zer0day:/tmp#
zero@zer0day:/tmp$ ./leak
...
zero@zer0day:/tmp$ ./leak 1
[+] Found Kernel Base Address!
[+] kbase : 0xffffffffb2600000
zero@zer0day:/tmp$ ls
leak  leak.c
zero@zer0day:/tmp$ su
root@zer0day:/tmp# cat /proc/kallsyms | grep _text | head -n 1
ffffffffb2600000 T _text
root@zer0day:/tmp# uname -a
Linux zer0day 4.16.0-rc7+ #22 SMP Thu Mar 29 16:46:52 KST 2018 x86_64 GNU/Linux

Anyway, successfully got kernel base address :)

0-day bug

In this chapter, I gonna show information disclosure which can lead to KASLR bypass. This feature is used on many Linux distributions, So, maybe it is reliable on many Linux systems :| (i didn't test all of them, yet)

Bugs

This bug is slab-out-of-bounds Read. By using this, kernel-land addresses (heap) can be leaked. Found on LK ~v.4.18.x-rc5.

Hint for the vulnerability is fs. There're no any validations for specific variable and it leads to OOB.

syslog

[  421.466496] ==================================================================
[  421.467034] BUG: KASAN: slab-out-of-bounds in xxx
[  421.467350] Read of size 48059 at addr ffff88006bb9002d by task poc/2823
[  421.467704] 
[  421.467795] CPU: 1 PID: 2823 Comm: poc Not tainted 4.18.0-rc5+ #23
[  421.468128] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[  421.468626] Call Trace:
...
[  421.484432] RIP: 0033:0x7fc9130f2afa
[  421.484623] Code: ... 
[  421.485702] RSP: 002b:0000000000700c18 EFLAGS: 00000206 ORIG_RAX: 00000000000000a5
[  421.486100] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007fc9130f2afa
[  421.486469] RDX: 0000000000700d30 RSI: 0000000000700d40 RDI: 0000000000000000
[  421.486838] RBP: 0000000000700d90 R08: 0000000000700c30 R09: 0000000000000001
[  421.487205] R10: 0000000000000000 R11: 0000000000000206 R12: 00000000004005d0
[  421.487571] R13: 00007ffcd7f02910 R14: 0000000000000000 R15: 0000000000000000
[  421.487945] 
[  421.488030] The buggy address belongs to the page:
[  421.488284] page:ffffea0001aee400 count:1 mapcount:0 mapping:0000000000000000 index:0x0 compound_mapcount: 0
[  421.488789] flags: 0x100000000008000(head)
[  421.489013] raw: 0100000000008000 dead000000000100 dead000000000200 0000000000000000
[  421.489411] raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000
[  421.489810] page dumped because: kasan: bad access detected
[  421.490099] 
[  421.490183] Memory state around the buggy address:
[  421.490436]  ffff88006bb91f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[  421.490815]  ffff88006bb91f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[  421.491188] >ffff88006bb92000: 00 00 00 00 fe fe fe fe fe fe fe fe fe fe fe fe
[  421.491558]                                ^
[  421.491786]  ffff88006bb92080: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
[  421.492163]  ffff88006bb92100: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
[  421.492530] ==================================================================
...
[ 1316.744978] kmemleak: 314 new suspected memory leaks (see /sys/kernel/debug/kmemleak)
[ 1939.198134] kmemleak: 12219 new suspected memory leaks (see /sys/kernel/debug/kmemleak)

file /sys/kernel/debug/kmemleak

unreferenced object 0xffff88005bfec000 (size 14280):
  comm "poc", pid 3256, jiffies 4295094805 (age 1984.122s)
  hex dump (first 32 bytes):
    00 00 00 00 00 00 00 00 88 51 10 6b 00 88 ff ff  .........Q.k....
    00 80 2c 6a 00 88 ff ff 00 c0 2c 6a 00 88 ff ff  ..,j......,j....
...
unreferenced object 0xffff88006b105188 (size 96):
  comm "poc", pid 3256, jiffies 4295094805 (age 1984.122s)
  hex dump (first 32 bytes):
    00 00 00 00 ad 4e ad de ff ff ff ff 6b 6b 6b 6b  .....N......kkkk
    ff ff ff ff ff ff ff ff a0 14 7a 98 ff ff ff ff  ..........z.....

Demo

I roughly make a PoC code, So, currently, it should be run by root privilege. But, maybe it can also be run by normal user privilege after a few fixes.

root@zer0day:/tmp# uname -a
Linux zer0day 4.18.0-rc5+ #23 SMP Tue Jul 17 22:48:05 KST 2018 x86_64 GNU/Linux
root@zer0day:/tmp# ls -al
total 24
drwxrwxrwt  2 root root 4096 Jul 20 09:51 .
drwxr-xr-x 24 root root 4096 Jul 17 14:19 ..
-rwxr-xr-x  1 zero zero 8307 Jul 20 09:18 poc
-rw-r--r--  1 zero zero 1222 Jul 20 09:18 poc.c
root@zer0day:/tmp# ./poc
...
[lots of kaddr leaks...]

Hmm... not cool stuff :(

Conclusion

There're more bugs that I and fuzzer found like UAFs, OOBs, etc... But, some of them can't be reproducible :( or can run under some conditions...

Anyway, the latest LK version is v4.18.0-rc5 (7/20/2018). And i think the Linux kernel still has a lot of security issues and they're just exposed to unprivileged users. It means many interfaces need to be tested and some features should be restricted by default.