Monday, June 20, 2016

Exploiting Recursion in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Linux Kernel

Posted by Jann Horn, Google Project Zero

On June 1st, I reported an arbitrary recursion bug in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Linux kernel that can be triggered by a local user on Ubuntu if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system was installed with home directory encryption support. If you want to see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 crasher, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit code and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shorter bug report, go to https://bugs.chromium.org/p/project-zero/issues/detail?id=836.

Prerequisites

On Linux, userland processes typically have a stack that is around 8 MB long. If a program overflows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack, e.g. using infinite recursion, this is normally caught by a guard page below cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack.

Linux kernel stacks, which are e.g. used when handling system calls, are very different. They are relatively short: 4096 8192 bytes on 32-bit x86, 16384 bytes on x86-64. (The kernel stack size is specified by THREAD_SIZE_ORDER and THREAD_SIZE.) They are allocated using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel's buddy allocator, which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel's normal allocator for page-sized allocations (and power-of-two numbers of pages) and doesn't create guard pages. This means that if kernel stacks overflow, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y overlap with normal data. For this reason, kernel code must be (and usually is) very careful to not make big allocations on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack and has to prevent excessive recursion.

Most filesystems on Linux eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r don't use an underlying device (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pseudo-filesystems, like sysfs, procfs, tmpfs and so on) or use a block device (typically a partition on a harddisk) as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 backing device. However, two filesystems, ecryptfs and overlayfs, are exceptions: They are stacking filesystems, filesystems that use a folder in anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r filesystem (or, in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case of overlayfs, multiple ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r folders, often from different filesystems) as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 backing device. (The filesystem that serves as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 backing device is called lower filesystem, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 files in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lower filesystem are called lower files.) The idea with a stacking filesystem is that it more or less forwards accesses to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lower filesystem, but performs some modification on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 passed-through data. overlayfs merges multiple filesystems into a common view, ecryptfs performs transparent encryption.

A potential danger with stacking filesystems is that because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir virtual filesystem handlers often call into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handlers of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying filesystem, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stacked filesystem increases cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack usage compared to a direct access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying filesystem. If it was possible to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stacked filesystem as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 backing device for anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r stacked filesystem and so on, at some point, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel stack would overflow because every layer of filesystem stacking increases cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel stack depth. However, this is averted by placing a limit (FILESYSTEM_MAX_STACK_DEPTH) on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of filesystem nesting layers - only up to two layers of nested filesystems can be placed on top of a non-stacking filesystem.

The procfs pseudo-filesystem contains one directory per running process on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system, and each directory contains various files describing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process. Of interest here are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-process "mem", "environ" and "cmdline" files because reading from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m causes synchronous accesses to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual memory of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target process. The files expose different virtual address ranges:

  • "mem" exposes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whole virtual address range (and requires PTRACE_MODE_ATTACH access)
  • "environ" exposes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory range from mm->env_start to mm->env_end (and requires PTRACE_MODE_READ access)
  • "cmdline" exposes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory range from mm->arg_start to mm->arg_end if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last byte before mm->arg_end is a nullbyte; ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise, it gets a bit more complicated

If it was possible to mmap() cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "mem" file (which wouldn't make much sense; don't think too hard about it), you could e.g. set up mappings like this:

mem mmap correct.png

Then, assuming that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 /proc/$pid/mem mappings would have to be faulted in, a reading pagefault on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mapping in process C would cause pages to be faulted in from process B, which would cause anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r pagefault in process B, which in turn would cause pages to be faulted in from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory of process A - a recursive pagefault.

However, this doesn't actually work - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mem, environ and cmdline files only have VFS handlers for normal reads and writes, not for mmap:

static const struct file_operations proc_pid_cmdline_ops = {
.read   = proc_pid_cmdline_read,
.llseek = generic_file_llseek,
};
[...]
static const struct file_operations proc_mem_operations = {
.llseek  = mem_lseek,
.read    = mem_read,
.write   = mem_write,
.open    = mem_open,
.release = mem_release,
};
[...]
static const struct file_operations proc_environ_operations = {
.open    = environ_open,
.read    = environ_read,
.llseek  = generic_file_llseek,
.release = mem_release,
};

An interesting detail about ecryptfs is that it supports mmap(). Because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory mappings seen by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user have to be decrypted while memory mappings from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying filesystem would be encrypted, ecryptfs can't just forward mmap() operations to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying filesystem's mmap() handler. Instead, ecryptfs needs to use its own page cache for mappings.

