kernel: /proc/self/mem hardening |
|||||||||||
Issue descriptionwe don't have any process (that i'm aware of) that needs to open its mem file (the memory map of the process) for writing for any legitimate purpose. this hole was used as part of an exploit chain, so let's kill it off. we can't rely on mounting /proc read-only everywhere as there are some paths in there that legitimately need to be writable (like uid_map and oom_score_adj). SELinux would be able to handle this, but that's a ways off, and we can do both (make it read-only & lock it via SELinux).
,
Nov 13 2017
,
Nov 13 2017
The following revision refers to this bug: https://chromium.googlesource.com/chromiumos/third_party/kernel/+/7ef6731fcaa6a8a5294814e017313de8097dc22a commit 7ef6731fcaa6a8a5294814e017313de8097dc22a Author: Mike Frysinger <vapier@chromium.org> Date: Mon Nov 13 23:25:49 2017 CHROMIUM: proc: make /proc/self/mem read-only Dropping the mode bits disables write access for non-root users. Trying to `chmod` the paths back fails as the kernel rejects it. For users with CAP_DAC_OVERRIDE (usually just root), we have to disable the mem_write callback to avoid bypassing the mode bits. While we have some use cases in CrOS where we want read access to another process's memory, there are no valid use cases for writing. This can be used to bypass permissions on memory maps -- even if a memory region is mapped r-x (as is a program's executable pages), the process can open its own /proc/self/mem file and write to the pages directly. Even if we have seccomp filters that block mmap and mprotect calls with W|X perms, we often cannot block open calls as daemons want to read/write their own runtime state (and seccomp filters cannot check file paths). Obviously blocking write calls are also fundamentally out. This was used in the recent exploit chain (chromium:766253) to install an arbitrary payload from noexec storage into a running process and then execute it (which itself could include an ELF loader to run arbitrary ELFs off of noexec storage). Since the mem file is part of the dynamic /proc/<pid>/ space, we can't run chmod once at boot to restrict it (and trying to react to every process and run chmod doesn't scale, and the kernel no longer allows chmod on any of these paths). Getting this merged upstream seems unlikely as it could easily break weird legacy apps that do want to do self modification. Adding a command line knob or a CONFIG option is possible, but seems like overkill for the code in question. We might consider replacing this with an SELinux rule assuming one could be written to cover all /proc/*/mem files, once we get SELinux working under CrOS. But even then, having multiple ways to deny an attack is useful in case one layer fails. BUG= chromium:781376 TEST=`dd if=/dev/zero of=mem bs=1 seek=<valid addr> conv=notrunc` no longer works for root or non-root Change-Id: Ida4937f52ccb81af07b6c0e84dfd71900e01d068 (cherry picked from commit f29788b89a297de428e22f2381c67ce06b9d29b3) Reviewed-on: https://chromium-review.googlesource.com/764827 Commit-Ready: Mike Frysinger <vapier@chromium.org> Tested-by: Mike Frysinger <vapier@chromium.org> Reviewed-by: Guenter Roeck <groeck@chromium.org> [modify] https://crrev.com/7ef6731fcaa6a8a5294814e017313de8097dc22a/fs/proc/base.c [modify] https://crrev.com/7ef6731fcaa6a8a5294814e017313de8097dc22a/security/chromiumos/Kconfig
,
Nov 14 2017
The following revision refers to this bug: https://chromium.googlesource.com/chromiumos/third_party/kernel/+/b0fe193c32bf797e987f29908ce38f345000c03e commit b0fe193c32bf797e987f29908ce38f345000c03e Author: Mike Frysinger <vapier@chromium.org> Date: Tue Nov 14 06:22:08 2017 CHROMIUM: proc: make /proc/self/mem read-only Dropping the mode bits disables write access for non-root users. Trying to `chmod` the paths back fails as the kernel rejects it. For users with CAP_DAC_OVERRIDE (usually just root), we have to disable the mem_write callback to avoid bypassing the mode bits. While we have some use cases in CrOS where we want read access to another process's memory, there are no valid use cases for writing. This can be used to bypass permissions on memory maps -- even if a memory region is mapped r-x (as is a program's executable pages), the process can open its own /proc/self/mem file and write to the pages directly. Even if we have seccomp filters that block mmap and mprotect calls with W|X perms, we often cannot block open calls as daemons want to read/write their own runtime state (and seccomp filters cannot check file paths). Obviously blocking write calls are also fundamentally out. This was used in the recent exploit chain (chromium:766253) to install an arbitrary payload from noexec storage into a running process and then execute it (which itself could include an ELF loader to run arbitrary ELFs off of noexec storage). Since the mem file is part of the dynamic /proc/<pid>/ space, we can't run chmod once at boot to restrict it (and trying to react to every process and run chmod doesn't scale, and the kernel no longer allows chmod on any of these paths). Getting this merged upstream seems unlikely as it could easily break weird legacy apps that do want to do self modification. Adding a command line knob or a CONFIG option is possible, but seems like overkill for the code in question. We might consider replacing this with an SELinux rule assuming one could be written to cover all /proc/*/mem files, once we get SELinux working under CrOS. But even then, having multiple ways to deny an attack is useful in case one layer fails. BUG= chromium:781376 TEST=`dd if=/dev/zero of=mem bs=1 seek=<valid addr> conv=notrunc` no longer works for root or non-root Change-Id: Ida4937f52ccb81af07b6c0e84dfd71900e01d068 (cherry picked from commit f29788b89a297de428e22f2381c67ce06b9d29b3) Reviewed-on: https://chromium-review.googlesource.com/764828 Commit-Ready: Mike Frysinger <vapier@chromium.org> Tested-by: Mike Frysinger <vapier@chromium.org> Reviewed-by: Guenter Roeck <groeck@chromium.org> [modify] https://crrev.com/b0fe193c32bf797e987f29908ce38f345000c03e/fs/proc/base.c [modify] https://crrev.com/b0fe193c32bf797e987f29908ce38f345000c03e/security/chromiumos/Kconfig
,
Nov 15 2017
Apologies if this is considered fixed. I wrote a noexec bypass similar to the one used in the exploit chain a while ago, and did some research into mitigations. It is possible to limit access to /proc/*/mem with the ptrace_scope sysctl: https://www.kernel.org/doc/Documentation/security/Yama.txt Mitigations are discussed at the bottom of https://blog.gdssecurity.com/labs/2017/9/5/linux-based-inter-process-code-injection-without-ptrace2.html
,
Nov 15 2017
we're happy to look at multiple hardening routes -- security in depth after all. feel free to mention things even if you're not sure if we're already doing it ... better to overshare than to miss something good. it's also helpful to have concrete examples of exploit tools that we're actually shutting down versus talking in the abstract. it's much easier to get people to agree when you have data ). CrOS already turns on YAMA, and the default ptrace_scope value is 1. we have autotests to make sure this doesn't change: https://chromium.googlesource.com/chromiumos/third_party/autotest/+/master/client/site_tests/platform_OSLimits/platform_OSLimits.py that means only parents/admin can start messing with mem files. a setting of 3 is pretty much a non-starter. at least upstart uses ptrace for legitimate purposes when spawning services. we might be able to change to a value of 2 though. upstart runs as root and will have sufficient caps. i can't think of other scenarios off the top of my head where 2 wouldn't work ... devs trying to use gdb i think already have troubles with a setting of 1, and they can always resort to `sudo` to attach. i've moved your suggestion to issue 785440 so we can track it further. i think this bug is all set as the tree is still green and people haven't been complaining :).
,
Nov 18 2017
The following revision refers to this bug: https://chromium.googlesource.com/chromiumos/third_party/kernel/+/72634ffb72705cdc8762d6f8d2e3124daa15be19 commit 72634ffb72705cdc8762d6f8d2e3124daa15be19 Author: Mike Frysinger <vapier@chromium.org> Date: Sat Nov 18 04:18:38 2017 CHROMIUM: proc: make /proc/self/mem read-only Dropping the mode bits disables write access for non-root users. Trying to `chmod` the paths back fails as the kernel rejects it. For users with CAP_DAC_OVERRIDE (usually just root), we have to disable the mem_write callback to avoid bypassing the mode bits. While we have some use cases in CrOS where we want read access to another process's memory, there are no valid use cases for writing. This can be used to bypass permissions on memory maps -- even if a memory region is mapped r-x (as is a program's executable pages), the process can open its own /proc/self/mem file and write to the pages directly. Even if we have seccomp filters that block mmap and mprotect calls with W|X perms, we often cannot block open calls as daemons want to read/write their own runtime state (and seccomp filters cannot check file paths). Obviously blocking write calls are also fundamentally out. This was used in the recent exploit chain (chromium:766253) to install an arbitrary payload from noexec storage into a running process and then execute it (which itself could include an ELF loader to run arbitrary ELFs off of noexec storage). Since the mem file is part of the dynamic /proc/<pid>/ space, we can't run chmod once at boot to restrict it (and trying to react to every process and run chmod doesn't scale, and the kernel no longer allows chmod on any of these paths). Getting this merged upstream seems unlikely as it could easily break weird legacy apps that do want to do self modification. Adding a command line knob or a CONFIG option is possible, but seems like overkill for the code in question. We might consider replacing this with an SELinux rule assuming one could be written to cover all /proc/*/mem files, once we get SELinux working under CrOS. But even then, having multiple ways to deny an attack is useful in case one layer fails. BUG= chromium:781376 TEST=`dd if=/dev/zero of=mem bs=1 seek=<valid addr> conv=notrunc` no longer works for root or non-root Change-Id: Ida4937f52ccb81af07b6c0e84dfd71900e01d068 (cherry picked from commit f29788b89a297de428e22f2381c67ce06b9d29b3) Reviewed-on: https://chromium-review.googlesource.com/764772 Commit-Ready: Mike Frysinger <vapier@chromium.org> Tested-by: Mike Frysinger <vapier@chromium.org> Reviewed-by: Guenter Roeck <groeck@chromium.org> [modify] https://crrev.com/72634ffb72705cdc8762d6f8d2e3124daa15be19/fs/proc/base.c [modify] https://crrev.com/72634ffb72705cdc8762d6f8d2e3124daa15be19/security/chromiumos/Kconfig
,
Nov 19 2017
The following revision refers to this bug: https://chromium.googlesource.com/chromiumos/third_party/kernel/+/202faf71946a3fc9ba77fe252e5ae8082082f8d0 commit 202faf71946a3fc9ba77fe252e5ae8082082f8d0 Author: Mike Frysinger <vapier@chromium.org> Date: Sun Nov 19 05:07:54 2017 CHROMIUM: proc: make /proc/self/mem read-only Dropping the mode bits disables write access for non-root users. Trying to `chmod` the paths back fails as the kernel rejects it. For users with CAP_DAC_OVERRIDE (usually just root), we have to disable the mem_write callback to avoid bypassing the mode bits. While we have some use cases in CrOS where we want read access to another process's memory, there are no valid use cases for writing. This can be used to bypass permissions on memory maps -- even if a memory region is mapped r-x (as is a program's executable pages), the process can open its own /proc/self/mem file and write to the pages directly. Even if we have seccomp filters that block mmap and mprotect calls with W|X perms, we often cannot block open calls as daemons want to read/write their own runtime state (and seccomp filters cannot check file paths). Obviously blocking write calls are also fundamentally out. This was used in the recent exploit chain (chromium:766253) to install an arbitrary payload from noexec storage into a running process and then execute it (which itself could include an ELF loader to run arbitrary ELFs off of noexec storage). Since the mem file is part of the dynamic /proc/<pid>/ space, we can't run chmod once at boot to restrict it (and trying to react to every process and run chmod doesn't scale, and the kernel no longer allows chmod on any of these paths). Getting this merged upstream seems unlikely as it could easily break weird legacy apps that do want to do self modification. Adding a command line knob or a CONFIG option is possible, but seems like overkill for the code in question. We might consider replacing this with an SELinux rule assuming one could be written to cover all /proc/*/mem files, once we get SELinux working under CrOS. But even then, having multiple ways to deny an attack is useful in case one layer fails. BUG= chromium:781376 TEST=`dd if=/dev/zero of=mem bs=1 seek=<valid addr> conv=notrunc` no longer works for root or non-root Change-Id: Ida4937f52ccb81af07b6c0e84dfd71900e01d068 (cherry picked from commit f29788b89a297de428e22f2381c67ce06b9d29b3) Reviewed-on: https://chromium-review.googlesource.com/764773 Commit-Ready: Mike Frysinger <vapier@chromium.org> Tested-by: Mike Frysinger <vapier@chromium.org> Reviewed-by: Guenter Roeck <groeck@chromium.org> [modify] https://crrev.com/202faf71946a3fc9ba77fe252e5ae8082082f8d0/fs/proc/base.c [modify] https://crrev.com/202faf71946a3fc9ba77fe252e5ae8082082f8d0/security/chromiumos/Kconfig
,
Nov 20 2017
The following revision refers to this bug: https://chromium.googlesource.com/chromiumos/third_party/kernel/+/05358e35cffa3e9117e692f1f4bd8f1c7654ff6a commit 05358e35cffa3e9117e692f1f4bd8f1c7654ff6a Author: Mike Frysinger <vapier@chromium.org> Date: Mon Nov 20 17:55:07 2017 CHROMIUM: proc: make /proc/self/mem read-only Dropping the mode bits disables write access for non-root users. Trying to `chmod` the paths back fails as the kernel rejects it. For users with CAP_DAC_OVERRIDE (usually just root), we have to disable the mem_write callback to avoid bypassing the mode bits. While we have some use cases in CrOS where we want read access to another process's memory, there are no valid use cases for writing. This can be used to bypass permissions on memory maps -- even if a memory region is mapped r-x (as is a program's executable pages), the process can open its own /proc/self/mem file and write to the pages directly. Even if we have seccomp filters that block mmap and mprotect calls with W|X perms, we often cannot block open calls as daemons want to read/write their own runtime state (and seccomp filters cannot check file paths). Obviously blocking write calls are also fundamentally out. This was used in the recent exploit chain (chromium:766253) to install an arbitrary payload from noexec storage into a running process and then execute it (which itself could include an ELF loader to run arbitrary ELFs off of noexec storage). Since the mem file is part of the dynamic /proc/<pid>/ space, we can't run chmod once at boot to restrict it (and trying to react to every process and run chmod doesn't scale, and the kernel no longer allows chmod on any of these paths). Getting this merged upstream seems unlikely as it could easily break weird legacy apps that do want to do self modification. Adding a command line knob or a CONFIG option is possible, but seems like overkill for the code in question. We might consider replacing this with an SELinux rule assuming one could be written to cover all /proc/*/mem files, once we get SELinux working under CrOS. But even then, having multiple ways to deny an attack is useful in case one layer fails. BUG= chromium:781376 TEST=`dd if=/dev/zero of=mem bs=1 seek=<valid addr> conv=notrunc` no longer works for root or non-root Change-Id: Ida4937f52ccb81af07b6c0e84dfd71900e01d068 (cherry picked from commit f29788b89a297de428e22f2381c67ce06b9d29b3) Reviewed-on: https://chromium-review.googlesource.com/764829 Commit-Ready: Mike Frysinger <vapier@chromium.org> Tested-by: Mike Frysinger <vapier@chromium.org> Reviewed-by: Guenter Roeck <groeck@chromium.org> [modify] https://crrev.com/05358e35cffa3e9117e692f1f4bd8f1c7654ff6a/fs/proc/base.c [modify] https://crrev.com/05358e35cffa3e9117e692f1f4bd8f1c7654ff6a/security/chromiumos/Kconfig
,
Jan 22 2018
,
Jan 23 2018
,
Mar 3 2018
The following revision refers to this bug: https://chromium.googlesource.com/chromiumos/third_party/kernel/+/6523350de6501fff457b440fcfb11a59d6de0422 commit 6523350de6501fff457b440fcfb11a59d6de0422 Author: Mike Frysinger <vapier@chromium.org> Date: Sat Mar 03 06:22:42 2018 CHROMIUM: proc: make /proc/self/mem read-only (cherrypicked from commit f29788b89a297de428e22f2381c67ce06b9d29b3 from chromeos-4.4) Dropping the mode bits disables write access for non-root users. Trying to `chmod` the paths back fails as the kernel rejects it. For users with CAP_DAC_OVERRIDE (usually just root), we have to disable the mem_write callback to avoid bypassing the mode bits. While we have some use cases in CrOS where we want read access to another process's memory, there are no valid use cases for writing. This can be used to bypass permissions on memory maps -- even if a memory region is mapped r-x (as is a program's executable pages), the process can open its own /proc/self/mem file and write to the pages directly. Even if we have seccomp filters that block mmap and mprotect calls with W|X perms, we often cannot block open calls as daemons want to read/write their own runtime state (and seccomp filters cannot check file paths). Obviously blocking write calls are also fundamentally out. This was used in the recent exploit chain (chromium:766253) to install an arbitrary payload from noexec storage into a running process and then execute it (which itself could include an ELF loader to run arbitrary ELFs off of noexec storage). Since the mem file is part of the dynamic /proc/<pid>/ space, we can't run chmod once at boot to restrict it (and trying to react to every process and run chmod doesn't scale, and the kernel no longer allows chmod on any of these paths). Getting this merged upstream seems unlikely as it could easily break weird legacy apps that do want to do self modification. Adding a command line knob or a CONFIG option is possible, but seems like overkill for the code in question. We might consider replacing this with an SELinux rule assuming one could be written to cover all /proc/*/mem files, once we get SELinux working under CrOS. But even then, having multiple ways to deny an attack is useful in case one layer fails. BUG= chromium:781376 TEST=`dd if=/dev/zero of=mem bs=1 seek=<valid addr> conv=notrunc` no longer works for root or non-root Signed-off-by: Mike Frysinger <vapier@chromium.org> Previous-Reviewed-on: https://chromium-review.googlesource.com/753406 Previous-Reviewed-by: Guenter Roeck <groeck@chromium.org> Signed-off-by: Aditya Kali <adityakali@google.com> Change-Id: Ida4937f52ccb81af07b6c0e84dfd71900e01d068 Reviewed-on: https://chromium-review.googlesource.com/946989 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: Aditya Kali <adityakali@google.com> Reviewed-by: Guenter Roeck <groeck@chromium.org> Reviewed-by: Mike Frysinger <vapier@chromium.org> [modify] https://crrev.com/6523350de6501fff457b440fcfb11a59d6de0422/fs/proc/base.c [modify] https://crrev.com/6523350de6501fff457b440fcfb11a59d6de0422/security/chromiumos/Kconfig
,
Aug 28
Please note, this breaks usage of rr ( https://rr-project.org/ ) on Chrome OS. A configurable knob would be appreciated.
,
Aug 28
sorry, but no. putting in a runtime knob would significantly weaken the security provided by it.
,
Aug 28
Note that similar permission-override behavior can be obtained with `PTRACE_POKE_TEXT/DATA` and I think `process_vm_writev` so I assume/hope those are also blocked by ChromeOS. I believe a process can still ptrace one of its own threads when YAMA `ptrace_scope` is 1.
,
Aug 28
sure, if you have ptrace scope on a process, it def leaves it open to arbitrary read/writes of any memory region. the difference there is that it requires you to be within a specific scope (as you noted), and you need to be able to access the ptrace syscall. that's a much higher barrier than accessing the "mem" file which can be hit with a simple `dd` fork+exec. when it comes to seccomp filters, we have the ability to run daemons/processes through restrictive syscall filters, and we have yet to include ptrace there. off the top of my head, the only valid use of ptrace on the system is in upstart itself (which uses it for process tracking). it might be interesting to severely restrict ptrace so only pid 1 (init) is allowed to use it. are you asking about a knob for the main CrOS host env (where you've put the system into dev mode), or so you can run these tools inside of Crostini (inside of the VM) ? we could look at adding a build-time knob for Crostini usage. |
|||||||||||
►
Sign in to add a comment |
|||||||||||
Comment 1 by bugdroid1@chromium.org
, Nov 10 2017