aboutsummaryrefslogtreecommitdiffstats
path: root/fs/coredump.c
diff options
context:
space:
mode:
authorJann Horn <jann@thejh.net>2015-09-09 18:38:28 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-09-10 16:29:01 -0400
commitfbb1816942c04429e85dbf4c1a080accc534299e (patch)
tree4b04d23fa36c46975589aaeec1201d2aecd32f45 /fs/coredump.c
parentbb304a5c6fc63d8506cd9741a3a5f35b73605625 (diff)
fs: if a coredump already exists, unlink and recreate with O_EXCL
It was possible for an attacking user to trick root (or another user) into writing his coredumps into an attacker-readable, pre-existing file using rename() or link(), causing the disclosure of secret data from the victim process' virtual memory. Depending on the configuration, it was also possible to trick root into overwriting system files with coredumps. Fix that issue by never writing coredumps into existing files. Requirements for the attack: - The attack only applies if the victim's process has a nonzero RLIMIT_CORE and is dumpable. - The attacker can trick the victim into coredumping into an attacker-writable directory D, either because the core_pattern is relative and the victim's cwd is attacker-writable or because an absolute core_pattern pointing to a world-writable directory is used. - The attacker has one of these: A: on a system with protected_hardlinks=0: execute access to a folder containing a victim-owned, attacker-readable file on the same partition as D, and the victim-owned file will be deleted before the main part of the attack takes place. (In practice, there are lots of files that fulfill this condition, e.g. entries in Debian's /var/lib/dpkg/info/.) This does not apply to most Linux systems because most distros set protected_hardlinks=1. B: on a system with protected_hardlinks=1: execute access to a folder containing a victim-owned, attacker-readable and attacker-writable file on the same partition as D, and the victim-owned file will be deleted before the main part of the attack takes place. (This seems to be uncommon.) C: on any system, independent of protected_hardlinks: write access to a non-sticky folder containing a victim-owned, attacker-readable file on the same partition as D (This seems to be uncommon.) The basic idea is that the attacker moves the victim-owned file to where he expects the victim process to dump its core. The victim process dumps its core into the existing file, and the attacker reads the coredump from it. If the attacker can't move the file because he does not have write access to the containing directory, he can instead link the file to a directory he controls, then wait for the original link to the file to be deleted (because the kernel checks that the link count of the corefile is 1). A less reliable variant that requires D to be non-sticky works with link() and does not require deletion of the original link: link() the file into D, but then unlink() it directly before the kernel performs the link count check. On systems with protected_hardlinks=0, this variant allows an attacker to not only gain information from coredumps, but also clobber existing, victim-writable files with coredumps. (This could theoretically lead to a privilege escalation.) Signed-off-by: Jann Horn <jann@thejh.net> Cc: Kees Cook <keescook@chromium.org> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/coredump.c')
-rw-r--r--fs/coredump.c38
1 files changed, 32 insertions, 6 deletions
diff --git a/fs/coredump.c b/fs/coredump.c
index c5ecde6f3eed..b696dc2c220d 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -513,10 +513,10 @@ void do_coredump(const siginfo_t *siginfo)
513 const struct cred *old_cred; 513 const struct cred *old_cred;
514 struct cred *cred; 514 struct cred *cred;
515 int retval = 0; 515 int retval = 0;
516 int flag = 0;
517 int ispipe; 516 int ispipe;
518 struct files_struct *displaced; 517 struct files_struct *displaced;
519 bool need_nonrelative = false; 518 /* require nonrelative corefile path and be extra careful */
519 bool need_suid_safe = false;
520 bool core_dumped = false; 520 bool core_dumped = false;
521 static atomic_t core_dump_count = ATOMIC_INIT(0); 521 static atomic_t core_dump_count = ATOMIC_INIT(0);
522 struct coredump_params cprm = { 522 struct coredump_params cprm = {
@@ -550,9 +550,8 @@ void do_coredump(const siginfo_t *siginfo)
550 */ 550 */
551 if (__get_dumpable(cprm.mm_flags) == SUID_DUMP_ROOT) { 551 if (__get_dumpable(cprm.mm_flags) == SUID_DUMP_ROOT) {
552 /* Setuid core dump mode */ 552 /* Setuid core dump mode */
553 flag = O_EXCL; /* Stop rewrite attacks */
554 cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */ 553 cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */
555 need_nonrelative = true; 554 need_suid_safe = true;
556 } 555 }
557 556
558 retval = coredump_wait(siginfo->si_signo, &core_state); 557 retval = coredump_wait(siginfo->si_signo, &core_state);
@@ -633,7 +632,7 @@ void do_coredump(const siginfo_t *siginfo)
633 if (cprm.limit < binfmt->min_coredump) 632 if (cprm.limit < binfmt->min_coredump)
634 goto fail_unlock; 633 goto fail_unlock;
635 634
636 if (need_nonrelative && cn.corename[0] != '/') { 635 if (need_suid_safe && cn.corename[0] != '/') {
637 printk(KERN_WARNING "Pid %d(%s) can only dump core "\ 636 printk(KERN_WARNING "Pid %d(%s) can only dump core "\
638 "to fully qualified path!\n", 637 "to fully qualified path!\n",
639 task_tgid_vnr(current), current->comm); 638 task_tgid_vnr(current), current->comm);
@@ -641,8 +640,35 @@ void do_coredump(const siginfo_t *siginfo)
641 goto fail_unlock; 640 goto fail_unlock;
642 } 641 }
643 642
643 /*
644 * Unlink the file if it exists unless this is a SUID
645 * binary - in that case, we're running around with root
646 * privs and don't want to unlink another user's coredump.
647 */
648 if (!need_suid_safe) {
649 mm_segment_t old_fs;
650
651 old_fs = get_fs();
652 set_fs(KERNEL_DS);
653 /*
654 * If it doesn't exist, that's fine. If there's some
655 * other problem, we'll catch it at the filp_open().
656 */
657 (void) sys_unlink((const char __user *)cn.corename);
658 set_fs(old_fs);
659 }
660
661 /*
662 * There is a race between unlinking and creating the
663 * file, but if that causes an EEXIST here, that's
664 * fine - another process raced with us while creating
665 * the corefile, and the other process won. To userspace,
666 * what matters is that at least one of the two processes
667 * writes its coredump successfully, not which one.
668 */
644 cprm.file = filp_open(cn.corename, 669 cprm.file = filp_open(cn.corename,
645 O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, 670 O_CREAT | 2 | O_NOFOLLOW |
671 O_LARGEFILE | O_EXCL,
646 0600); 672 0600);
647 if (IS_ERR(cprm.file)) 673 if (IS_ERR(cprm.file))
648 goto fail_unlock; 674 goto fail_unlock;