Re: [PATCH] ptrace: use fsuid, fsgid, effective creds for fs access checks
On Sat, Dec 5, 2015 at 6:04 PM, Jann Hornwrote: > By checking the effective credentials instead of the real UID / > permitted capabilities, ensure that the calling process actually > intended to use its credentials. > > To ensure that all ptrace checks use the correct caller > credentials (e.g. in case out-of-tree code or newly added code > omits the PTRACE_MODE_*CREDS flag), use two new flags and > require one of them to be set. > > The problem was that when a privileged task had temporarily dropped > its privileges, e.g. by calling setreuid(0, user_uid), with the > intent to perform following syscalls with the credentials of > a user, it still passed ptrace access checks that the user would > not be able to pass. > > While an attacker should not be able to convince the privileged > task to perform a ptrace() syscall, this is a problem because the > ptrace access check is reused for things in procfs. > > In particular, the following somewhat interesting procfs entries > only rely on ptrace access checks: > > /proc/$pid/stat - uses the check for determining whether pointers > should be visible, useful for bypassing ASLR > /proc/$pid/maps - also useful for bypassing ASLR > /proc/$pid/cwd - useful for gaining access to restricted > directories that contain files with lax permissions, e.g. in > this scenario: > lrwxrwxrwx root root /proc/13020/cwd -> /root/foobar > drwx-- root root /root > drwxr-xr-x root root /root/foobar > -rw-r--r-- root root /root/foobar/secret > > Therefore, on a system where a root-owned mode 6755 binary > changes its effective credentials as described and then dumps a > user-specified file, this could be used by an attacker to reveal > the memory layout of root's processes or reveal the contents of > files he is not allowed to access (through /proc/$pid/cwd). > > Signed-off-by: Jann Horn > --- > fs/proc/array.c| 2 +- > fs/proc/base.c | 21 +++-- > fs/proc/namespaces.c | 4 ++-- > include/linux/ptrace.h | 24 +++- > kernel/events/core.c | 2 +- > kernel/futex.c | 2 +- > kernel/futex_compat.c | 2 +- > kernel/kcmp.c | 4 ++-- > kernel/ptrace.c| 36 +--- > mm/process_vm_access.c | 2 +- > security/commoncap.c | 7 ++- > 11 files changed, 78 insertions(+), 28 deletions(-) > > diff --git a/fs/proc/array.c b/fs/proc/array.c > index d73291f..b6c00ce 100644 > --- a/fs/proc/array.c > +++ b/fs/proc/array.c > @@ -395,7 +395,7 @@ static int do_task_stat(struct seq_file *m, struct > pid_namespace *ns, > > state = *get_task_state(task); > vsize = eip = esp = 0; > - permitted = ptrace_may_access(task, PTRACE_MODE_READ | > PTRACE_MODE_NOAUDIT); > + permitted = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS | > PTRACE_MODE_NOAUDIT); > mm = get_task_mm(task); > if (mm) { > vsize = task_vsize(mm); > diff --git a/fs/proc/base.c b/fs/proc/base.c > index bd3e9e6..c0a2f29 100644 > --- a/fs/proc/base.c > +++ b/fs/proc/base.c > @@ -403,7 +403,7 @@ static const struct file_operations proc_pid_cmdline_ops > = { > static int proc_pid_auxv(struct seq_file *m, struct pid_namespace *ns, > struct pid *pid, struct task_struct *task) > { > - struct mm_struct *mm = mm_access(task, PTRACE_MODE_READ); > + struct mm_struct *mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); > if (mm && !IS_ERR(mm)) { > unsigned int nwords = 0; > do { > @@ -430,7 +430,8 @@ static int proc_pid_wchan(struct seq_file *m, struct > pid_namespace *ns, > > wchan = get_wchan(task); > > - if (wchan && ptrace_may_access(task, PTRACE_MODE_READ) && > !lookup_symbol_name(wchan, symname)) > + if (wchan && ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS) > + && !lookup_symbol_name(wchan, symname)) > seq_printf(m, "%s", symname); > else > seq_putc(m, '0'); > @@ -444,7 +445,7 @@ static int lock_trace(struct task_struct *task) > int err = mutex_lock_killable(>signal->cred_guard_mutex); > if (err) > return err; > - if (!ptrace_may_access(task, PTRACE_MODE_ATTACH)) { > + if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_FSCREDS)) { > mutex_unlock(>signal->cred_guard_mutex); > return -EPERM; > } > @@ -697,7 +698,7 @@ static int proc_fd_access_allowed(struct inode *inode) > */ > task = get_proc_task(inode); > if (task) { > - allowed = ptrace_may_access(task, PTRACE_MODE_READ); > + allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS); > put_task_struct(task); > } > return allowed; > @@ -732,7 +733,7 @@ static bool has_pid_permissions(struct pid_namespace *pid, >
Re: [PATCH] ptrace: use fsuid, fsgid, effective creds for fs access checks
On Mon, Dec 07, 2015 at 12:32:06PM -0800, Kees Cook wrote: > On Sat, Dec 5, 2015 at 6:04 PM, Jann Hornwrote: [...] > > - if (ptrace_may_access(task, PTRACE_MODE_READ)) { > > + if (ptrace_may_access(task, PTRACE_MODE_READ | > > PTRACE_MODE_FSCREDS)) { > > This should maybe use the PTRACE_MODE_READ_FSCREDS macro? Oh, yes. I don't know how I missed that. :/ > > error = ns_get_path(_path, task, ns_ops); > > if (!error) > > nd_jump_link(_path); > > @@ -63,7 +63,7 @@ static int proc_ns_readlink(struct dentry *dentry, char > > __user *buffer, int bufl > > if (!task) > > return res; > > > > - if (ptrace_may_access(task, PTRACE_MODE_READ)) { > > + if (ptrace_may_access(task, PTRACE_MODE_READ | > > PTRACE_MODE_FSCREDS)) { > > same here? Yes. signature.asc Description: Digital signature
Re: [PATCH] ptrace: use fsuid, fsgid, effective creds for fs access checks
Whoops. After Kees pointed out my last mistake, I decided to grep around a bit to make sure I didn't miss anything else and noticed that apparently, Yama and Smack aren't completely aware that the ptrace access mode can have flags ORed in? Until now, it was just the NOAUDIT flag for /proc/$pid/stat, but with my patch, that would have been broken completely as far as I can tell. I don't use either of those LSMs and didn't test with them. Can the LSM maintainers have a look at this and say whether this looks okay now? -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] ptrace: use fsuid, fsgid, effective creds for fs access checks
By checking the effective credentials instead of the real UID / permitted capabilities, ensure that the calling process actually intended to use its credentials. To ensure that all ptrace checks use the correct caller credentials (e.g. in case out-of-tree code or newly added code omits the PTRACE_MODE_*CREDS flag), use two new flags and require one of them to be set. The problem was that when a privileged task had temporarily dropped its privileges, e.g. by calling setreuid(0, user_uid), with the intent to perform following syscalls with the credentials of a user, it still passed ptrace access checks that the user would not be able to pass. While an attacker should not be able to convince the privileged task to perform a ptrace() syscall, this is a problem because the ptrace access check is reused for things in procfs. In particular, the following somewhat interesting procfs entries only rely on ptrace access checks: /proc/$pid/stat - uses the check for determining whether pointers should be visible, useful for bypassing ASLR /proc/$pid/maps - also useful for bypassing ASLR /proc/$pid/cwd - useful for gaining access to restricted directories that contain files with lax permissions, e.g. in this scenario: lrwxrwxrwx root root /proc/13020/cwd -> /root/foobar drwx-- root root /root drwxr-xr-x root root /root/foobar -rw-r--r-- root root /root/foobar/secret Therefore, on a system where a root-owned mode 6755 binary changes its effective credentials as described and then dumps a user-specified file, this could be used by an attacker to reveal the memory layout of root's processes or reveal the contents of files he is not allowed to access (through /proc/$pid/cwd). Signed-off-by: Jann Horn--- fs/proc/array.c| 2 +- fs/proc/base.c | 21 +++-- fs/proc/namespaces.c | 4 ++-- include/linux/ptrace.h | 24 +++- kernel/events/core.c | 2 +- kernel/futex.c | 2 +- kernel/futex_compat.c | 2 +- kernel/kcmp.c | 4 ++-- kernel/ptrace.c| 36 +--- mm/process_vm_access.c | 2 +- security/commoncap.c | 7 ++- 11 files changed, 78 insertions(+), 28 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index d73291f..b6c00ce 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -395,7 +395,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, state = *get_task_state(task); vsize = eip = esp = 0; - permitted = ptrace_may_access(task, PTRACE_MODE_READ | PTRACE_MODE_NOAUDIT); + permitted = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS | PTRACE_MODE_NOAUDIT); mm = get_task_mm(task); if (mm) { vsize = task_vsize(mm); diff --git a/fs/proc/base.c b/fs/proc/base.c index bd3e9e6..c0a2f29 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -403,7 +403,7 @@ static const struct file_operations proc_pid_cmdline_ops = { static int proc_pid_auxv(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task) { - struct mm_struct *mm = mm_access(task, PTRACE_MODE_READ); + struct mm_struct *mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); if (mm && !IS_ERR(mm)) { unsigned int nwords = 0; do { @@ -430,7 +430,8 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns, wchan = get_wchan(task); - if (wchan && ptrace_may_access(task, PTRACE_MODE_READ) && !lookup_symbol_name(wchan, symname)) + if (wchan && ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS) + && !lookup_symbol_name(wchan, symname)) seq_printf(m, "%s", symname); else seq_putc(m, '0'); @@ -444,7 +445,7 @@ static int lock_trace(struct task_struct *task) int err = mutex_lock_killable(>signal->cred_guard_mutex); if (err) return err; - if (!ptrace_may_access(task, PTRACE_MODE_ATTACH)) { + if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_FSCREDS)) { mutex_unlock(>signal->cred_guard_mutex); return -EPERM; } @@ -697,7 +698,7 @@ static int proc_fd_access_allowed(struct inode *inode) */ task = get_proc_task(inode); if (task) { - allowed = ptrace_may_access(task, PTRACE_MODE_READ); + allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS); put_task_struct(task); } return allowed; @@ -732,7 +733,7 @@ static bool has_pid_permissions(struct pid_namespace *pid, return true; if (in_group_p(pid->pid_gid)) return true; - return ptrace_may_access(task, PTRACE_MODE_READ); + return ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS); } @@ -809,7 +810,7 @@ struct mm_struct *proc_mem_open(struct
Re: [PATCH] ptrace: use fsuid, fsgid, effective creds for fs access checks
On Mon, Nov 09, 2015 at 12:55:54PM -0800, Andrew Morton wrote: > > --- a/fs/proc/array.c > > +++ b/fs/proc/array.c > > @@ -395,7 +395,8 @@ static int do_task_stat(struct seq_file *m, struct > > pid_namespace *ns, > > > > state = *get_task_state(task); > > vsize = eip = esp = 0; > > - permitted = ptrace_may_access(task, PTRACE_MODE_READ | > > PTRACE_MODE_NOAUDIT); > > + permitted = ptrace_may_access(task, > > + PTRACE_MODE_READ | PTRACE_MODE_NOAUDIT | PTRACE_MODE_FSCREDS); > > There's lots of ugliness in the patch to do with fitting code into 80 cols. > Can we do > > #define PTRACE_foo (PTRACE_MODE_READ|PTRACE_MODE_FSCREDS) > > to avoid all that? Or even simply bypass the 80-cols rule. Making code ugly or less easy to read for sake of an arbitrary rule is often not fun, and that's even more so when it comes to security fixes that people are expected to easily understand next time they put their fingers there. Willy -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] ptrace: use fsuid, fsgid, effective creds for fs access checks
On Mon, Nov 09, 2015 at 12:55:54PM -0800, Andrew Morton wrote: > On Sun, 8 Nov 2015 13:08:36 +0100 Jann Hornwrote: > > > By checking the effective credentials instead of the real UID / > > permitted capabilities, ensure that the calling process actually > > intended to use its credentials. > > > > To ensure that all ptrace checks use the correct caller > > credentials (e.g. in case out-of-tree code or newly added code > > omits the PTRACE_MODE_*CREDS flag), use two new flags and > > require one of them to be set. > > > > The problem was that when a privileged task had temporarily dropped > > its privileges, e.g. by calling setreuid(0, user_uid), with the > > intent to perform following syscalls with the credentials of > > a user, it still passed ptrace access checks that the user would > > not be able to pass. > > > > While an attacker should not be able to convince the privileged > > task to perform a ptrace() syscall, this is a problem because the > > ptrace access check is reused for things in procfs. > > > > In particular, the following somewhat interesting procfs entries > > only rely on ptrace access checks: > > > > /proc/$pid/stat - uses the check for determining whether pointers > > should be visible, useful for bypassing ASLR > > /proc/$pid/maps - also useful for bypassing ASLR > > /proc/$pid/cwd - useful for gaining access to restricted > > directories that contain files with lax permissions, e.g. in > > this scenario: > > lrwxrwxrwx root root /proc/13020/cwd -> /root/foobar > > drwx-- root root /root > > drwxr-xr-x root root /root/foobar > > -rw-r--r-- root root /root/foobar/secret > > > > Therefore, on a system where a root-owned mode 6755 binary > > changes its effective credentials as described and then dumps a > > user-specified file, this could be used by an attacker to reveal > > the memory layout of root's processes or reveal the contents of > > files he is not allowed to access (through /proc/$pid/cwd). > > I'll await reviewer input on this one. Meanwhile, a bunch of > minor(ish) things... > > > --- a/fs/proc/array.c > > +++ b/fs/proc/array.c > > @@ -395,7 +395,8 @@ static int do_task_stat(struct seq_file *m, struct > > pid_namespace *ns, > > > > state = *get_task_state(task); > > vsize = eip = esp = 0; > > - permitted = ptrace_may_access(task, PTRACE_MODE_READ | > > PTRACE_MODE_NOAUDIT); > > + permitted = ptrace_may_access(task, > > + PTRACE_MODE_READ | PTRACE_MODE_NOAUDIT | PTRACE_MODE_FSCREDS); > > There's lots of ugliness in the patch to do with fitting code into 80 cols. I agree. > Can we do > > #define PTRACE_foo (PTRACE_MODE_READ|PTRACE_MODE_FSCREDS) > > to avoid all that? Hm. All combinations of the PTRACE_MODE_*CREDS flags with PTRACE_MODE_{READ,ATTACH} plus optionally PTRACE_MODE_NOAUDIT make sense, I think. So your suggestion would be to create four new #defines PTRACE_MODE_{READ,ATTACH}_{FSCREDS,REALCREDS} and then let callers OR in the PTRACE_MODE_NOAUDIT flag if needed? > > --- a/include/linux/ptrace.h > > +++ b/include/linux/ptrace.h > > @@ -57,7 +57,22 @@ extern void exit_ptrace(struct task_struct *tracer, > > struct list_head *dead); > > #define PTRACE_MODE_READ 0x01 > > #define PTRACE_MODE_ATTACH 0x02 > > #define PTRACE_MODE_NOAUDIT0x04 > > -/* Returns true on success, false on denial. */ > > +#define PTRACE_MODE_FSCREDS 0x08 > > +#define PTRACE_MODE_REALCREDS 0x10 > > +/** > > + * ptrace_may_access - check whether the caller is permitted to access > > + * a target task. > > + * @task: target task > > + * @mode: selects type of access and caller credentials > > + * > > + * Returns true on success, false on denial. > > + * > > + * One of the flags PTRACE_MODE_FSCREDS and PTRACE_MODE_REALCREDS must > > + * be set in @mode to specify whether the access was requested through > > + * a filesystem syscall (should use effective capabilities and fsuid > > + * of the caller) or through an explicit syscall such as > > + * process_vm_writev or ptrace (and should use the real credentials). > > + */ > > extern bool ptrace_may_access(struct task_struct *task, unsigned int mode); > > It is unconventional to put the kernedoc in the header - people have > been trained to look for it in the .c file. OK, will fix that. I thought it would be appropriate to put it in the header since that one-line comment was already there. > > +++ b/kernel/ptrace.c > > @@ -219,6 +219,13 @@ static int ptrace_has_cap(struct user_namespace *ns, > > unsigned int mode) > > static int __ptrace_may_access(struct task_struct *task, unsigned int mode) > > { > > const struct cred *cred = current_cred(), *tcred; > > + kuid_t caller_uid; > > + kgid_t caller_gid; > > + > > + if (!(mode & PTRACE_MODE_FSCREDS) != !(mode & PTRACE_MODE_REALCREDS)) { > > So setting either one of these and not the other is an error. How > come? Oh. Sorry about that. I only added PTRACE_MODE_REALCREDS
Re: [PATCH] ptrace: use fsuid, fsgid, effective creds for fs access checks
On Mon, 9 Nov 2015 22:12:09 +0100 Jann Hornwrote: > > > Can we do > > > > #define PTRACE_foo (PTRACE_MODE_READ|PTRACE_MODE_FSCREDS) > > > > to avoid all that? > > Hm. All combinations of the PTRACE_MODE_*CREDS flags with > PTRACE_MODE_{READ,ATTACH} plus optionally PTRACE_MODE_NOAUDIT > make sense, I think. So your suggestion would be to create > four new #defines > PTRACE_MODE_{READ,ATTACH}_{FSCREDS,REALCREDS} and then let > callers OR in the PTRACE_MODE_NOAUDIT flag if needed? If these flag combinations have an identifiable concept behind them then sure, it makes sense to capture that via a well-chosen identifier. -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] ptrace: use fsuid, fsgid, effective creds for fs access checks
On Sun, 8 Nov 2015 13:08:36 +0100 Jann Hornwrote: > By checking the effective credentials instead of the real UID / > permitted capabilities, ensure that the calling process actually > intended to use its credentials. > > To ensure that all ptrace checks use the correct caller > credentials (e.g. in case out-of-tree code or newly added code > omits the PTRACE_MODE_*CREDS flag), use two new flags and > require one of them to be set. > > The problem was that when a privileged task had temporarily dropped > its privileges, e.g. by calling setreuid(0, user_uid), with the > intent to perform following syscalls with the credentials of > a user, it still passed ptrace access checks that the user would > not be able to pass. > > While an attacker should not be able to convince the privileged > task to perform a ptrace() syscall, this is a problem because the > ptrace access check is reused for things in procfs. > > In particular, the following somewhat interesting procfs entries > only rely on ptrace access checks: > > /proc/$pid/stat - uses the check for determining whether pointers > should be visible, useful for bypassing ASLR > /proc/$pid/maps - also useful for bypassing ASLR > /proc/$pid/cwd - useful for gaining access to restricted > directories that contain files with lax permissions, e.g. in > this scenario: > lrwxrwxrwx root root /proc/13020/cwd -> /root/foobar > drwx-- root root /root > drwxr-xr-x root root /root/foobar > -rw-r--r-- root root /root/foobar/secret > > Therefore, on a system where a root-owned mode 6755 binary > changes its effective credentials as described and then dumps a > user-specified file, this could be used by an attacker to reveal > the memory layout of root's processes or reveal the contents of > files he is not allowed to access (through /proc/$pid/cwd). I'll await reviewer input on this one. Meanwhile, a bunch of minor(ish) things... > --- a/fs/proc/array.c > +++ b/fs/proc/array.c > @@ -395,7 +395,8 @@ static int do_task_stat(struct seq_file *m, struct > pid_namespace *ns, > > state = *get_task_state(task); > vsize = eip = esp = 0; > - permitted = ptrace_may_access(task, PTRACE_MODE_READ | > PTRACE_MODE_NOAUDIT); > + permitted = ptrace_may_access(task, > + PTRACE_MODE_READ | PTRACE_MODE_NOAUDIT | PTRACE_MODE_FSCREDS); There's lots of ugliness in the patch to do with fitting code into 80 cols. Can we do #define PTRACE_foo (PTRACE_MODE_READ|PTRACE_MODE_FSCREDS) to avoid all that? > --- a/include/linux/ptrace.h > +++ b/include/linux/ptrace.h > @@ -57,7 +57,22 @@ extern void exit_ptrace(struct task_struct *tracer, struct > list_head *dead); > #define PTRACE_MODE_READ 0x01 > #define PTRACE_MODE_ATTACH 0x02 > #define PTRACE_MODE_NOAUDIT 0x04 > -/* Returns true on success, false on denial. */ > +#define PTRACE_MODE_FSCREDS 0x08 > +#define PTRACE_MODE_REALCREDS 0x10 > +/** > + * ptrace_may_access - check whether the caller is permitted to access > + * a target task. > + * @task: target task > + * @mode: selects type of access and caller credentials > + * > + * Returns true on success, false on denial. > + * > + * One of the flags PTRACE_MODE_FSCREDS and PTRACE_MODE_REALCREDS must > + * be set in @mode to specify whether the access was requested through > + * a filesystem syscall (should use effective capabilities and fsuid > + * of the caller) or through an explicit syscall such as > + * process_vm_writev or ptrace (and should use the real credentials). > + */ > extern bool ptrace_may_access(struct task_struct *task, unsigned int mode); It is unconventional to put the kernedoc in the header - people have been trained to look for it in the .c file. > +++ b/kernel/ptrace.c > @@ -219,6 +219,13 @@ static int ptrace_has_cap(struct user_namespace *ns, > unsigned int mode) > static int __ptrace_may_access(struct task_struct *task, unsigned int mode) > { > const struct cred *cred = current_cred(), *tcred; > + kuid_t caller_uid; > + kgid_t caller_gid; > + > + if (!(mode & PTRACE_MODE_FSCREDS) != !(mode & PTRACE_MODE_REALCREDS)) { So setting either one of these and not the other is an error. How come? > + WARN(1, "denying ptrace access check without > PTRACE_MODE_*CREDS\n"); This warning cannot be triggered by malicious userspace, I trust? -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] ptrace: use fsuid, fsgid, effective creds for fs access checks
By checking the effective credentials instead of the real UID / permitted capabilities, ensure that the calling process actually intended to use its credentials. To ensure that all ptrace checks use the correct caller credentials (e.g. in case out-of-tree code or newly added code omits the PTRACE_MODE_*CREDS flag), use two new flags and require one of them to be set. The problem was that when a privileged task had temporarily dropped its privileges, e.g. by calling setreuid(0, user_uid), with the intent to perform following syscalls with the credentials of a user, it still passed ptrace access checks that the user would not be able to pass. While an attacker should not be able to convince the privileged task to perform a ptrace() syscall, this is a problem because the ptrace access check is reused for things in procfs. In particular, the following somewhat interesting procfs entries only rely on ptrace access checks: /proc/$pid/stat - uses the check for determining whether pointers should be visible, useful for bypassing ASLR /proc/$pid/maps - also useful for bypassing ASLR /proc/$pid/cwd - useful for gaining access to restricted directories that contain files with lax permissions, e.g. in this scenario: lrwxrwxrwx root root /proc/13020/cwd -> /root/foobar drwx-- root root /root drwxr-xr-x root root /root/foobar -rw-r--r-- root root /root/foobar/secret Therefore, on a system where a root-owned mode 6755 binary changes its effective credentials as described and then dumps a user-specified file, this could be used by an attacker to reveal the memory layout of root's processes or reveal the contents of files he is not allowed to access (through /proc/$pid/cwd). Signed-off-by: Jann Horn--- fs/proc/array.c| 3 ++- fs/proc/base.c | 24 ++-- fs/proc/namespaces.c | 4 ++-- include/linux/ptrace.h | 17 - kernel/events/core.c | 2 +- kernel/futex.c | 2 +- kernel/futex_compat.c | 2 +- kernel/kcmp.c | 6 -- kernel/ptrace.c| 37 ++--- mm/process_vm_access.c | 2 +- security/commoncap.c | 7 ++- 11 files changed, 78 insertions(+), 28 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index f60f012..00928c6 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -395,7 +395,8 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, state = *get_task_state(task); vsize = eip = esp = 0; - permitted = ptrace_may_access(task, PTRACE_MODE_READ | PTRACE_MODE_NOAUDIT); + permitted = ptrace_may_access(task, + PTRACE_MODE_READ | PTRACE_MODE_NOAUDIT | PTRACE_MODE_FSCREDS); mm = get_task_mm(task); if (mm) { vsize = task_vsize(mm); diff --git a/fs/proc/base.c b/fs/proc/base.c index b25eee4..d6b9475 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -403,7 +403,8 @@ static const struct file_operations proc_pid_cmdline_ops = { static int proc_pid_auxv(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task) { - struct mm_struct *mm = mm_access(task, PTRACE_MODE_READ); + struct mm_struct *mm = mm_access(task, + PTRACE_MODE_READ | PTRACE_MODE_FSCREDS); if (mm && !IS_ERR(mm)) { unsigned int nwords = 0; do { @@ -431,7 +432,8 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns, wchan = get_wchan(task); if (lookup_symbol_name(wchan, symname) < 0) { - if (!ptrace_may_access(task, PTRACE_MODE_READ)) + if (!ptrace_may_access(task, + PTRACE_MODE_READ | PTRACE_MODE_FSCREDS)) return 0; seq_printf(m, "%lu", wchan); } else { @@ -447,7 +449,8 @@ static int lock_trace(struct task_struct *task) int err = mutex_lock_killable(>signal->cred_guard_mutex); if (err) return err; - if (!ptrace_may_access(task, PTRACE_MODE_ATTACH)) { + if (!ptrace_may_access(task, + PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS)) { mutex_unlock(>signal->cred_guard_mutex); return -EPERM; } @@ -700,7 +703,8 @@ static int proc_fd_access_allowed(struct inode *inode) */ task = get_proc_task(inode); if (task) { - allowed = ptrace_may_access(task, PTRACE_MODE_READ); + allowed = ptrace_may_access(task, + PTRACE_MODE_READ | PTRACE_MODE_FSCREDS); put_task_struct(task); } return allowed; @@ -735,7 +739,7 @@ static bool has_pid_permissions(struct pid_namespace *pid, return true; if (in_group_p(pid->pid_gid)) return true; - return ptrace_may_access(task, PTRACE_MODE_READ);