New issue
Advanced search Search tips

Issue 781376 link

Starred by 2 users

Issue metadata

Status: Fixed
Owner:
Closed: Nov 2017
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: Chrome
Pri: 2
Type: Feature



Sign in to add a comment

kernel: /proc/self/mem hardening

Project Member Reported by vapier@chromium.org, Nov 3 2017

Issue description

we 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).
 
Project Member

Comment 1 by bugdroid1@chromium.org, Nov 10 2017

Labels: merge-merged-chromeos-4.4
The following revision refers to this bug:
  https://chromium.googlesource.com/chromiumos/third_party/kernel/+/f29788b89a297de428e22f2381c67ce06b9d29b3

commit f29788b89a297de428e22f2381c67ce06b9d29b3
Author: Mike Frysinger <vapier@chromium.org>
Date: Fri Nov 10 22:05: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
Signed-off-by: Mike Frysinger <vapier@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/753406
Reviewed-by: Guenter Roeck <groeck@chromium.org>

[modify] https://crrev.com/f29788b89a297de428e22f2381c67ce06b9d29b3/fs/proc/base.c
[modify] https://crrev.com/f29788b89a297de428e22f2381c67ce06b9d29b3/security/chromiumos/Kconfig

Blocking: -766253
Project Member

Comment 3 by bugdroid1@chromium.org, Nov 13 2017

Labels: merge-merged-chromeos-4.12
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

Project Member

Comment 4 by bugdroid1@chromium.org, Nov 14 2017

Labels: merge-merged-chromeos-3.18
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

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


Comment 6 by vapier@chromium.org, Nov 15 2017

Owner: vapier@chromium.org
Status: Fixed (was: Available)
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 :).
Project Member

Comment 7 by bugdroid1@chromium.org, Nov 18 2017

Labels: merge-merged-chromeos-3.10
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

Project Member

Comment 8 by bugdroid1@chromium.org, Nov 19 2017

Labels: merge-merged-chromeos-3.8
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

Project Member

Comment 9 by bugdroid1@chromium.org, Nov 20 2017

Labels: merge-merged-chromeos-3.14
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

Comment 10 by dchan@chromium.org, Jan 22 2018

Status: Archived (was: Fixed)

Comment 11 by dchan@chromium.org, Jan 23 2018

Status: Fixed (was: Archived)
Project Member

Comment 12 by bugdroid1@chromium.org, Mar 3 2018

Labels: merge-merged-chromeos-4.14
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

Please note, this breaks usage of rr ( https://rr-project.org/ ) on Chrome OS.
A configurable knob would be appreciated.

sorry, but no.  putting in a runtime knob would significantly weaken the security provided by it.
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.
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