fs: make dumpable=2 require fully qualified path

When the suid_dumpable sysctl is set to "2", and there is no core dump
pipe defined in the core_pattern sysctl, a local user can cause core files
to be written to root-writable directories, potentially with
user-controlled content.

This means an admin can unknowningly reintroduce a variation of
CVE-2006-2451, allowing local users to gain root privileges.

  $ cat /proc/sys/fs/suid_dumpable
  2
  $ cat /proc/sys/kernel/core_pattern
  core
  $ ulimit -c unlimited
  $ cd /
  $ ls -l core
  ls: cannot access core: No such file or directory
  $ touch core
  touch: cannot touch `core': Permission denied
  $ OHAI="evil-string-here" ping localhost >/dev/null 2>&1 &
  $ pid=$!
  $ sleep 1
  $ kill -SEGV $pid
  $ ls -l core
  -rw------- 1 root kees 458752 Jun 21 11:35 core
  $ sudo strings core | grep evil
  OHAI=evil-string-here

While cron has been fixed to abort reading a file when there is any
parse error, there are still other sensitive directories that will read
any file present and skip unparsable lines.

Instead of introducing a suid_dumpable=3 mode and breaking all users of
mode 2, this only disables the unsafe portion of mode 2 (writing to disk
via relative path).  Most users of mode 2 (e.g.  Chrome OS) already use
a core dump pipe handler, so this change will not break them.  For the
situations where a pipe handler is not defined but mode 2 is still
active, crash dumps will only be written to fully qualified paths.  If a
relative path is defined (e.g.  the default "core" pattern), dump
attempts will trigger a printk yelling about the lack of a fully
qualified path.

Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Alan Cox <alan@linux.intel.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Doug Ledford <dledford@redhat.com>
Cc: Serge Hallyn <serge.hallyn@canonical.com>
Cc: James Morris <james.l.morris@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Kees Cook 2012-07-30 14:39:15 -07:00 committed by Linus Torvalds
parent 779302e678
commit 9520628e8c
2 changed files with 26 additions and 9 deletions

View File

@ -163,16 +163,22 @@ This value can be used to query and set the core dump mode for setuid
or otherwise protected/tainted binaries. The modes are or otherwise protected/tainted binaries. The modes are
0 - (default) - traditional behaviour. Any process which has changed 0 - (default) - traditional behaviour. Any process which has changed
privilege levels or is execute only will not be dumped privilege levels or is execute only will not be dumped.
1 - (debug) - all processes dump core when possible. The core dump is 1 - (debug) - all processes dump core when possible. The core dump is
owned by the current user and no security is applied. This is owned by the current user and no security is applied. This is
intended for system debugging situations only. Ptrace is unchecked. intended for system debugging situations only. Ptrace is unchecked.
This is insecure as it allows regular users to examine the memory
contents of privileged processes.
2 - (suidsafe) - any binary which normally would not be dumped is dumped 2 - (suidsafe) - any binary which normally would not be dumped is dumped
readable by root only. This allows the end user to remove anyway, but only if the "core_pattern" kernel sysctl is set to
such a dump but not access it directly. For security reasons either a pipe handler or a fully qualified path. (For more details
core dumps in this mode will not overwrite one another or on this limitation, see CVE-2006-2451.) This mode is appropriate
other files. This mode is appropriate when administrators are when administrators are attempting to debug problems in a normal
attempting to debug problems in a normal environment. environment, and either have a core dump pipe handler that knows
to treat privileged core dumps with care, or specific directory
defined for catching core dumps. If a core dump happens without
a pipe handler or fully qualifid path, a message will be emitted
to syslog warning about the lack of a correct setting.
============================================================== ==============================================================

View File

@ -2111,6 +2111,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
int retval = 0; int retval = 0;
int flag = 0; int flag = 0;
int ispipe; int ispipe;
bool need_nonrelative = false;
static atomic_t core_dump_count = ATOMIC_INIT(0); static atomic_t core_dump_count = ATOMIC_INIT(0);
struct coredump_params cprm = { struct coredump_params cprm = {
.signr = signr, .signr = signr,
@ -2136,14 +2137,16 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
if (!cred) if (!cred)
goto fail; goto fail;
/* /*
* We cannot trust fsuid as being the "true" uid of the * We cannot trust fsuid as being the "true" uid of the process
* process nor do we know its entire history. We only know it * nor do we know its entire history. We only know it was tainted
* was tainted so we dump it as root in mode 2. * so we dump it as root in mode 2, and only into a controlled
* environment (pipe handler or fully qualified path).
*/ */
if (__get_dumpable(cprm.mm_flags) == 2) { if (__get_dumpable(cprm.mm_flags) == 2) {
/* Setuid core dump mode */ /* Setuid core dump mode */
flag = O_EXCL; /* Stop rewrite attacks */ flag = O_EXCL; /* Stop rewrite attacks */
cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */ cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */
need_nonrelative = true;
} }
retval = coredump_wait(exit_code, &core_state); retval = coredump_wait(exit_code, &core_state);
@ -2223,6 +2226,14 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
if (cprm.limit < binfmt->min_coredump) if (cprm.limit < binfmt->min_coredump)
goto fail_unlock; goto fail_unlock;
if (need_nonrelative && cn.corename[0] != '/') {
printk(KERN_WARNING "Pid %d(%s) can only dump core "\
"to fully qualified path!\n",
task_tgid_vnr(current), current->comm);
printk(KERN_WARNING "Skipping core dump\n");
goto fail_unlock;
}
cprm.file = filp_open(cn.corename, cprm.file = filp_open(cn.corename,
O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
0600); 0600);