When ecryptfs handles a page fault, it has to somehow read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 encrypted page from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying filesystem. It could do this by reading through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lower filesystem's page cache (using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lower filesystem's mmap handler), but that would be a waste of memory. Instead, ecryptfs simply uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lower filesystem's VFS read handler (via kernel_read()). This is more efficient and straightforward, but also has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 side effect that it is possible to mmap() decrypted views of files that normally wouldn't be mappable (because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap handler of an ecryptfs file works as long as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lower file has a read handler and contains valid encrypted data).

The vulnerability

At this point, we can piece togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack. We start by creating a process A with PID $A. Then, we create an ecryptfs mount /tmp/$A with /proc/$A as lower filesystem. (ecryptfs should be used with only one key so that filename encryption is disabled.) Now, /tmp/$A/mem, /tmp/$A/environ and /tmp/$A/cmdline are mappable if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding files in /proc/$A/ contain valid ecryptfs headers at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file. Unless we already have root privileges, we can't map anything to address 0x0 in process A, which corresponds to offset 0 in /proc/$A/mem, so a read from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start of /proc/$A/mem will always return -EIO and /proc/$A/mem never contains a valid ecryptfs header. Therefore, in a potential attack, only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 environ and cmdline files are interesting.

On kernels that are compiled with CONFIG_CHECKPOINT_RESTORE (which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case at least for Ubuntu's distro kernels), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arg_start, arg_end, env_start and env_end properties of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mm_struct can easily be set by an unprivileged process using prctl(PR_SET_MM, PR_SET_MM_MAP, &mm_map, sizeof(mm_map), 0. This permits pointing /proc/$A/environ and /proc/$A/cmdline at arbitrary virtual memory ranges. (On kernels without checkpoint-restore support, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack is slightly more annoying to execute, but still possible by re-executing with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 desired argument area and environment area lengths and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n replacing part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack memory mapping.)

If a valid encrypted ecryptfs file is loaded into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory of process A and its environment area is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n configured to point to that area, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decrypted view of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 environment area becomes accessible at /tmp/$A/environ. This file can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n be mapped into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory of anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r process, process B. In order to be able to repeat cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process, some data has to be encrypted with ecryptfs repeatedly, creating an ecryptfs matroska that can be loaded into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory of process A. Now, a chain of processes that map each ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r's decrypted environment area can be set up:

ecryptfs-mem chain relayouted.png

If no data is present in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relevant ranges of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory mappings in process C and process B, a pagefault in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory of process C (eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r caused by a pagefault from userspace or caused by a userspace access in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, e.g. via copy_from_user()) causes ecryptfs to read from /proc/$B/environ, causing a pagefault in process B, causing a read from /proc/$A/environ via ecryptfs, causing a pagefault in process A. This can be repeated arbitrarily, causing a stack overflow that crashes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel. The stack looks like this:

[...]
[] handle_mm_fault+0xf8b/0x1820
[] __get_user_pages+0x135/0x620
[] get_user_pages+0x52/0x60
[] __access_remote_vm+0xe6/0x2d0
[] ? alloc_pages_current+0x8c/0x110
[] access_remote_vm+0x1f/0x30
[] environ_read+0x122/0x1a0
[] ? security_file_permission+0xa0/0xc0
[] __vfs_read+0x18/0x40
[] vfs_read+0x86/0x130
[] kernel_read+0x50/0x80
[] ecryptfs_read_lower+0x23/0x30
[] ecryptfs_decrypt_page+0x82/0x130
[] ecryptfs_readpage+0xcd/0x110
[] filemap_fault+0x23b/0x3f0
[] __do_fault+0x50/0xe0
[] handle_mm_fault+0xf8b/0x1820
[] __get_user_pages+0x135/0x620
[] get_user_pages+0x52/0x60
[] __access_remote_vm+0xe6/0x2d0
[] ? alloc_pages_current+0x8c/0x110
[] access_remote_vm+0x1f/0x30
[] environ_read+0x122/0x1a0
[...]

Regarding reachability of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug: Exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug requires cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to mount ecryptfs filesystems with /proc/$pid as source. When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ecryptfs-utils package is installed (Ubuntu installs it if you enable home directory encryption during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 setup), this can be done using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 /sbin/mount.ecryptfs_private setuid helper. (It verifies that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user owns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source directory, but that's not a problem because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user "owns" cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 procfs directories of his own processes.)

Exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug

The following explanation is sometimes architecture-specific; when that is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case, it refers to amd64.

It used to be pretty simple to exploit bugs like this: As described in Jon Oberheide's "The Stack is Back" slides, it used to be possible to overflow into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread_info structure at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bottom of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack, overwrite eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 restart_block or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 addr_limit in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re with appropriate values and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n, depending on which one is being attacked, eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r execute code from an executable userspace mapping or use copy_from_user() and copy_to_user() to read and write kernel data.

However, restart_block was moved out of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread_info struct, and because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack overflow is triggered with a stack that contains kernel_read() frames, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 addr_limit is already KERNEL_DS and will be reset to USER_DS on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way back out. Additionally, at least Ubuntu Xenial's distro kernel turns on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CONFIG_SCHED_STACK_END_CHECK kernel config option, which causes a canary directly above cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread_info struct to be checked whenever cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scheduler is invoked; if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canary is incorrect, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel recursively oopses and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n panics (fixed to directly panic in 29d6455178a0).

Since it is hard to find anything worth targeting in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread_info struct (and because it would be nice to show that moving stuff out of thread_info is not a sufficient mitigation), I chose a different strategy: Overflow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack into an allocation in front of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n exploit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r allocation overlap. The problem with this approach is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canary and some components of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread_info struct must not be overwritten. The layout looks like this (green is fine to clobber, red must not be clobbered, yellow might be unpleasant if clobbered depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value):

kernel_stack_bottom.png

Luckily, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are stack frames that contain holes - if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bottom of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recursion uses cmdline instead of environ, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is a five-QWORD hole that isn't touched on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way down cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recursion, which is sufficient to cover everything from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 STACK_END_MAGIC to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flags. This can be seen when using a safe recursion level togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r with a debugging helper kernel module that sprays cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack with markers to make holes (green) in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack visible:


[...]
0xffff88015d115030: 0x0000000000000020 0xffff880077494640
0xffff88015d115040: 0xffffea000531feb0 0xffff88015d115118
0xffff88015d115050: 0xffffffff811bfc2b 0xdead505cdead5058
0xffff88015d115060: 0xdead5064dead5060 0xdead506cdead5068
0xffff88015d115070: 0xffff88014e3dff70 0xffff88015d1150d8
[...]
0xffff88015d115120: 0xffffffff811bacd5 0xdead512cdead5128
0xffff88015d115130: 0xdead5134dead5130 0xdead513cdead5138
0xffff88015d115140: 0xdead5144dead5140 0xdead514cdead5148
0xffff88015d115150: 0xffff8800d8364b00 0xffff88015d118000
[...]
0xffff88015d1154d0: 0xffffffff811bfc2b 0xdead54dcdead54d8
[...]
0xffff88015d1155a0: 0xffffffff811bacd5 0xdead55acdead55a8
0xffff88015d1155b0: 0xdead55b4dead55b0 0xdead55bcdead55b8
0xffff88015d1155c0: 0xdead55c4dead55c0 0xdead55ccdead55c8
[...]
0xffff88015d115950: 0xffffffff811bfc2b 0xdead595cdead5958
0xffff88015d115960: 0xdead5964dead5960 0xdead596cdead5968
[...]
0xffff88015d115a20: 0xffffffff811bacd5 0xdead5a2cdead5a28
0xffff88015d115a30: 0xdead5a34dead5a30 0xdead5a3cdead5a38
0xffff88015d115a40: 0xdead5a44dead5a40 0xdead5a4cdead5a48
[...]

The next problem is that this hole only appears at specific stack depths while, for successful exploitation, it needs to be precisely in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right position. However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are several tricks that can be used togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r to align cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack:

  • On every recursion level, it is possible to choose whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "environ" file or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "cmdline" file is used, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir stackframe sizes and hole patterns differ.
  • Any copy_from_user() is a valid entry point to a page fault. Even better, it is possible to combine an arbitrary write syscall with an arbitrary VFS write handler so that both cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 write syscall and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VFS write handler influence cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 depth. (And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 combined depth can be calculated without having to test every variant.)

After testing various combinations, I ended up with a mix of environ files and cmdline files, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 write() syscall and uid_map's VFS write handler.

At this point, we can recurse down into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous allocation without touching any of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dangerous fields. Now, execution of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel thread has to be paused while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack pointer points into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 preceding allocation, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 allocation cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack pointer points to should be overwritten with a new stack, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n execution of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel code should be resumed.

To pause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel thread while it is inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recursion, after setting up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chain of mappings, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 anonymous mapping at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chain can be replaced with a FUSE mapping (userfaultfd won't work here; it doesn't catch remote memory accesses).

For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 preceding allocation, my exploit uses pipes. When data is written to a newly allocated empty pipe, a page will be allocated for that data using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buddy allocator. My exploit simply spams cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory with pipe page allocations while creating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process that will trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pagefault using clone(). Using clone() instead of fork() has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 advantage that with appropriate flags, less information has to be duplicated, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is less memory allocation noise. During clone(), all of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe pages are filled up to, but excluding, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first saved RIP behind cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 expected RSP of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recursing process when it pauses in FUSE. Writing less would cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second pipe write to clobber stack data that is used before RIP control is achieved, potentially crashing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel. Then, when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recursing process has paused in FUSE, a second write is performed on all pipes to overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 saved RIP and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data behind it with a completely attacker-controlled new stack.

oob_stack_overwrite.png

At this point, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last line of defense should be KASLR. Ubuntu, as described on its Security Features page, supports KASLR on x86 and amd64 - but you have to enable it manually because doing so breaks hibernation. This bug has been fixed very recently, so now distros should be able to turn on KASLR by default - even if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security benefit isn't huge, it probably makes sense to turn on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 feature, since it doesn't really cost anything. Since most machines probably aren't configured to pass special parameters on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel command line, I'm assuming that KASLR, while compiled into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, is inactive, and so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker knows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 addresses of kernel text and static data.

Now that it is trivial to do arbitrary things in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel with ROP, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are two ways to continue with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit. You could e.g. use ROP to do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 equivalent of commit_creds(prepare_kernel_cred(NULL)). I chose to go a different way. Note that during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack overflow, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original KERNEL_DS value of addr_limit was preserved. Returning back through all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 saved stackframes would eventually reset cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 addr_limit to USER_DS - but if we just return back to userspace directly, addr_limit will stay KERNEL_DS. So my exploit writes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following new stack, which is more or less a copy of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 top of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack:

unsigned long new_stack[] = {
 0xffffffff818252f2, /* return pointer of syscall handler */
 /* 16 useless registers */
 0x1515151515151515, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 (unsigned long) post_corruption_user_code, /* user RIP */
 0x33, /* user CS */
 0x246, /* EFLAGS: most importantly, turn interrupts on */
 /* user RSP */
 (unsigned long) (post_corruption_user_stack + sizeof(post_corruption_user_stack)),
 0x2b /* user SS */
};

After cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recursing process is resumed by killing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FUSE server process, it resumes execution at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 post_corruption_user_code method. This method can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n e.g. use pipes to write to arbitrary kernel addresses because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address check in copy_to_user() is disabled:

void kernel_write(unsigned long addr, char *buf, size_t len) {
 int pipefds[2];
 if (pipe(pipefds))
   err(1, "pipe");
 if (write(pipefds[1], buf, len) != len)
   errx(1, "pipe write");
 close(pipefds[1]);
 if (read(pipefds[0], (char*)addr, len) != len)
   errx(1, "pipe read to kernelspace");
 close(pipefds[0]);
}

Now you can comfortably perform arbitrary reads and writes from userland. If you want a root shell, you can e.g. overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 coredump handler, which is stored in a static variable, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n raise SIGSEGV to execute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 coredump handler with root privileges:

 char *core_handler = "|/tmp/crash_to_root";
 kernel_write(0xffffffff81e87a60, core_handler, strlen(core_handler)+1);

Fixing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug

The bug was fixed using two separate patches: 2f36db710093 disallows opening files without an mmap handler through ecryptfs, and just to be sure, e54ad7f1ee26 disallows stacking anything on top of procfs because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is a lot of magic going on in procfs and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re really isn't any good reason to stack anything on top of it.

However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reason why I wrote a full root exploit for this not exactly widely exploitable bug is that I wanted to demonstrate that Linux stack overflows can occur in very non-obvious ways, and even with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 existing mitigations turned on, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're still exploitable. In my bug report, I asked cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel security list to add guard pages to kernel stacks and remove cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread_info struct from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bottom of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack to more reliably mitigate this bug class, similar to what ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r operating systems and grsecurity are already doing. Andy Lutomirski had actually already started working on this, and he has now published patches that add guard pages: https://lkml.org/lkml/2016/6/15/1064