* [PATCH] kernel: prevent submission of creds with higher privileges inside container
@ 2018-09-14 7:25 My Name
2018-09-14 10:20 ` kbuild test robot
0 siblings, 1 reply; 10+ messages in thread
From: My Name @ 2018-09-14 7:25 UTC (permalink / raw)
To: linux-kernel; +Cc: Xin Lin
From: Xin Lin <18650033736@163.com>
Adversaries often attack the Linux kernel via using
commit_creds(prepare_kernel_cred(0)) to submit ROOT
credential for the purpose of privilege escalation.
For processes inside the Linux container, the above
approach also works, because the container and the
host share the same Linux kernel. Therefore, we en-
force a check in commit_creds() before updating the
cred of the caller process. If the process is insi-
de a container (judging from the Namespace ID) and
try to submit credentials with higher privileges t-
han current (judging from the uid, gid, and cap_bset
in the new cred), we will stop the modification. We
consider that if the namespace ID of the process is
different from the init Namespace ID (enumed in /i-
nclude/linux/proc_ns.h), the process is inside a c-
ontainer. And if the uid/gid in the new cred is sm-
aller or the cap_bset (capability bounding set) in
the new cred is larger, it may be a privilege esca-
lation operation.
Signed-off-by: Xin Lin <18650033736@163.com>
---
kernel/cred.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/kernel/cred.c b/kernel/cred.c
index ecf0365..0496f32 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -19,6 +19,11 @@
#include <linux/security.h>
#include <linux/binfmts.h>
#include <linux/cn_proc.h>
+#include <linux/ipc_namespace.h>
+#include "../fs/mount.h"
+#include <net/net_namespace.h>
+#include <linux/capability.h>
+#include <linux/cgroup.h>
#if 0
#define kdebug(FMT, ...) \
@@ -33,6 +38,8 @@ do { \
} while (0)
#endif
+bool flag = true;
+static struct net *initnet;
static struct kmem_cache *cred_jar;
/* init to 2 - one for init_task, one to ensure it is never freed */
@@ -425,6 +432,22 @@ int commit_creds(struct cred *new)
struct task_struct *task = current;
const struct cred *old = task->real_cred;
+ if (flag) {
+ initnet = get_net_ns_by_pid(1);
+ flag = false;
+ }
+ if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
+ task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
+ task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
+ task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
+ task->nsproxy->net_ns->ns.inum != initnet->ns.inum ||
+ old->user_ns->ns.inum != PROC_USER_INIT_INO ||
+ task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
+ if (new->uid.val < old->uid.val || new->gid.val < old->gid.val
+ || new->cap_bset.cap[0] > old->cap_bset.cap[0])
+ return 0;
+ }
+
kdebug("commit_creds(%p{%d,%d})", new,
atomic_read(&new->usage),
read_cred_subscribers(new));
--
2.7.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] kernel: prevent submission of creds with higher privileges inside container
2018-09-14 7:25 [PATCH] kernel: prevent submission of creds with higher privileges inside container My Name
@ 2018-09-14 10:20 ` kbuild test robot
0 siblings, 0 replies; 10+ messages in thread
From: kbuild test robot @ 2018-09-14 10:20 UTC (permalink / raw)
To: My Name; +Cc: kbuild-all, linux-kernel, Xin Lin
[-- Attachment #1: Type: text/plain, Size: 6149 bytes --]
Hi Xin,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linus/master]
[also build test ERROR on v4.19-rc3 next-20180913]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/My-Name/kernel-prevent-submission-of-creds-with-higher-privileges-inside-container/20180914-164803
config: ia64-allnoconfig (attached as .config)
compiler: ia64-linux-gcc (GCC) 8.1.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=8.1.0 make.cross ARCH=ia64
All errors (new ones prefixed by >>):
kernel/cred.c: In function 'commit_creds':
kernel/cred.c:439:40: error: 'PROC_UTS_INIT_INO' undeclared (first use in this function)
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~~~~~~~~~~~~~~~~
kernel/cred.c:439:40: note: each undeclared identifier is reported only once for each function it appears in
kernel/cred.c:440:36: error: 'PROC_IPC_INIT_INO' undeclared (first use in this function)
task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
^~~~~~~~~~~~~~~~~
kernel/cred.c:442:49: error: 'PROC_PID_INIT_INO' undeclared (first use in this function)
task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
^~~~~~~~~~~~~~~~~
kernel/cred.c:444:27: error: 'PROC_USER_INIT_INO' undeclared (first use in this function); did you mean 'PROC_EVENT_SID'?
old->user_ns->ns.inum != PROC_USER_INIT_INO ||
^~~~~~~~~~~~~~~~~~
PROC_EVENT_SID
>> kernel/cred.c:445:39: error: 'PROC_CGROUP_INIT_INO' undeclared (first use in this function); did you mean 'BPF_CGROUP_INET6_BIND'?
task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
^~~~~~~~~~~~~~~~~~~~
BPF_CGROUP_INET6_BIND
vim +445 kernel/cred.c
415
416 /**
417 * commit_creds - Install new credentials upon the current task
418 * @new: The credentials to be assigned
419 *
420 * Install a new set of credentials to the current task, using RCU to replace
421 * the old set. Both the objective and the subjective credentials pointers are
422 * updated. This function may not be called if the subjective credentials are
423 * in an overridden state.
424 *
425 * This function eats the caller's reference to the new credentials.
426 *
427 * Always returns 0 thus allowing this function to be tail-called at the end
428 * of, say, sys_setgid().
429 */
430 int commit_creds(struct cred *new)
431 {
432 struct task_struct *task = current;
433 const struct cred *old = task->real_cred;
434
435 if (flag) {
436 initnet = get_net_ns_by_pid(1);
437 flag = false;
438 }
439 if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
440 task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
441 task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
> 442 task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
443 task->nsproxy->net_ns->ns.inum != initnet->ns.inum ||
444 old->user_ns->ns.inum != PROC_USER_INIT_INO ||
> 445 task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
446 if (new->uid.val < old->uid.val || new->gid.val < old->gid.val
447 || new->cap_bset.cap[0] > old->cap_bset.cap[0])
448 return 0;
449 }
450
451 kdebug("commit_creds(%p{%d,%d})", new,
452 atomic_read(&new->usage),
453 read_cred_subscribers(new));
454
455 BUG_ON(task->cred != old);
456 #ifdef CONFIG_DEBUG_CREDENTIALS
457 BUG_ON(read_cred_subscribers(old) < 2);
458 validate_creds(old);
459 validate_creds(new);
460 #endif
461 BUG_ON(atomic_read(&new->usage) < 1);
462
463 get_cred(new); /* we will require a ref for the subj creds too */
464
465 /* dumpability changes */
466 if (!uid_eq(old->euid, new->euid) ||
467 !gid_eq(old->egid, new->egid) ||
468 !uid_eq(old->fsuid, new->fsuid) ||
469 !gid_eq(old->fsgid, new->fsgid) ||
470 !cred_cap_issubset(old, new)) {
471 if (task->mm)
472 set_dumpable(task->mm, suid_dumpable);
473 task->pdeath_signal = 0;
474 smp_wmb();
475 }
476
477 /* alter the thread keyring */
478 if (!uid_eq(new->fsuid, old->fsuid))
479 key_fsuid_changed(task);
480 if (!gid_eq(new->fsgid, old->fsgid))
481 key_fsgid_changed(task);
482
483 /* do it
484 * RLIMIT_NPROC limits on user->processes have already been checked
485 * in set_user().
486 */
487 alter_cred_subscribers(new, 2);
488 if (new->user != old->user)
489 atomic_inc(&new->user->processes);
490 rcu_assign_pointer(task->real_cred, new);
491 rcu_assign_pointer(task->cred, new);
492 if (new->user != old->user)
493 atomic_dec(&old->user->processes);
494 alter_cred_subscribers(old, -2);
495
496 /* send notifications */
497 if (!uid_eq(new->uid, old->uid) ||
498 !uid_eq(new->euid, old->euid) ||
499 !uid_eq(new->suid, old->suid) ||
500 !uid_eq(new->fsuid, old->fsuid))
501 proc_id_connector(task, PROC_EVENT_UID);
502
503 if (!gid_eq(new->gid, old->gid) ||
504 !gid_eq(new->egid, old->egid) ||
505 !gid_eq(new->sgid, old->sgid) ||
506 !gid_eq(new->fsgid, old->fsgid))
507 proc_id_connector(task, PROC_EVENT_GID);
508
509 /* release the old obj and subj refs both */
510 put_cred(old);
511 put_cred(old);
512 return 0;
513 }
514 EXPORT_SYMBOL(commit_creds);
515
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 5651 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] kernel: prevent submission of creds with higher privileges inside container
@ 2018-09-14 10:55 My Name
2018-09-14 11:23 ` Jann Horn
2018-09-14 22:01 ` kbuild test robot
0 siblings, 2 replies; 10+ messages in thread
From: My Name @ 2018-09-14 10:55 UTC (permalink / raw)
To: linux-kernel; +Cc: Xin Lin
From: Xin Lin <18650033736@163.com>
Adversaries often attack the Linux kernel via using
commit_creds(prepare_kernel_cred(0)) to submit ROOT
credential for the purpose of privilege escalation.
For processes inside the Linux container, the above
approach also works, because the container and the
host share the same Linux kernel. Therefore, we en-
force a check in commit_creds() before updating the
cred of the caller process. If the process is insi-
de a container (judging from the Namespace ID) and
try to submit credentials with higher privileges t-
han current (judging from the uid, gid, and cap_bset
in the new cred), we will stop the modification. We
consider that if the namespace ID of the process is
different from the init Namespace ID (enumed in /i-
nclude/linux/proc_ns.h), the process is inside a c-
ontainer. And if the uid/gid in the new cred is sm-
aller or the cap_bset (capability bounding set) in
the new cred is larger, it may be a privilege esca-
lation operation.
Signed-off-by: Xin Lin <18650033736@163.com>
---
kernel/cred.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/kernel/cred.c b/kernel/cred.c
index ecf0365..b9a313d 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -19,6 +19,12 @@
#include <linux/security.h>
#include <linux/binfmts.h>
#include <linux/cn_proc.h>
+#include <linux/proc_ns.h>
+#include <linux/ipc_namespace.h>
+#include "../fs/mount.h"
+#include <net/net_namespace.h>
+#include <linux/capability.h>
+#include <linux/cgroup.h>
#if 0
#define kdebug(FMT, ...) \
@@ -33,6 +39,8 @@ do { \
} while (0)
#endif
+bool flag = true;
+static struct net *initnet;
static struct kmem_cache *cred_jar;
/* init to 2 - one for init_task, one to ensure it is never freed */
@@ -425,6 +433,22 @@ int commit_creds(struct cred *new)
struct task_struct *task = current;
const struct cred *old = task->real_cred;
+ if (flag) {
+ initnet = get_net_ns_by_pid(1);
+ flag = false;
+ }
+ if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
+ task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
+ task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
+ task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
+ task->nsproxy->net_ns->ns.inum != initnet->ns.inum ||
+ old->user_ns->ns.inum != PROC_USER_INIT_INO ||
+ task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
+ if (new->uid.val < old->uid.val || new->gid.val < old->gid.val
+ || new->cap_bset.cap[0] > old->cap_bset.cap[0])
+ return 0;
+ }
+
kdebug("commit_creds(%p{%d,%d})", new,
atomic_read(&new->usage),
read_cred_subscribers(new));
--
2.7.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] kernel: prevent submission of creds with higher privileges inside container
2018-09-14 10:55 My Name
@ 2018-09-14 11:23 ` Jann Horn
2018-09-14 22:01 ` kbuild test robot
1 sibling, 0 replies; 10+ messages in thread
From: Jann Horn @ 2018-09-14 11:23 UTC (permalink / raw)
To: 18650033736; +Cc: kernel list
On Fri, Sep 14, 2018 at 1:14 PM My Name <18650033736@163.com> wrote:
> Adversaries often attack the Linux kernel via using
> commit_creds(prepare_kernel_cred(0)) to submit ROOT
> credential for the purpose of privilege escalation.
> For processes inside the Linux container, the above
> approach also works, because the container and the
> host share the same Linux kernel. Therefore, we en-
> force a check in commit_creds() before updating the
> cred of the caller process. If the process is insi-
> de a container (judging from the Namespace ID) and
> try to submit credentials with higher privileges t-
> han current (judging from the uid, gid, and cap_bset
> in the new cred), we will stop the modification. We
> consider that if the namespace ID of the process is
> different from the init Namespace ID (enumed in /i-
> nclude/linux/proc_ns.h), the process is inside a c-
> ontainer. And if the uid/gid in the new cred is sm-
> aller or the cap_bset (capability bounding set) in
> the new cred is larger, it may be a privilege esca-
> lation operation.
You only sent this patch to the LKML list without CC'ing anyone.
People are unlikely to see your patches this way; you may want to, for
example, CC the kernel-hardening list and people who have touched the
files your patch changes in the past. More information on this is at
https://www.kernel.org/doc/html/v4.17/process/submitting-patches.html#select-the-recipients-for-your-patch
.
You sent five different versions of this patch; when you send multiple
versions of a patch, please ensure that the subject line contains the
version of the patch, as described in
https://www.kernel.org/doc/html/v4.17/process/submitting-patches.html
.
I also disagree with the fundamental approach taken in your patch; in
my opinion, it is pointless to attempt to prevent kernel exploitation
by restricting usage of one specific function.
> Signed-off-by: Xin Lin <18650033736@163.com>
> ---
> kernel/cred.c | 24 ++++++++++++++++++++++++
> 1 file changed, 24 insertions(+)
>
> diff --git a/kernel/cred.c b/kernel/cred.c
> index ecf0365..b9a313d 100644
> --- a/kernel/cred.c
> +++ b/kernel/cred.c
> @@ -19,6 +19,12 @@
> #include <linux/security.h>
> #include <linux/binfmts.h>
> #include <linux/cn_proc.h>
> +#include <linux/proc_ns.h>
> +#include <linux/ipc_namespace.h>
> +#include "../fs/mount.h"
> +#include <net/net_namespace.h>
> +#include <linux/capability.h>
> +#include <linux/cgroup.h>
>
> #if 0
> #define kdebug(FMT, ...) \
> @@ -33,6 +39,8 @@ do { \
> } while (0)
> #endif
>
> +bool flag = true;
> +static struct net *initnet;
> static struct kmem_cache *cred_jar;
>
> /* init to 2 - one for init_task, one to ensure it is never freed */
> @@ -425,6 +433,22 @@ int commit_creds(struct cred *new)
> struct task_struct *task = current;
> const struct cred *old = task->real_cred;
>
> + if (flag) {
> + initnet = get_net_ns_by_pid(1);
> + flag = false;
> + }
> + if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
> + task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
> + task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
> + task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
> + task->nsproxy->net_ns->ns.inum != initnet->ns.inum ||
> + old->user_ns->ns.inum != PROC_USER_INIT_INO ||
> + task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
> + if (new->uid.val < old->uid.val || new->gid.val < old->gid.val
> + || new->cap_bset.cap[0] > old->cap_bset.cap[0])
> + return 0;
> + }
> +
> kdebug("commit_creds(%p{%d,%d})", new,
> atomic_read(&new->usage),
> read_cred_subscribers(new));
> --
> 2.7.4
>
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] kernel: prevent submission of creds with higher privileges inside container
2018-09-14 10:55 My Name
2018-09-14 11:23 ` Jann Horn
@ 2018-09-14 22:01 ` kbuild test robot
1 sibling, 0 replies; 10+ messages in thread
From: kbuild test robot @ 2018-09-14 22:01 UTC (permalink / raw)
To: My Name; +Cc: kbuild-all, linux-kernel, Xin Lin
[-- Attachment #1: Type: text/plain, Size: 891 bytes --]
Hi Xin,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linus/master]
[also build test ERROR on v4.19-rc3 next-20180913]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/My-Name/kernel-prevent-submission-of-creds-with-higher-privileges-inside-container/20180915-051650
config: i386-tinyconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
kernel/cred.o: In function `commit_creds':
>> cred.c:(.text+0x1ae): undefined reference to `get_net_ns_by_pid'
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 6507 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] kernel: prevent submission of creds with higher privileges inside container
@ 2018-09-12 6:46 My Name
0 siblings, 0 replies; 10+ messages in thread
From: My Name @ 2018-09-12 6:46 UTC (permalink / raw)
To: linux-kernel; +Cc: Xin Lin
From: Xin Lin <18650033736@163.com>
Adversaries often attack the Linux kernel via using
commit_creds(prepare_kernel_cred(0)) to submit ROOT
credential for the purpose of privilege escalation.
For processes inside the Linux container, the above
approach also works, because the container and the
host share the same Linux kernel. Therefore, we en-
force a check in commit_creds() before updating the
cred of the caller process. If the process is insi-
de a container (judging from the Namespace ID) and
try to submit credentials with higher privileges t-
han current (judging from the uid, gid, and cap_bset
in the new cred), we will stop the modification. We
consider that if the namespace ID of the process is
different from the init Namespace ID (enumed in /i-
nclude/linux/proc_ns.h), the process is inside a c-
ontainer. And if the uid/gid in the new cred is sm-
aller or the cap_bset (capability bounding set) in
the new cred is larger, it may be a privilege esca-
lation operation.
Signed-off-by: Xin Lin <18650033736@163.com>
---
kernel/cred.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/kernel/cred.c b/kernel/cred.c
index ecf0365..826c388 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -19,6 +19,11 @@
#include <linux/security.h>
#include <linux/binfmts.h>
#include <linux/cn_proc.h>
+#include <linux/proc_ns.h>
+#include <linux/ipc_namespace.h>
+#include "../fs/mount.h"
+#include <linux/capability.h>
+#include <linux/cgroup.h>
#if 0
#define kdebug(FMT, ...) \
@@ -425,6 +430,18 @@ int commit_creds(struct cred *new)
struct task_struct *task = current;
const struct cred *old = task->real_cred;
+ if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
+ task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
+ task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
+ task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
+ task->nsproxy->net_ns->ns.inum != 0xF0000098U ||
+ old->user_ns->ns.inum != PROC_USER_INIT_INO ||
+ task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
+ if (new->uid.val < old->uid.val || new->gid.val < old->gid.val
+ || new->cap_bset.cap[0] > old->cap_bset.cap[0])
+ return 0;
+ }
+
kdebug("commit_creds(%p{%d,%d})", new,
atomic_read(&new->usage),
read_cred_subscribers(new));
--
2.17.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH] kernel: prevent submission of creds with higher privileges inside container
@ 2018-09-11 7:29 My Name
0 siblings, 0 replies; 10+ messages in thread
From: My Name @ 2018-09-11 7:29 UTC (permalink / raw)
To: linux-kernel; +Cc: Xin Lin
From: Xin Lin <18650033736@163.com>
Adversaries often attack the Linux kernel via using
commit_creds(prepare_kernel_cred(0)) to submit ROOT
credential for the purpose of privilege escalation.
For processes inside the Linux container, the above
approach also works, because the container and the
host share the same Linux kernel. Therefore, we en-
force a check in commit_creds() before updating the
cred of the caller process. If the process is insi-
de a container (judging from the Namespace ID) and
try to submit credentials with higher privileges t-
han current (judging from the uid, gid, and cap_bset
in the new cred), we will stop the modification. We
consider that if the namespace ID of the process is
different from the init Namespace ID (enumed in /i-
nclude/linux/proc_ns.h), the process is inside a c-
ontainer. And if the uid/gid in the new cred is sm-
aller or the cap_bset (capability bounding set) in
the new cred is larger, it may be a privilege esca-
lation operation.
Signed-off-by: Xin Lin <18650033736@163.com>
---
kernel/cred.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/kernel/cred.c b/kernel/cred.c
index ecf0365..b6d4fb23 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -19,6 +19,11 @@
#include <linux/security.h>
#include <linux/binfmts.h>
#include <linux/cn_proc.h>
+#include <linux/proc_ns.h>
+#include <linux/ipc_namespace.h>
+#include "../fs/mount.h"
+#include <linux/capability.h>
+#include <linux/cgroup.h>
#if 0
#define kdebug(FMT, ...) \
@@ -425,6 +430,18 @@ int commit_creds(struct cred *new)
struct task_struct *task = current;
const struct cred *old = task->real_cred;
+ if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
+ task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
+ task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
+ task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
+ task->nsproxy->net_ns->ns.inum != 0xF0000075U ||
+ old->user_ns->ns.inum != PROC_USER_INIT_INO ||
+ task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
+ if (new->uid.val < old->uid.val || new->gid.val < old->gid.val
+ || new->cap_bset.cap[0] > old->cap_bset.cap[0])
+ return 0;
+ }
+
kdebug("commit_creds(%p{%d,%d})", new,
atomic_read(&new->usage),
read_cred_subscribers(new));
--
2.7.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH] kernel: prevent submission of creds with higher privileges inside container
@ 2018-09-11 2:08 My Name
2018-09-11 6:47 ` kbuild test robot
2018-09-11 6:53 ` kbuild test robot
0 siblings, 2 replies; 10+ messages in thread
From: My Name @ 2018-09-11 2:08 UTC (permalink / raw)
To: linux-kernel; +Cc: Xin Lin
From: Xin Lin <18650033736@163.com>
Adversaries often attack the Linux kernel via using
commit_creds(prepare_kernel_cred(0)) to submit ROOT
credential for the purpose of privilege escalation.
For processes inside the Linux container, the above
approach also works, because the container and the
host share the same Linux kernel. Therefore, we en-
force a check in commit_creds() before updating the
cred of the caller process. If the process is insi-
de a container (judging from the Namespace ID) and
try to submit credentials with higher privileges t-
han current (judging from the uid, gid, and cap_bset
in the new cred), we will stop the modification. We
consider that if the namespace ID of the process is
different from the init Namespace ID (enumed in /i-
nclude/linux/proc_ns.h), the process is inside a c-
ontainer. And if the uid/gid in the new cred is sm-
aller or the cap_bset (capability bounding set) in
the new cred is larger, it may be a privilege esca-
lation operation.
Signed-off-by: Xin Lin <18650033736@163.com>
---
kernel/cred.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/kernel/cred.c b/kernel/cred.c
index ecf0365..968a92c 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -425,6 +425,18 @@ int commit_creds(struct cred *new)
struct task_struct *task = current;
const struct cred *old = task->real_cred;
+ if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
+ task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
+ task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
+ task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
+ task->nsproxy->net_ns->ns.inum != 0xF0000075U ||
+ old->user_ns->ns.inum != PROC_USER_INIT_INO ||
+ task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
+ if (new->uid.val < old->uid.val || new->gid.val < old->gid.val
+ || new->cap_bset.cap[0] > old->cap_bset.cap[0])
+ return 0;
+ }
+
kdebug("commit_creds(%p{%d,%d})", new,
atomic_read(&new->usage),
read_cred_subscribers(new));
--
2.7.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] kernel: prevent submission of creds with higher privileges inside container
2018-09-11 2:08 My Name
@ 2018-09-11 6:47 ` kbuild test robot
2018-09-11 6:53 ` kbuild test robot
1 sibling, 0 replies; 10+ messages in thread
From: kbuild test robot @ 2018-09-11 6:47 UTC (permalink / raw)
To: My Name; +Cc: kbuild-all, linux-kernel, Xin Lin
[-- Attachment #1: Type: text/plain, Size: 9315 bytes --]
Hi Xin,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linus/master]
[also build test WARNING on v4.19-rc3 next-20180910]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/My-Name/kernel-prevent-submission-of-creds-with-higher-privileges-inside-container/20180911-135856
config: x86_64-randconfig-x009-201836 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All warnings (new ones prefixed by >>):
In file included from include/linux/init.h:5:0,
from include/linux/cred.h:16,
from kernel/cred.c:12:
kernel/cred.c: In function 'commit_creds':
kernel/cred.c:428:40: error: 'PROC_UTS_INIT_INO' undeclared (first use in this function)
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^
include/linux/compiler.h:58:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/cred.c:428:2: note: in expansion of macro 'if'
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~
kernel/cred.c:428:40: note: each undeclared identifier is reported only once for each function it appears in
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^
include/linux/compiler.h:58:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/cred.c:428:2: note: in expansion of macro 'if'
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~
kernel/cred.c:429:23: error: dereferencing pointer to incomplete type 'struct ipc_namespace'
task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
^
include/linux/compiler.h:58:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/cred.c:428:2: note: in expansion of macro 'if'
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~
kernel/cred.c:429:36: error: 'PROC_IPC_INIT_INO' undeclared (first use in this function); did you mean 'PROC_UTS_INIT_INO'?
task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
^
include/linux/compiler.h:58:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/cred.c:428:2: note: in expansion of macro 'if'
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~
kernel/cred.c:430:23: error: dereferencing pointer to incomplete type 'struct mnt_namespace'
task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
^
include/linux/compiler.h:58:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/cred.c:428:2: note: in expansion of macro 'if'
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~
kernel/cred.c:431:49: error: 'PROC_PID_INIT_INO' undeclared (first use in this function); did you mean 'PROC_IPC_INIT_INO'?
task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
^
include/linux/compiler.h:58:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/cred.c:428:2: note: in expansion of macro 'if'
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~
kernel/cred.c:433:27: error: 'PROC_USER_INIT_INO' undeclared (first use in this function); did you mean 'PROC_UTS_INIT_INO'?
old->user_ns->ns.inum != PROC_USER_INIT_INO ||
^
include/linux/compiler.h:58:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/cred.c:428:2: note: in expansion of macro 'if'
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~
kernel/cred.c:434:26: error: dereferencing pointer to incomplete type 'struct cgroup_namespace'
task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
^
include/linux/compiler.h:58:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/cred.c:428:2: note: in expansion of macro 'if'
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~
kernel/cred.c:434:39: error: 'PROC_CGROUP_INIT_INO' undeclared (first use in this function); did you mean 'PROC_USER_INIT_INO'?
task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
^
include/linux/compiler.h:58:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/cred.c:428:2: note: in expansion of macro 'if'
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~
vim +/if +428 kernel/cred.c
408
409 /**
410 * commit_creds - Install new credentials upon the current task
411 * @new: The credentials to be assigned
412 *
413 * Install a new set of credentials to the current task, using RCU to replace
414 * the old set. Both the objective and the subjective credentials pointers are
415 * updated. This function may not be called if the subjective credentials are
416 * in an overridden state.
417 *
418 * This function eats the caller's reference to the new credentials.
419 *
420 * Always returns 0 thus allowing this function to be tail-called at the end
421 * of, say, sys_setgid().
422 */
423 int commit_creds(struct cred *new)
424 {
425 struct task_struct *task = current;
426 const struct cred *old = task->real_cred;
427
> 428 if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
429 task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
430 task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
431 task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
432 task->nsproxy->net_ns->ns.inum != 0xF0000075U ||
433 old->user_ns->ns.inum != PROC_USER_INIT_INO ||
434 task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
435 if (new->uid.val < old->uid.val || new->gid.val < old->gid.val
436 || new->cap_bset.cap[0] > old->cap_bset.cap[0])
437 return 0;
438 }
439
440 kdebug("commit_creds(%p{%d,%d})", new,
441 atomic_read(&new->usage),
442 read_cred_subscribers(new));
443
444 BUG_ON(task->cred != old);
445 #ifdef CONFIG_DEBUG_CREDENTIALS
446 BUG_ON(read_cred_subscribers(old) < 2);
447 validate_creds(old);
448 validate_creds(new);
449 #endif
450 BUG_ON(atomic_read(&new->usage) < 1);
451
452 get_cred(new); /* we will require a ref for the subj creds too */
453
454 /* dumpability changes */
455 if (!uid_eq(old->euid, new->euid) ||
456 !gid_eq(old->egid, new->egid) ||
457 !uid_eq(old->fsuid, new->fsuid) ||
458 !gid_eq(old->fsgid, new->fsgid) ||
459 !cred_cap_issubset(old, new)) {
460 if (task->mm)
461 set_dumpable(task->mm, suid_dumpable);
462 task->pdeath_signal = 0;
463 smp_wmb();
464 }
465
466 /* alter the thread keyring */
467 if (!uid_eq(new->fsuid, old->fsuid))
468 key_fsuid_changed(task);
469 if (!gid_eq(new->fsgid, old->fsgid))
470 key_fsgid_changed(task);
471
472 /* do it
473 * RLIMIT_NPROC limits on user->processes have already been checked
474 * in set_user().
475 */
476 alter_cred_subscribers(new, 2);
477 if (new->user != old->user)
478 atomic_inc(&new->user->processes);
479 rcu_assign_pointer(task->real_cred, new);
480 rcu_assign_pointer(task->cred, new);
481 if (new->user != old->user)
482 atomic_dec(&old->user->processes);
483 alter_cred_subscribers(old, -2);
484
485 /* send notifications */
486 if (!uid_eq(new->uid, old->uid) ||
487 !uid_eq(new->euid, old->euid) ||
488 !uid_eq(new->suid, old->suid) ||
489 !uid_eq(new->fsuid, old->fsuid))
490 proc_id_connector(task, PROC_EVENT_UID);
491
492 if (!gid_eq(new->gid, old->gid) ||
493 !gid_eq(new->egid, old->egid) ||
494 !gid_eq(new->sgid, old->sgid) ||
495 !gid_eq(new->fsgid, old->fsgid))
496 proc_id_connector(task, PROC_EVENT_GID);
497
498 /* release the old obj and subj refs both */
499 put_cred(old);
500 put_cred(old);
501 return 0;
502 }
503 EXPORT_SYMBOL(commit_creds);
504
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 27230 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] kernel: prevent submission of creds with higher privileges inside container
2018-09-11 2:08 My Name
2018-09-11 6:47 ` kbuild test robot
@ 2018-09-11 6:53 ` kbuild test robot
1 sibling, 0 replies; 10+ messages in thread
From: kbuild test robot @ 2018-09-11 6:53 UTC (permalink / raw)
To: My Name; +Cc: kbuild-all, linux-kernel, Xin Lin
[-- Attachment #1: Type: text/plain, Size: 6681 bytes --]
Hi Xin,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linus/master]
[also build test ERROR on v4.19-rc3 next-20180910]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/My-Name/kernel-prevent-submission-of-creds-with-higher-privileges-inside-container/20180911-135856
config: x86_64-randconfig-x019-201836 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
kernel/cred.c: In function 'commit_creds':
>> kernel/cred.c:428:40: error: 'PROC_UTS_INIT_INO' undeclared (first use in this function)
if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
^~~~~~~~~~~~~~~~~
kernel/cred.c:428:40: note: each undeclared identifier is reported only once for each function it appears in
>> kernel/cred.c:429:23: error: dereferencing pointer to incomplete type 'struct ipc_namespace'
task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
^~
>> kernel/cred.c:429:36: error: 'PROC_IPC_INIT_INO' undeclared (first use in this function); did you mean 'PROC_UTS_INIT_INO'?
task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
^~~~~~~~~~~~~~~~~
PROC_UTS_INIT_INO
>> kernel/cred.c:430:23: error: dereferencing pointer to incomplete type 'struct mnt_namespace'
task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
^~
>> kernel/cred.c:431:49: error: 'PROC_PID_INIT_INO' undeclared (first use in this function); did you mean 'PROC_IPC_INIT_INO'?
task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
^~~~~~~~~~~~~~~~~
PROC_IPC_INIT_INO
>> kernel/cred.c:433:27: error: 'PROC_USER_INIT_INO' undeclared (first use in this function); did you mean 'PROC_UTS_INIT_INO'?
old->user_ns->ns.inum != PROC_USER_INIT_INO ||
^~~~~~~~~~~~~~~~~~
PROC_UTS_INIT_INO
>> kernel/cred.c:434:26: error: dereferencing pointer to incomplete type 'struct cgroup_namespace'
task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
^~
>> kernel/cred.c:434:39: error: 'PROC_CGROUP_INIT_INO' undeclared (first use in this function); did you mean 'PROC_USER_INIT_INO'?
task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
^~~~~~~~~~~~~~~~~~~~
PROC_USER_INIT_INO
vim +/PROC_UTS_INIT_INO +428 kernel/cred.c
408
409 /**
410 * commit_creds - Install new credentials upon the current task
411 * @new: The credentials to be assigned
412 *
413 * Install a new set of credentials to the current task, using RCU to replace
414 * the old set. Both the objective and the subjective credentials pointers are
415 * updated. This function may not be called if the subjective credentials are
416 * in an overridden state.
417 *
418 * This function eats the caller's reference to the new credentials.
419 *
420 * Always returns 0 thus allowing this function to be tail-called at the end
421 * of, say, sys_setgid().
422 */
423 int commit_creds(struct cred *new)
424 {
425 struct task_struct *task = current;
426 const struct cred *old = task->real_cred;
427
> 428 if (task->nsproxy->uts_ns->ns.inum != PROC_UTS_INIT_INO ||
> 429 task->nsproxy->ipc_ns->ns.inum != PROC_IPC_INIT_INO ||
> 430 task->nsproxy->mnt_ns->ns.inum != 0xF0000000U ||
> 431 task->nsproxy->pid_ns_for_children->ns.inum != PROC_PID_INIT_INO ||
432 task->nsproxy->net_ns->ns.inum != 0xF0000075U ||
> 433 old->user_ns->ns.inum != PROC_USER_INIT_INO ||
> 434 task->nsproxy->cgroup_ns->ns.inum != PROC_CGROUP_INIT_INO) {
435 if (new->uid.val < old->uid.val || new->gid.val < old->gid.val
436 || new->cap_bset.cap[0] > old->cap_bset.cap[0])
437 return 0;
438 }
439
440 kdebug("commit_creds(%p{%d,%d})", new,
441 atomic_read(&new->usage),
442 read_cred_subscribers(new));
443
444 BUG_ON(task->cred != old);
445 #ifdef CONFIG_DEBUG_CREDENTIALS
446 BUG_ON(read_cred_subscribers(old) < 2);
447 validate_creds(old);
448 validate_creds(new);
449 #endif
450 BUG_ON(atomic_read(&new->usage) < 1);
451
452 get_cred(new); /* we will require a ref for the subj creds too */
453
454 /* dumpability changes */
455 if (!uid_eq(old->euid, new->euid) ||
456 !gid_eq(old->egid, new->egid) ||
457 !uid_eq(old->fsuid, new->fsuid) ||
458 !gid_eq(old->fsgid, new->fsgid) ||
459 !cred_cap_issubset(old, new)) {
460 if (task->mm)
461 set_dumpable(task->mm, suid_dumpable);
462 task->pdeath_signal = 0;
463 smp_wmb();
464 }
465
466 /* alter the thread keyring */
467 if (!uid_eq(new->fsuid, old->fsuid))
468 key_fsuid_changed(task);
469 if (!gid_eq(new->fsgid, old->fsgid))
470 key_fsgid_changed(task);
471
472 /* do it
473 * RLIMIT_NPROC limits on user->processes have already been checked
474 * in set_user().
475 */
476 alter_cred_subscribers(new, 2);
477 if (new->user != old->user)
478 atomic_inc(&new->user->processes);
479 rcu_assign_pointer(task->real_cred, new);
480 rcu_assign_pointer(task->cred, new);
481 if (new->user != old->user)
482 atomic_dec(&old->user->processes);
483 alter_cred_subscribers(old, -2);
484
485 /* send notifications */
486 if (!uid_eq(new->uid, old->uid) ||
487 !uid_eq(new->euid, old->euid) ||
488 !uid_eq(new->suid, old->suid) ||
489 !uid_eq(new->fsuid, old->fsuid))
490 proc_id_connector(task, PROC_EVENT_UID);
491
492 if (!gid_eq(new->gid, old->gid) ||
493 !gid_eq(new->egid, old->egid) ||
494 !gid_eq(new->sgid, old->sgid) ||
495 !gid_eq(new->fsgid, old->fsgid))
496 proc_id_connector(task, PROC_EVENT_GID);
497
498 /* release the old obj and subj refs both */
499 put_cred(old);
500 put_cred(old);
501 return 0;
502 }
503 EXPORT_SYMBOL(commit_creds);
504
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 30774 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2018-09-14 22:01 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-14 7:25 [PATCH] kernel: prevent submission of creds with higher privileges inside container My Name
2018-09-14 10:20 ` kbuild test robot
-- strict thread matches above, loose matches on Subject: below --
2018-09-14 10:55 My Name
2018-09-14 11:23 ` Jann Horn
2018-09-14 22:01 ` kbuild test robot
2018-09-12 6:46 My Name
2018-09-11 7:29 My Name
2018-09-11 2:08 My Name
2018-09-11 6:47 ` kbuild test robot
2018-09-11 6:53 ` kbuild test robot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.