Exploit-DB #39992
Description: Linux Kernel - 'ecryptfs' '/proc/$pid/environ' Local Privilege Escalation
Type: local
Platform: linux
Date: 2016-06-21
Author: Google Security Research

Exploit:
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=836

Stacking filesystems, including ecryptfs, protect themselves against
deep nesting, which would lead to kernel stack overflow, by tracking
the recursion depth of filesystems. E.g. in ecryptfs, this is
implemented in ecryptfs_mount() as follows:

	s->s_stack_depth = path.dentry->d_sb->s_stack_depth + 1;

	rc = -EINVAL;
	if (s->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
		pr_err("eCryptfs: maximum fs stacking depth exceeded\n");
		goto out_free;
	}


The files /proc/$pid/{mem,environ,cmdline}, when read, access the
userspace memory of the target process, involving, if necessary,
normal pagefault handling. If it was possible to mmap() them, an
attacker could create a chain of e.g. /proc/$pid/environ mappings
where process 1 has /proc/2/environ mapped into its environment area,
process 2 has /proc/3/environ mapped into its environment area and so
on. A read from /proc/1/environ would invoke the pagefault handler for
process 1, which would invoke the pagefault handler for process 2 and
so on. This would, again, lead to kernel stack overflow.


One interesting fact about ecryptfs is that, because of the encryption
involved, it doesn't just forward mmap to the lower file's mmap
operation. Instead, it has its own page cache, maintained using the
normal filemap helpers, and performs its cryptographic operations when
dirty pages need to be written out or when pages need to be faulted
in. Therefore, not just its read and write handlers, but also its mmap
handler only uses the lower filesystem's read and write methods.
This means that using ecryptfs, you can mmap [decrypted views of]
files that normally wouldn't be mappable.

Combining these things, it is possible to trigger recursion with
arbitrary depth where:

a reading userspace memory access in process A (from userland or from
    copy_from_user())
causes a pagefault in an ecryptfs mapping in process A, which
causes a read from /proc/{B}/environ, which
causes a pagefault in an ecryptfs mapping in process B, which
causes a read from /proc/{C}/environ, which
causes a pagefault in an ecryptfs mapping in process C, and so on.

On systems with the /sbin/mount.ecryptfs_private helper installed
(e.g. Ubuntu if the "encrypt my home directory" checkbox is ticked
during installation), this bug can be triggered by an unprivileged
user. The mount helper considers /proc/$pid, where $pid is the PID of
a process owned by the user, to be a valid mount source because the
directory is "owned" by the user.

I have attached both a generic crash PoC and a build-specific exploit
that can be used to gain root privileges from a normal user account on
Ubuntu 16.04 with kernel package linux-image-4.4.0-22-generic, version
4.4.0-22.40, uname "Linux user-VirtualBox 4.4.0-22-generic #40-Ubuntu
SMP Thu May 12 22:03:46 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux".

dmesg output of the crasher:

```
[   80.036069] BUG: unable to handle kernel paging request at fffffffe4b9145c0
[   80.040028] IP: [<ffffffff810c9a33>] cpuacct_charge+0x23/0x40
[   80.040028] PGD 1e0d067 PUD 0 
[   80.040028] Thread overran stack, or stack corrupted
[   80.040028] Oops: 0000 [#1] SMP 
[   80.040028] Modules linked in: vboxsf drbg ansi_cprng xts gf128mul dm_crypt snd_intel8x0 snd_ac97_codec ac97_bus snd_pcm snd_seq_midi snd_seq_midi_event snd_rawmidi vboxvideo snd_seq ttm snd_seq_device drm_kms_helper snd_timer joydev drm snd fb_sys_fops soundcore syscopyarea sysfillrect sysimgblt vboxguest input_leds i2c_piix4 8250_fintek mac_hid serio_raw parport_pc ppdev lp parport autofs4 hid_generic usbhid hid psmouse ahci libahci e1000 pata_acpi fjes video
[   80.040028] CPU: 0 PID: 2135 Comm: crasher Not tainted 4.4.0-22-generic #40-Ubuntu
[   80.040028] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[   80.040028] task: ffff[PII_REDACTED]00 ti: ffff8800d933c000 task.ti: ffff8800d933c000
[   80.040028] RIP: 0010:[<ffffffff810c9a33>]  [<ffffffff810c9a33>] cpuacct_charge+0x23/0x40
[   80.040028] RSP: 0000:ffff88021fc03d70  EFLAGS: 00010046
[   80.040028] RAX:[PII_REDACTED]00dc68 RBX: ffff[PII_REDACTED]60 RCX: ffffffffd933c068
[   80.040028] RDX: ffffffff81e50560 RSI:[PII_REDACTED]13877a RDI: ffff[PII_REDACTED]00
[   80.040028] RBP: ffff88021fc03d70 R08: [PII_REDACTED] R09: [PII_REDACTED]
[   80.040028] R10:[PII_REDACTED]002d4e R11:[PII_REDACTED]0010ae R12: ffff8802137aa200
[   80.040028] R13:[PII_REDACTED]13877a R14: ffff[PII_REDACTED]00 R15: ffff88021fc0ee68
[   80.040028] FS:  00007fbd9fadd700(0000) GS:ffff88021fc00000(0000) knlGS:[PII_REDACTED]
[   80.040028] CS:  0010 DS: 0000 ES: 0000 CR0: [PII_REDACTED]
[   80.040028] CR2: fffffffe4b9145c0 CR3: [PII_REDACTED] CR4:[PII_REDACTED]0006f0
[   80.040028] Stack:
[   80.040028]  ffff88021fc03db0 ffffffff810b4b83[PII_REDACTED]016d00 ffff88021fc16d00
[   80.040028]  ffff[PII_REDACTED]60 ffff8802137aa200 [PII_REDACTED] ffff88021fc0ee68
[   80.040028]  ffff88021fc03e30 ffffffff810bb414 ffff88021fc03dd0 ffff[PII_REDACTED]00
[   80.040028] Call Trace:
[   80.040028]  <IRQ> 
[   80.040028]  [<ffffffff810b4b83>] update_curr+0xe3/0x160
[   80.040028]  [<ffffffff810bb414>] task_tick_fair+0x44/0x8e0
[   80.040028]  [<ffffffff810b1267>] ? sched_clock_local+0x17/0x80
[   80.040028]  [<ffffffff810b146f>] ? sched_clock_cpu+0x7f/0xa0
[   80.040028]  [<ffffffff810ad35c>] scheduler_tick+0x5c/0xd0
[   80.040028]  [<ffffffff810fe560>] ? tick_sched_handle.isra.14+0x60/0x60
[   80.040028]  [<ffffffff810ee961>] update_process_times+0x51/0x60
[   80.040028]  [<ffffffff810fe525>] tick_sched_handle.isra.14+0x25/0x60
[   80.040028]  [<ffffffff810fe59d>] tick_sched_timer+0x3d/0x70
[   80.040028]  [<ffffffff810ef282>] __hrtimer_run_queues+0x102/0x290
[   80.040028]  [<ffffffff810efa48>] hrtimer_interrupt+0xa8/0x1a0
[   80.040028]  [<ffffffff81052fa8>] local_apic_timer_interrupt+0x38/0x60
[   80.040028]  [<ffffffff81827d9d>]