From patchwork Sun Oct 25 13:45:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Wood X-Patchwork-Id: 11855271 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8E3D292C for ; Sun, 25 Oct 2020 14:27:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 69D2B21707 for ; Sun, 25 Oct 2020 14:27:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=gmx.net header.i=@gmx.net header.b="dyEwEY+v" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1416622AbgJYO1D (ORCPT ); Sun, 25 Oct 2020 10:27:03 -0400 Received: from mout.gmx.net ([212.227.17.21]:57621 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1416619AbgJYO1B (ORCPT ); Sun, 25 Oct 2020 10:27:01 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1603635995; bh=dC1GAYDBvewqH+15RkWtGKNy0TPLCQGsQjJzFQDQAnw=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=dyEwEY+vBxu55T65yXEq7Ce8bmYNMvoz5AMtdy2OtJ9UG1KeHIQuvKPhiOogXje+F I6UwzEWWwf4+k+E7uOmOdTtkg6FQsA3szGKBaOuvtuVfYqocprDRsI9xW01z5PH52M wv0V/kAR8Oh5AJtVe+QK4Gj01oqmrvli2sQn5sLs= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from localhost.localdomain ([83.52.231.59]) by mail.gmx.com (mrgmx105 [212.227.17.174]) with ESMTPSA (Nemesis) id 1Ma24y-1kriRS3bKW-00Vuve; Sun, 25 Oct 2020 15:26:35 +0100 From: John Wood To: Kees Cook , Jann Horn Cc: John Wood , Jonathan Corbet , James Morris , "Serge E. Hallyn" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Subject: [PATCH v2 1/8] security: Add LSM hook at the point where a task gets a fatal signal Date: Sun, 25 Oct 2020 14:45:33 +0100 Message-Id: <20201025134540.3770-2-john.wood@gmx.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201025134540.3770-1-john.wood@gmx.com> References: <20201025134540.3770-1-john.wood@gmx.com> MIME-Version: 1.0 X-Provags-ID: V03:K1:mkupQ1akacLnRJqP1KdBhhAbud+zuJqEkwRwuwT3qFhfPIzQho8 LJgT+hubrBqL9itzdwXipymtIx1zhmqM88Ik/XTX/YgeHMgPLgZ1Ppss7Vmx3E0i8ymxPXR zU62AwkMKBMLVoYz8w9Zm0Ev+k/K4S3a4CnuuPFdCuNCipgeS6KUwvKegJGfuoPwAOhuCNY Mr6p26zrS6KljN0UxitBA== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1;V03:K0:5bjOM493H6M=:o4xZVjH+p0ZO7TJHbqj/N1 jTDqJbR7M/l4QXpt9Y4b7IpYlwAv9QwPDLnePDKBkXdGLyDIFLyJl6mvPv36EMWiwi8jEfOp8 vzZ/wGJ4gaxeoZiFNFuq6UH5ex6+wTatp5HF7N6TJ8+Ji13nYBO0lj4rTpOyFeS3uDtRYULDl dVi+kDo2VlUTajWYn3ok6MToz7/qcC5cjSwSerYZP1e9R/eMm7NiuEt0K55YtgpP5gfB/C6Pk e1u7Qvt0FFykuKlkL4k883ehav6T7TLQ39d4nU6xBi7JY2jfjSq4Xixy8e82d5hDGP+XiOuNP mntGQgEtCItOsU/PFpcBOiFWcoFSGY8E5hMr8rDXPTIkMLN0B/jN8xrLp1rjCsYzWKtTZ8ehE fz/oZX1sW5J4RYLIbRzOFCL8XXfqUHSR1mP/k/BN88g/PBUEm/Rg9Axq17EePwOwBx95kVtHR QryxYAdv6q71gu8Ad3pD6+fTbt2fTkWAJF11EPuU43vbxXdKvM13LmYkpmB+DrASMIw6XQbpS H3h/967FdPMCtjgfE+PUij73O6VEfZN4lHu17BxNgV1L065Q8+PbchiJ/P7ccsv25GEJtqu/E 0bwKfz/IoitNb3p/Lr3y055y4ydsKPFvvc9io2NMNRrCZUY70NxWyzSJAy6c2B5neS3kgqHI8 gQn+LpQ2XyYrWfZcv+Xg8SDkBVhsvq3uh0u8m3oSBY/tOzZcpy1FFzhsz6CLYn53eSdSZpnvv ct4UgextoikBaHPETK7KE0U//2PL5mBxkKCZgwfMxilC7bstf8E926/wGzl5Pvd2B7Vw6KlBK lX/PsEiPgmH1eFNyYKCwjWVfFjJWp3J2ZwLehRGS6ewz49c2ICyjPPkJ7xmKi8DD5RbYnOrTI gYndmheGlz9Fz6ZGQhww== Precedence: bulk List-ID: Add a security hook that allows a LSM to be notified when a task gets a fatal signal. This patch is a previous step on the way to compute the task crash period by the "brute" LSM (linux security module to detect and mitigate fork brute force attack against vulnerable userspace processes). Signed-off-by: John Wood --- include/linux/lsm_hook_defs.h | 1 + include/linux/lsm_hooks.h | 4 ++++ include/linux/security.h | 4 ++++ kernel/signal.c | 1 + security/security.c | 5 +++++ 5 files changed, 15 insertions(+) -- 2.25.1 diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 2a8c74d99015..8ecbb6849555 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -213,6 +213,7 @@ LSM_HOOK(int, -ENOSYS, task_prctl, int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) LSM_HOOK(void, LSM_RET_VOID, task_to_inode, struct task_struct *p, struct inode *inode) +LSM_HOOK(void, LSM_RET_VOID, task_fatal_signal, const kernel_siginfo_t *siginfo) LSM_HOOK(int, 0, ipc_permission, struct kern_ipc_perm *ipcp, short flag) LSM_HOOK(void, LSM_RET_VOID, ipc_getsecid, struct kern_ipc_perm *ipcp, u32 *secid) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 9e2e3e63719d..0a8b0fab0212 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -761,6 +761,10 @@ * security attributes, e.g. for /proc/pid inodes. * @p contains the task_struct for the task. * @inode contains the inode structure for the inode. + * @task_fatal_signal: + * This hook allows security modules to be notified when a task gets a + * fatal signal. + * @siginfo contains the signal information. * * Security hooks for Netlink messaging. * diff --git a/include/linux/security.h b/include/linux/security.h index 0a0a03b36a3b..4bc000bb8685 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -413,6 +413,7 @@ int security_task_kill(struct task_struct *p, struct kernel_siginfo *info, int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); void security_task_to_inode(struct task_struct *p, struct inode *inode); +void security_task_fatal_signal(const kernel_siginfo_t *siginfo); int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag); void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid); int security_msg_msg_alloc(struct msg_msg *msg); @@ -1127,6 +1128,9 @@ static inline int security_task_prctl(int option, unsigned long arg2, static inline void security_task_to_inode(struct task_struct *p, struct inode *inode) { } +static inline void security_task_fatal_signal(const kernel_siginfo_t *siginfo) +{ } + static inline int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag) { diff --git a/kernel/signal.c b/kernel/signal.c index a38b3edc6851..a0866d6b2c06 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2734,6 +2734,7 @@ bool get_signal(struct ksignal *ksig) /* * Anything else is fatal, maybe with a core dump. */ + security_task_fatal_signal(&ksig->info); current->flags |= PF_SIGNALED; if (sig_kernel_coredump(signr)) { diff --git a/security/security.c b/security/security.c index 70a7ad357bc6..e8c7978b515c 100644 --- a/security/security.c +++ b/security/security.c @@ -1810,6 +1810,11 @@ void security_task_to_inode(struct task_struct *p, struct inode *inode) call_void_hook(task_to_inode, p, inode); } +void security_task_fatal_signal(const kernel_siginfo_t *siginfo) +{ + call_void_hook(task_fatal_signal, siginfo); +} + int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag) { return call_int_hook(ipc_permission, 0, ipcp, flag); From patchwork Sun Oct 25 13:45:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Wood X-Patchwork-Id: 11855289 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 12463697 for ; Sun, 25 Oct 2020 15:08:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E0617208A9 for ; Sun, 25 Oct 2020 15:08:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=gmx.net header.i=@gmx.net header.b="YTQEAY/u" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1416229AbgJYPHH (ORCPT ); Sun, 25 Oct 2020 11:07:07 -0400 Received: from mout.gmx.net ([212.227.15.19]:45779 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1416224AbgJYPHH (ORCPT ); Sun, 25 Oct 2020 11:07:07 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1603638398; bh=wuoS53Deov2WuIzLJNZWMN+kyeRs9Ey6AoYGWmib7HU=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=YTQEAY/uDsAziYS/nBhb9v8t6l7IOw1o3OXzgL8Mx3wvc4uSAoIjKmsvLey2EGiMS zZYtzpWV93+xq5zIdLEBwhPHJREDPDRxHUHrdDTd2WDt/YzjjbI7idQLUg/F8Km2+H O8Ji+OZjDlcLeRurH6xjn6Fkt6SksroGAFJlpYjQ= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from localhost.localdomain ([83.52.231.59]) by mail.gmx.com (mrgmx004 [212.227.17.184]) with ESMTPSA (Nemesis) id 1Mr9Bk-1k1ESJ3R1a-00oJp5; Sun, 25 Oct 2020 16:06:38 +0100 From: John Wood To: Kees Cook , Jann Horn Cc: John Wood , Jonathan Corbet , James Morris , "Serge E. Hallyn" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Subject: [PATCH v2 2/8] security/brute: Define a LSM and manage statistical data Date: Sun, 25 Oct 2020 14:45:34 +0100 Message-Id: <20201025134540.3770-3-john.wood@gmx.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201025134540.3770-1-john.wood@gmx.com> References: <20201025134540.3770-1-john.wood@gmx.com> MIME-Version: 1.0 X-Provags-ID: V03:K1:gj4Pv1tMSqJQblHiMNF3ohGw0eX6O5310SKSKkbOzUwoX1sssju nMXHyTGoxwLY6TNnA2crs5k13efBZLs4fzVDOf9Cvg2hO3pcUPahKk07Y6ZR5xbNMoybynv aCH++vSQdsUBrVtPjyOmrgUA2cxNIZnXWB7rC1Fgi+zk9tANwKxSFvQVMFnyM/n73Iu3dTe KFTMbvG2hDOddl86BgbAQ== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1;V03:K0:h/qcHZd+Stw=:9whRYikgVS7mb6a/XD9wgp 3MPZ2ed1Pc68v2HMBPgM8Q1eAkHh6Af58JVepeOzZkGvhTMbIzas6AdXjPzmuygbprAEFAWjj 1KC9B3ELpQus/iuRLN26gBKqxFfWP68SOrFdl93p8jJ0jItgT+KTPohfZT/OuNrRpEyI+Et6Z ozmoBlN13PzTIoGqxUOJ/YV+QZrqRMFW1o/QECWO8Ea7pMBGBq7Ci6j2e0tCGiLUm2g5IP1yX aZ0l/QxQZe+zi/QEl5sK6E7pm0KZqWqIS3l7opc3U5PXX/eIS8W2xj0LOZLu8fIWoqeAyegdj LPPv6LxyWgNci5fEinNOKpAYfDb1yOwY4mkXfKkdoj6yo9PHWb8jJgbYK1aQD8d2/GwWuaFhJ qmnYkEklRGGg/V2CozHzJy+wWJxfcZuUMEHIHMW5QG7FoYdyHpZ1wqYdfiFJqrtTuiRlL31ad h/d4LMZWxnMIiHdwyTvy2Sdz1cL67Ge7+0FjEfvXySnDPLR6+unqngePIm8PQxnlSRr8f43hy BZvqduf3Fcm7g5Baf8ZFwQqxNNMNnUlHErIBc8/pqA307oPVyAcvUh38k3FAyWUam6tJPQiqk fnCDVFRFXFR5nISReHaQsZBAGZqShkr15e7ArbEYosXzYU54l9KnBMT40Bn68IgN8Fe7rvNtc 6pzzKhdxBRBkux6LnPGbHd2rAz79PxuFxdC65IyYR3VW3sLfY2pOpzeB6uTMYmSmR7p5lSnTZ 3Bwox7dd1lc0LYBfC8RbBpByF/UD5UQpWyi5sEK632n74ehVrJ7CvF5DGcrveEXUqsJxPfgzz 1DE5Dts3tSH4aSYEcnJ0e2WVv0ihouY+Z8cIK2yJCAE7RSoCuN0F1ai6X2HZyU7rN4RW6g9Jc KWxIgmuleZBmnLGOnnJQ== Precedence: bulk List-ID: Add a new Kconfig file to define a menu entry under "Security options" to enable the "Fork brute force attack detection and mitigation" feature. For a correct management of a fork brute force attack it is necessary that all the tasks hold statistical data. The same statistical data needs to be shared between all the tasks that hold the same memory contents or in other words, between all the tasks that have been forked without any execve call. So, define a statistical data structure to hold all the necessary information shared by all the fork hierarchy processes. This info is basically a list of the last crashes timestamps and a reference counter. When a forked task calls the execve system call, the memory contents are set with new values. So, in this scenario the parent's statistical data no need to be shared. Instead, a new statistical data structure must be allocated to start a new hierarchy. The statistical data that is shared between all the fork hierarchy processes needs to be freed when this hierarchy disappears. So, based in all the previous information define a LSM with three hooks to manage all the commented cases. These hooks are "task_alloc" to do the fork management, "bprm_committing_creds" for do the execve management and "task_free" to release the resources. Also, add to the task_struct's security blob the pointer to the statistical data. This way, all the tasks will have access to this information. Signed-off-by: John Wood --- security/Kconfig | 11 +- security/Makefile | 4 + security/brute/Kconfig | 12 ++ security/brute/Makefile | 2 + security/brute/brute.c | 339 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 363 insertions(+), 5 deletions(-) create mode 100644 security/brute/Kconfig create mode 100644 security/brute/Makefile create mode 100644 security/brute/brute.c -- 2.25.1 diff --git a/security/Kconfig b/security/Kconfig index 7561f6f99f1d..204bb311b1f1 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -240,6 +240,7 @@ source "security/safesetid/Kconfig" source "security/lockdown/Kconfig" source "security/integrity/Kconfig" +source "security/brute/Kconfig" choice prompt "First legacy 'major LSM' to be initialized" @@ -277,11 +278,11 @@ endchoice config LSM string "Ordered list of enabled LSMs" - default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK - default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR - default "lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO - default "lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC - default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf" + default "brute,lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK + default "brute,lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR + default "brute,lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO + default "brute,lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC + default "brute,lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf" help A comma-separated list of LSMs, in initialization order. Any LSMs left off this list will be ignored. This can be diff --git a/security/Makefile b/security/Makefile index 3baf435de541..1236864876da 100644 --- a/security/Makefile +++ b/security/Makefile @@ -36,3 +36,7 @@ obj-$(CONFIG_BPF_LSM) += bpf/ # Object integrity file lists subdir-$(CONFIG_INTEGRITY) += integrity obj-$(CONFIG_INTEGRITY) += integrity/ + +# Object brute file lists +subdir-$(CONFIG_SECURITY_FORK_BRUTE) += brute +obj-$(CONFIG_SECURITY_FORK_BRUTE) += brute/ diff --git a/security/brute/Kconfig b/security/brute/Kconfig new file mode 100644 index 000000000000..1bd2df1e2dec --- /dev/null +++ b/security/brute/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +config SECURITY_FORK_BRUTE + bool "Fork brute force attack detection and mitigation" + depends on SECURITY + help + This is an LSM that stops any fork brute force attack against + vulnerable userspace processes. The detection method is based on + the application crash period and as a mitigation procedure all the + offending tasks are killed. Like capabilities, this security module + stacks with other LSMs. + + If you are unsure how to answer this question, answer N. diff --git a/security/brute/Makefile b/security/brute/Makefile new file mode 100644 index 000000000000..d3f233a132a9 --- /dev/null +++ b/security/brute/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_SECURITY_FORK_BRUTE) += brute.o diff --git a/security/brute/brute.c b/security/brute/brute.c new file mode 100644 index 000000000000..307d07bf9d98 --- /dev/null +++ b/security/brute/brute.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct brute_stats - Fork brute force attack statistics. + * @lock: Lock to protect the brute_stats structure. + * @refc: Reference counter. + * @timestamps: Last crashes timestamps list. + * @timestamps_size: Last crashes timestamps list size. + * + * This structure holds the statistical data shared by all the fork hierarchy + * processes. + */ +struct brute_stats { + spinlock_t lock; + refcount_t refc; + struct list_head timestamps; + unsigned char timestamps_size; +}; + +/** + * struct brute_timestamp - Last crashes timestamps list entry. + * @jiffies: Crash timestamp. + * @node: Entry list head. + * + * This structure holds a crash timestamp. + */ +struct brute_timestamp { + u64 jiffies; + struct list_head node; +}; + +/** + * brute_blob_sizes - LSM blob sizes. + * + * To share statistical data among all the fork hierarchy processes, define a + * pointer to the brute_stats structure as a part of the task_struct's security + * blob. + */ +static struct lsm_blob_sizes brute_blob_sizes __lsm_ro_after_init = { + .lbs_task = sizeof(struct brute_stats *), +}; + +/** + * brute_stats_ptr() - Get the pointer to the brute_stats structure. + * @task: Task that holds the statistical data. + * + * Return: A pointer to a pointer to the brute_stats structure. + */ +static inline struct brute_stats **brute_stats_ptr(struct task_struct *task) +{ + return task->security + brute_blob_sizes.lbs_task; +} + +/** + * brute_new_timestamp() - Allocate a new timestamp structure. + * + * If the allocation is successful the timestamp is set to now. + * + * Return: NULL if the allocation fails. A pointer to the new allocated + * timestamp structure if it success. + */ +static struct brute_timestamp *brute_new_timestamp(void) +{ + struct brute_timestamp *timestamp; + + timestamp = kmalloc(sizeof(struct brute_timestamp), GFP_KERNEL); + if (timestamp) + timestamp->jiffies = get_jiffies_64(); + + return timestamp; +} + +/** + * brute_new_stats() - Allocate a new statistics structure. + * + * If the allocation is successful the reference counter is set to one to + * indicate that there will be one task that points to this structure. The last + * crashes timestamps list is initialized with one entry set to now. This way, + * its possible to compute the application crash period at the first fault. + * + * Return: NULL if the allocation fails. A pointer to the new allocated + * statistics structure if it success. + */ +static struct brute_stats *brute_new_stats(void) +{ + struct brute_stats *stats; + struct brute_timestamp *timestamp; + + stats = kmalloc(sizeof(struct brute_stats), GFP_KERNEL); + if (!stats) + return NULL; + + timestamp = brute_new_timestamp(); + if (!timestamp) { + kfree(stats); + return NULL; + } + + spin_lock_init(&stats->lock); + refcount_set(&stats->refc, 1); + INIT_LIST_HEAD(&stats->timestamps); + list_add_tail(×tamp->node, &stats->timestamps); + stats->timestamps_size = 1; + + return stats; +} + +/** + * brute_share_stats() - Share the statistical data between processes. + * @src: Source of statistics to be shared. + * @dst: Destination of statistics to be shared. + * + * Copy the src's pointer to the statistical data structure to the dst's pointer + * to the same structure. Since there is a new process that shares the same + * data, increase the reference counter. The src's pointer cannot be NULL. + * + * It's mandatory to disable interrupts before acquiring the lock since the + * task_free hook can be called from an IRQ context during the execution of the + * task_alloc hook. + */ +static void brute_share_stats(struct brute_stats **src, + struct brute_stats **dst) +{ + unsigned long flags; + + spin_lock_irqsave(&(*src)->lock, flags); + refcount_inc(&(*src)->refc); + *dst = *src; + spin_unlock_irqrestore(&(*src)->lock, flags); +} + +/** + * brute_task_alloc() - Target for the task_alloc hook. + * @task: Task being allocated. + * @clone_flags: Contains the flags indicating what should be shared. + * + * For a correct management of a fork brute force attack it is necessary that + * all the tasks hold statistical data. The same statistical data needs to be + * shared between all the tasks that hold the same memory contents or in other + * words, between all the tasks that have been forked without any execve call. + * + * To ensure this, if the current task doesn't have statistical data when forks, + * it is mandatory to allocate a new statistics structure and share it between + * this task and the new one being allocated. Otherwise, share the statistics + * that the current task already has. + * + * Return: -ENOMEM if the allocation of the new statistics structure fails. Zero + * otherwise. + */ +static int brute_task_alloc(struct task_struct *task, unsigned long clone_flags) +{ + struct brute_stats **stats, **p_stats; + + stats = brute_stats_ptr(task); + p_stats = brute_stats_ptr(current); + + if (likely(*p_stats)) { + brute_share_stats(p_stats, stats); + return 0; + } + + *stats = brute_new_stats(); + if (!*stats) + return -ENOMEM; + + brute_share_stats(stats, p_stats); + return 0; +} + +/** + * brute_reset_stats() - Reset the statistical data. + * @stats: Statistics to be reset. + * + * Ensure that the last crashes timestamps list holds only one entry and set + * this timestamp to now. This way, its possible to compute the application + * crash period at the next fault. The statistics to be reset cannot be NULL. + * + * Context: Must be called with stats->lock held. + */ +static void brute_reset_stats(struct brute_stats *stats) +{ + unsigned char entries_to_delete; + struct brute_timestamp *timestamp, *next; + + if (WARN(!stats->timestamps_size, "No last timestamps\n")) + return; + + entries_to_delete = stats->timestamps_size - 1; + stats->timestamps_size = 1; + + list_for_each_entry_safe(timestamp, next, &stats->timestamps, node) { + if (unlikely(!entries_to_delete)) { + timestamp->jiffies = get_jiffies_64(); + break; + } + + list_del(×tamp->node); + kfree(timestamp); + entries_to_delete -= 1; + } +} + +/** + * brute_task_execve() - Target for the bprm_committing_creds hook. + * @bprm: Points to the linux_binprm structure. + * + * When a forked task calls the execve system call, the memory contents are set + * with new values. So, in this scenario the parent's statistical data no need + * to be shared. Instead, a new statistical data structure must be allocated to + * start a new hierarchy. This condition is detected when the statistics + * reference counter holds a value greater than or equal to two (a fork always + * sets the statistics reference counter to a minimum of two since the parent + * and the child task are sharing the same data). + * + * However, if the execve function is called immediately after another execve + * call, althought the memory contents are reset, there is no need to allocate + * a new statistical data structure. This is possible because at this moment + * only one task (the task that calls the execve function) points to the data. + * In this case, the previous allocation is used but the statistics are reset. + * + * It's mandatory to disable interrupts before acquiring the lock since the + * task_free hook can be called from an IRQ context during the execution of the + * bprm_committing_creds hook. + */ +static void brute_task_execve(struct linux_binprm *bprm) +{ + struct brute_stats **stats; + unsigned long flags; + + stats = brute_stats_ptr(current); + if (WARN(!*stats, "No statistical data\n")) + return; + + spin_lock_irqsave(&(*stats)->lock, flags); + + if (!refcount_dec_not_one(&(*stats)->refc)) { + /* execve call after an execve call */ + brute_reset_stats(*stats); + spin_unlock_irqrestore(&(*stats)->lock, flags); + return; + } + + /* execve call after a fork call */ + spin_unlock_irqrestore(&(*stats)->lock, flags); + *stats = brute_new_stats(); + WARN(!*stats, "Cannot allocate statistical data\n"); +} + +/** + * brute_stats_free() - Deallocate a statistics structure. + * @stats: Statistics to be freed. + * + * Deallocate all the last crashes timestamps list entries and then the + * statistics structure. The statistics to be freed cannot be NULL. + * + * Context: Must be called with stats->lock held and this function releases it. + */ +static void brute_stats_free(struct brute_stats *stats) +{ + struct brute_timestamp *timestamp, *next; + + list_for_each_entry_safe(timestamp, next, &stats->timestamps, node) { + list_del(×tamp->node); + kfree(timestamp); + } + + spin_unlock(&stats->lock); + kfree(stats); +} + +/** + * brute_task_free() - Target for the task_free hook. + * @task: Task about to be freed. + * + * The statistical data that is shared between all the fork hierarchy processes + * needs to be freed when this hierarchy disappears. + */ +static void brute_task_free(struct task_struct *task) +{ + struct brute_stats **stats; + + stats = brute_stats_ptr(task); + if (WARN(!*stats, "No statistical data\n")) + return; + + spin_lock(&(*stats)->lock); + + if (refcount_dec_and_test(&(*stats)->refc)) + brute_stats_free(*stats); + else + spin_unlock(&(*stats)->lock); +} + +/** + * brute_hooks - Targets for the LSM's hooks. + */ +static struct security_hook_list brute_hooks[] __lsm_ro_after_init = { + LSM_HOOK_INIT(task_alloc, brute_task_alloc), + LSM_HOOK_INIT(bprm_committing_creds, brute_task_execve), + LSM_HOOK_INIT(task_free, brute_task_free), +}; + +/** + * brute_init() - Initialize the brute LSM. + * + * Return: Always returns zero. + */ +static int __init brute_init(void) +{ + pr_info("Brute initialized\n"); + security_add_hooks(brute_hooks, ARRAY_SIZE(brute_hooks), + KBUILD_MODNAME); + return 0; +} + +DEFINE_LSM(brute) = { + .name = KBUILD_MODNAME, + .init = brute_init, + .blobs = &brute_blob_sizes, +}; + From patchwork Sun Oct 25 13:45:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Wood X-Patchwork-Id: 11855325 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7FA8014B7 for ; Sun, 25 Oct 2020 15:47:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 56EA72080A for ; Sun, 25 Oct 2020 15:47:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=gmx.net header.i=@gmx.net header.b="cA3lU6Xd" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1417055AbgJYPrC (ORCPT ); Sun, 25 Oct 2020 11:47:02 -0400 Received: from mout.gmx.net ([212.227.15.18]:57375 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1417049AbgJYPrB (ORCPT ); Sun, 25 Oct 2020 11:47:01 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1603640800; bh=FL6P9PlcVXad6H4MtVd1cnzcCv3Sr7IXdhqycJIBKF0=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=cA3lU6XdbcZa8MvTNKxLW5CQS/ifFwerYN+SHSpe0GFT4CbiyspGTFYNW9oP3vZ8P pTvgn5dYxj3a5LmyQdMdnM1FIOwSxxRlu2+tMv9AVoLLbRPGSw4GMg0kO67aj5jer/ jqUxkhrCHM3GJtRcN6CYePCAk+PMjz5/TVf8+3JM= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from localhost.localdomain ([83.52.231.59]) by mail.gmx.com (mrgmx004 [212.227.17.184]) with ESMTPSA (Nemesis) id 1M9FnZ-1kRT1k3r1T-006PTS; Sun, 25 Oct 2020 16:46:40 +0100 From: John Wood To: Kees Cook , Jann Horn Cc: John Wood , Jonathan Corbet , James Morris , "Serge E. Hallyn" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Subject: [PATCH v2 3/8] security/brute: Add sysctl attributes to allow detection fine tuning Date: Sun, 25 Oct 2020 14:45:35 +0100 Message-Id: <20201025134540.3770-4-john.wood@gmx.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201025134540.3770-1-john.wood@gmx.com> References: <20201025134540.3770-1-john.wood@gmx.com> MIME-Version: 1.0 X-Provags-ID: V03:K1:pofVpQawf3cGIOvUmDx1sWEL7cIqeQhbsAoJcoXuQe4Kmy96jmh QQgyBhhHJ1dcWAV19f31YFCvjT+ZDiMTFk6KC/FyoDKTKb1TVpbe9YmYKf+vuWiRjw2EJKz Lt+2qG/0k3LKn/qYawV5ON0lt3kbPTrzXSxp8NZOGnjerTVfFeCoJ0xx0cPQR0dBRQPwARp 2EyO1lU2t2fow+l2KKxWQ== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1;V03:K0:fD2ezhdR8F4=:pnRSOwo+YDnupRmdVquV14 FI/Seu4PTrzkrG9v8P14awXBqqhv2GPSKRRNaUZpnKsqzCXqqZ40BDRYTsv4/CLZI1vAL81YC hTGvnbxYxL8JIfMjZJMkRzHlWP6Y2eTR4/uddYXePSglt2bwaoQcyh5S4rL8djtonosmD9iMU lLO3MvFWqOgAq9en5TjR+1oBaU3cAW0FU4rYGydB0QONwm93vE6Kz/2vn3BoAfqi1iIuHygmA muzA50Hxu3N1OOkQZtdwB423BwGaFJxdeOetjT4kBOLj9+I/5AmqB05Dte/uuA7EYkv3k46oz H6KhVBFRjAevye6SjlVXmggbvSzFqDMh2NpD/IXe+Ciw+lhq8zrHTYWqEA27cxVmm+sKdz9SF BMhh36UCDi2vndTYDAuEa/ANLeDKoS2J++5AZPELSpO3mFJHy1oHU93y/eU/SdstUkaVFzRbA h6UG+WGYWYNHWuxZqAo8j1ASnKgLdjOR3KQmVho6Rr0bDpbs7G3U4Xa9saFNJ5+me09gmSnu8 JS07TYteXtJtsWTjaV2DmOOS5CWjSsDN/PLwNoU2mAPmNcvA7XUC3tBaxEh/1+c0wUSje/t2I 1dV33FSSVyit/21xCbGi4xdBV/93Z6dZi80kgU5Shc2zAAhKmbIcOF0yGZw4Q6EaPLdNmPlAH JobMvvbp8Tnx2HAFxrE3rPQP+hqMBK/vG1VDMlIk3FOhblDYFy5tzSwYZ9cLXSQFm5KMznbUo SGzjjEtkFqEMPW/J7tQXXMrBVsVdk+hsZheFrSfARqt5As8ggyLER6jnO7mJoW8umSTz5m+ly BFST+YJSAykDMGpN2Z2kURl2d+fIKX0jz1z7j1aJSzWwfiMkKIQDLE92h/+mTUvWYKbgS7kHC R97vGMdtKJzy55FV+7sQ== Precedence: bulk List-ID: This is a previous step to add the detection feature. A fork brute force attack will be detected when an application crashes quickly. Since, the application crash period is the time between the execve system call and the first fault or the time between two consecutives faults add a new sysctl attribute to control the crash period threshold. But a detection method based only on this computation has a drawback. If an application crashes once quickly from the execve system call or crashes twice in a short period of time for some reason, a false positive attack will be triggered. To avoid this scenario use a list of the i last crashes timestamps and compute the application crash period as follows: crash_period = (n_last_timestamp - n_minus_i_timestamp) / i; So, also add a new sysctl attribute to control the size of this list. This way, each system can tune the detection's sensibility adjusting the application crash period threshold and the size of the last crashes timestamps list. Signed-off-by: John Wood --- security/brute/brute.c | 83 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) -- 2.25.1 diff --git a/security/brute/brute.c b/security/brute/brute.c index 307d07bf9d98..29835fe2f141 100644 --- a/security/brute/brute.c +++ b/security/brute/brute.c @@ -4,12 +4,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -17,6 +19,34 @@ #include #include #include +#include + +/** + * brute_timestamps_list_size - Last crashes timestamps list size. + * + * The application crash period is the time between the execve system call and + * the first fault or the time between two consecutives faults, but this has a + * drawback. If an application crashes once quickly from the execve system call + * or crashes twice in a short period of time for some reason, a false positive + * attack will be triggered. To avoid this scenario use a list of the i last + * crashes timestamps and compute the application crash period as follows: + * + * crash_period = (n_last_timestamp - n_minus_i_timestamp) / i; + * + * The brute_timestamps_list_size variable sets the size of this list. + */ +static unsigned int brute_timestamps_list_size __read_mostly = 5; + +/** + * brute_crash_period_threshold - Application crash period threshold. + * + * The units are expressed in milliseconds. + * + * A fork brute force attack will be detected if the application crash period + * falls under this threshold. So, the higher this value, the more sensitive the + * detection will be. + */ +static unsigned int brute_crash_period_threshold __read_mostly = 30000; /** * struct brute_stats - Fork brute force attack statistics. @@ -318,6 +348,58 @@ static struct security_hook_list brute_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_free, brute_task_free), }; +#ifdef CONFIG_SYSCTL +static unsigned int uint_one = 1; +static unsigned int uint_max = UINT_MAX; +static unsigned int max_brute_timestamps_list_size = 10; + +/** + * brute_sysctl_path - Sysctl attributes path. + */ +static struct ctl_path brute_sysctl_path[] = { + { .procname = "kernel", }, + { .procname = "brute", }, + { } +}; + +/** + * brute_sysctl_table - Sysctl attributes. + */ +static struct ctl_table brute_sysctl_table[] = { + { + .procname = "timestamps_list_size", + .data = &brute_timestamps_list_size, + .maxlen = sizeof(brute_timestamps_list_size), + .mode = 0644, + .proc_handler = proc_douintvec_minmax, + .extra1 = &uint_one, + .extra2 = &max_brute_timestamps_list_size, + }, + { + .procname = "crash_period_threshold", + .data = &brute_crash_period_threshold, + .maxlen = sizeof(brute_crash_period_threshold), + .mode = 0644, + .proc_handler = proc_douintvec_minmax, + .extra1 = &uint_one, + .extra2 = &uint_max, + }, + { } +}; + +/** + * brute_init_sysctl() - Initialize the sysctl interface. + */ +static void __init brute_init_sysctl(void) +{ + if (!register_sysctl_paths(brute_sysctl_path, brute_sysctl_table)) + panic("Cannot register the sysctl interface\n"); +} + +#else +static inline void brute_init_sysctl(void) { } +#endif /* CONFIG_SYSCTL */ + /** * brute_init() - Initialize the brute LSM. * @@ -328,6 +410,7 @@ static int __init brute_init(void) pr_info("Brute initialized\n"); security_add_hooks(brute_hooks, ARRAY_SIZE(brute_hooks), KBUILD_MODNAME); + brute_init_sysctl(); return 0; } From patchwork Sun Oct 25 13:45:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Wood X-Patchwork-Id: 11855329 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 02B266A2 for ; Sun, 25 Oct 2020 16:27:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D546020874 for ; Sun, 25 Oct 2020 16:27:34 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=gmx.net header.i=@gmx.net header.b="Nf18DFPn" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1417145AbgJYQ1J (ORCPT ); Sun, 25 Oct 2020 12:27:09 -0400 Received: from mout.gmx.net ([212.227.17.20]:43621 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1417124AbgJYQ1J (ORCPT ); Sun, 25 Oct 2020 12:27:09 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1603643204; bh=pNENR6NYpvtez0wq6byw3Bmq32UAhJHvRjyV0Y2UrCw=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=Nf18DFPn1FJtAhUjcCn9Myacw3Ib7PWLI1PH/jkfN9J+q9kWo+6ADefF0fhpZ2pKV rFDx7pNMtfAEuyNjRZVL4jBc/ADnzd2xVxPefyn4SPF8qEpy+KYrWfgW3O3+AgR098 bKABUvh1zpHrNUdcXfjqSJ+zItP41th/vo7sODc4= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from localhost.localdomain ([83.52.231.59]) by mail.gmx.com (mrgmx105 [212.227.17.174]) with ESMTPSA (Nemesis) id 1Mv31c-1kF3Tf3PEG-00qyN1; Sun, 25 Oct 2020 17:26:43 +0100 From: John Wood To: Kees Cook , Jann Horn Cc: John Wood , Jonathan Corbet , James Morris , "Serge E. Hallyn" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Subject: [PATCH v2 4/8] security/brute: Detect a fork brute force attack Date: Sun, 25 Oct 2020 14:45:36 +0100 Message-Id: <20201025134540.3770-5-john.wood@gmx.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201025134540.3770-1-john.wood@gmx.com> References: <20201025134540.3770-1-john.wood@gmx.com> MIME-Version: 1.0 X-Provags-ID: V03:K1:jpDULrgTFkzvRL5MdNbDprGG+7NOjPXmtdh02SKJ/gibuend0zu 4FpzOT6e517C6yk6mpkDK5zei6S+mowxi3z8/XZLVMRF+SCI9NLVVW308BINp9aOwL11bnm GGjKkNzTDqhcTjSA6oZTLTsTRCHa2Tzfa08wIZatg80wkAAMYfXLKS+dKySQcJDQ4GEV6EZ qE8Gtl25sCH5YHq3cx5OQ== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1;V03:K0:wZBrcUl8kvA=:NnjH4p8YLrCWYMo+aDWE0E a13no4H61b/h3tkI6THq0QraQmfKXpzJxOLL8cZGl58ociWuQ/jrAKbJ68gyLiMWVabs9RLC7 OKPJgTyr5NMHtTGf39WBqn2i5SDoti5fhXq7FW7syccRZNOMIQB8PT7dVBTyp2knaAY/aJrjS 2nLFZgy46qwzkewLjoMp9PFnc7vsB7LBDgMEObNtYvS4+d7sS0mTkR6j+UPOcexwkxu7whk1T rO4/NZIeRVoEyrPsLiBhSj5BiQe6VxkGT/jYIXVALluh9ncqcSxT7AZiZ7mcOHthuK9SDYJuJ rH/LknJBZS2vBSI3DueTKCv31vMphq3DN87teMlbC+z1+KKW6s9NXU92aAT9nO3u946aOpEBx JqXItebzpQ3enyVaLLjo8K9dDvsSED/5sXA6ItNi0sNSX8CPWgKo5Ue4sbF8msaslx0zqcaJr sDZNsQesD73hi26icj/kT+a7ggWX41NKvwIiIyHjfCa73qygqsJyKHgOGMqkGeIH/HmdL3Efc 9FicgxJQyRCrNAW9FFb34VlcZTpQ63ulxHLggD3oG2/y8OCYUinU5jKDpYYySIpTZhO+A5d2t AksmbPeyr8LSbMAD+nzqc2H7DfnkU4dGhu+N/FHIiFvqzx72uU0UczeX7dKA1aOo195cmz23m KUfodjC7RYpdz6wOd7ivozh3zoaZkiCu78Hc/yyBnbSU94z3KedTBSDscZtGtBiMAqPNaGE5t 2dToidE3hJe+rE50vFsvRI6VGSaL02wYu+7wffC3g8H9YTFK6FxpgieO+3mNnyIb0iGZzgU3R ed7ZK+M8ptVn6miIunqVtGU+1V4vTYUwmZNjKEi0Q+d7KVb5gMqghoqZy1m11LrFYr1GwwClR EEGFjubBkxpO1jqUHhIw== Precedence: bulk List-ID: To detect a fork brute force attack is necessary that the list that holds the last crashes timestamps be updated in every fatal crash. To do so, use the "task_fatal_signal" LSM hook added in a previous step. Then, an only when this list is large enough, the application crash period can be computed as the difference between the newest crash timestamp and the oldest one divided by the size of the list. This way, the scenario where an application crashes few times in a short period of time due to reasons unrelated to a real attack is avoided. Finally, an application crash period that falls under the defined threshold shows that the application is crashing quickly and there is a clear signal that an attack is happening. Signed-off-by: John Wood --- security/brute/brute.c | 130 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) -- 2.25.1 diff --git a/security/brute/brute.c b/security/brute/brute.c index 29835fe2f141..223a18c2084a 100644 --- a/security/brute/brute.c +++ b/security/brute/brute.c @@ -339,6 +339,135 @@ static void brute_task_free(struct task_struct *task) spin_unlock(&(*stats)->lock); } +/** + * brute_add_timestamp() - Add a new entry to the last crashes timestamps list. + * @stats: Statistics that hold the last crashes timestamps list. + * @new_entry: New timestamp to add to the list. + * + * The statistics that hold the last crashes timestamps list cannot be NULL. The + * new timestamp to add to the list cannot be NULL. + * + * Context: Must be called with stats->lock held. + */ +static void brute_add_timestamp(struct brute_stats *stats, + struct brute_timestamp *new_entry) +{ + list_add_tail(&new_entry->node, &stats->timestamps); + stats->timestamps_size += 1; +} + +/** + * brute_old_timestamp_entry() - Get the oldest timestamp entry. + * @head: Last crashes timestamps list. + * + * Context: Must be called with stats->lock held. + * Return: The oldest entry added to the last crashes timestamps list. + */ +#define brute_old_timestamp_entry(head) \ + list_first_entry(head, struct brute_timestamp, node) + +/** + * brute_update_timestamps_list() - Update the last crashes timestamps list. + * @stats: Statistics that hold the last crashes timestamps list. + * @new_entry: New timestamp to update the list. + * + * Add a new timestamp structure to the list if this one has not reached the + * maximum size yet. Replace the oldest timestamp entry otherwise. + * + * The statistics that hold the last crashes timestamps list cannot be NULL. The + * new timestamp to update the list cannot be NULL. + * + * Context: Must be called with stats->lock held. + * Return: The oldest timestamp that has been replaced. NULL otherwise. + */ +static struct brute_timestamp * +brute_update_timestamps_list(struct brute_stats *stats, + struct brute_timestamp *new_entry) +{ + unsigned int list_size; + struct brute_timestamp *old_entry; + + list_size = (unsigned int)stats->timestamps_size; + if (list_size < brute_timestamps_list_size) { + brute_add_timestamp(stats, new_entry); + return NULL; + } + + old_entry = brute_old_timestamp_entry(&stats->timestamps); + list_replace(&old_entry->node, &new_entry->node); + list_rotate_left(&stats->timestamps); + + return old_entry; +} + +/** + * brute_get_crash_period() - Get the application crash period. + * @new_entry: New timestamp added to the last crashes timestamps list. + * @old_entry: Old timestamp replaced in the last crashes timestamps list. + * + * The application crash period is computed as the difference between the newest + * crash timestamp and the oldest one divided by the size of the list. This way, + * the scenario where an application crashes few times in a short period of time + * due to reasons unrelated to a real attack is avoided. + * + * The new and old timestamp cannot be NULL. + * + * Context: Must be called with stats->lock held. + * Return: The application crash period in milliseconds. + */ +static u64 brute_get_crash_period(struct brute_timestamp *new_entry, + struct brute_timestamp *old_entry) +{ + u64 jiffies; + + jiffies = new_entry->jiffies - old_entry->jiffies; + jiffies /= (u64)brute_timestamps_list_size; + + return jiffies64_to_msecs(jiffies); +} + +/** + * brute_task_fatal_signal() - Target for the task_fatal_signal hook. + * @siginfo: Contains the signal information. + * + * To detect a fork brute force attack is necessary that the list that holds the + * last crashes timestamps be updated in every fatal crash. Then, an only when + * this list is large enough, the application crash period can be computed an + * compared with the defined threshold. + * + * It's mandatory to disable interrupts before acquiring the lock since the + * task_free hook can be called from an IRQ context during the execution of the + * task_fatal_signal hook. + */ +static void brute_task_fatal_signal(const kernel_siginfo_t *siginfo) +{ + struct brute_stats **stats; + struct brute_timestamp *new_entry, *old_entry; + unsigned long flags; + u64 crash_period; + + stats = brute_stats_ptr(current); + if (WARN(!*stats, "No statistical data\n")) + return; + + new_entry = brute_new_timestamp(); + if (WARN(!new_entry, "Cannot allocate last crash timestamp\n")) + return; + + spin_lock_irqsave(&(*stats)->lock, flags); + old_entry = brute_update_timestamps_list(*stats, new_entry); + + if (old_entry) { + crash_period = brute_get_crash_period(new_entry, old_entry); + kfree(old_entry); + + if (crash_period < (u64)brute_crash_period_threshold) + pr_warn("Fork brute force attack detected\n"); + } + + spin_unlock_irqrestore(&(*stats)->lock, flags); +} + /** * brute_hooks - Targets for the LSM's hooks. */ @@ -346,6 +475,7 @@ static struct security_hook_list brute_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_alloc, brute_task_alloc), LSM_HOOK_INIT(bprm_committing_creds, brute_task_execve), LSM_HOOK_INIT(task_free, brute_task_free), + LSM_HOOK_INIT(task_fatal_signal, brute_task_fatal_signal), }; #ifdef CONFIG_SYSCTL From patchwork Sun Oct 25 13:45:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Wood X-Patchwork-Id: 11855337 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6232914B4 for ; Sun, 25 Oct 2020 17:07:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 29010208E4 for ; Sun, 25 Oct 2020 17:07:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=gmx.net header.i=@gmx.net header.b="NPKRYkx+" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1417515AbgJYRHI (ORCPT ); Sun, 25 Oct 2020 13:07:08 -0400 Received: from mout.gmx.net ([212.227.17.21]:51833 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1417514AbgJYRHI (ORCPT ); Sun, 25 Oct 2020 13:07:08 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1603645607; bh=qJWyMrjXpeE+rjYpUtbfZPi2nfj0bI6PEUYZ+A9TfME=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=NPKRYkx+eOfNAIhW+xIuStO2QsD5wrLZ6ymsj/ZQ2esY+11mnjL7KCoHWp9p8RjW0 o35wV9q8kHxPGoWUz3XJYyVL+yUUst/CvuCdSopsp9Ht2kEvdOvGyzABNDK5NEmSKV hQtNR3KZ3P1RK64oeyh1sqRkNyFthyjXwf2Z/eg0= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from localhost.localdomain ([83.52.231.59]) by mail.gmx.com (mrgmx104 [212.227.17.174]) with ESMTPSA (Nemesis) id 1MLi8m-1koOt509Cq-00HjZH; Sun, 25 Oct 2020 18:06:47 +0100 From: John Wood To: Kees Cook , Jann Horn Cc: John Wood , Jonathan Corbet , James Morris , "Serge E. Hallyn" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Subject: [PATCH v2 5/8] security/brute: Mitigate a fork brute force attack Date: Sun, 25 Oct 2020 14:45:37 +0100 Message-Id: <20201025134540.3770-6-john.wood@gmx.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201025134540.3770-1-john.wood@gmx.com> References: <20201025134540.3770-1-john.wood@gmx.com> MIME-Version: 1.0 X-Provags-ID: V03:K1:77xi6IvKdGSSBMMKfua9wA0Oa9DZLborxMu1NdaOiO2Lj+qhobI e38gbQKHc3gzjETsO/vAVViY33uuYot1GogAhq+40KanLotMoD8GGMw4JBxbMp2O8Y2mKYO d7BOq5wvtqJOWvevUTmA6D/NNTQhrPrYRqhRrITCDA4zhhJ62rUvMLc0nnU4gWnaDe+mg9A QlYZBFz4U2KzsP6JvLKGg== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1;V03:K0:T2PcRj1NkPg=:nHqPkat2rraJOPUjD5nn47 ip6v6TVOH0UHQFsLGj/QxRW/1FEgBtIWu46PUzycDhjzsEf/hAqZEVhXdj0NRqYDHk+A+/K6Y byJP6l93IOzGX0X3iJXZyc39BUHrU+P8t29c715bdTLMUPj1aoMOOQ6BLaBuyD1Ncnr99UVbR hqKcHmMpUhBjnrRbYUpBqgufMPgqSphdHaJNEuo0zO0KJut1U3S8obTnnBcu2z6gXPfghBfh8 THxCN/rdVxDccxxJIg/Pwmw450W3MlQQzDjAUHBlpH1YNjRmearOtpxVHbnBbHTdiPD6/94Ps xi0JYeFbmh4NQNIiGJOw0FtUSaU7V3qVR2qMWjh6TzWtX7xl5r9d92tcrtRj0MpNhThfWAwyX Jaafed1Ir6/E2GPpDIRPSRKydit+Cr5IqyaVm53FdvnTRiTmHDqgEweYvR6+E1NKmFlhht55T n3LqXUaZLSOSHHfzQvSwzgsp39tk0kBtBGGXsWjB9tFLtE4oI2xfbU+EbtwunU61ZqoHdraB9 j8opcY7Eb5nDskXBCE+8VBKHMEBJ2pibx0DhZ+K73YL6loYMClMIwjju54ZdVZw+b6blf1kp/ QlFyX+GkX50+hgnuRPBiJR3WuMn/ZEyArSsw4HBW2VqSrBBZvoQTha0xzgYfoSpUaKfr/CSCV RhqsyGXMNr2thrimFjFn2FXS5xQKT9IOwhWKmlfGBI69kDqDOVLO4NZlKx0PUS1EmF/mzXEmw /s1R3d8u6qT67cu2Nz7aeg3uX7bUOEGGAaEMmrv93yC3B9Sd4T1EZ2+7ddMC5bd35JNcnH20f rxO6/U4Qy3wVSfmp8AyqXPnTGXKmyidn8JDLaKh9/jQjEsJVGigFgeihmuYAOZWLudErigRsv iCNREpE+jL4hXCdQn+sA== Precedence: bulk List-ID: In order to mitigate a fork brute force attack it is necessary to kill all the offending tasks. This tasks are all the ones that share the statistical data with the current task (the task that has crashed). Since the attack detection is done in the task_fatal_signal LSM hook only is needed to kill the other tasks that share the same statistical data, not the ones that have the same group_leader that the current task since the latter are in the path to be killed. When the SIGKILL signal is sent to the offending tasks, the brute_kill_offending_tasks function will be called in a recursive way from the task_fatal_signal LSM hook due to a small crash period. So, to avoid kill again the same tasks due to a recursive call of this function, it is necessary to disable the attack detection for this fork hierarchy. To disable this attack detection, empty the last crashes timestamps list and avoid to compute the application crash period if the size of this list is zero. Signed-off-by: John Wood --- security/brute/brute.c | 144 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 9 deletions(-) -- 2.25.1 diff --git a/security/brute/brute.c b/security/brute/brute.c index 223a18c2084a..a1bdf25ffcf9 100644 --- a/security/brute/brute.c +++ b/security/brute/brute.c @@ -3,6 +3,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -14,9 +15,14 @@ #include #include #include +#include #include #include +#include #include +#include +#include +#include #include #include #include @@ -295,23 +301,39 @@ static void brute_task_execve(struct linux_binprm *bprm) } /** - * brute_stats_free() - Deallocate a statistics structure. - * @stats: Statistics to be freed. + * brute_timestamps_free() - Empty a last crashes timestamp list. + * @timestamps: Last crashes timestamps list to be emptied. * - * Deallocate all the last crashes timestamps list entries and then the - * statistics structure. The statistics to be freed cannot be NULL. + * Empty the last crashes timestamps list and deallocate all the entries. This + * list cannot be NULL. * - * Context: Must be called with stats->lock held and this function releases it. + * Context: Must be called with stats->lock held. */ -static void brute_stats_free(struct brute_stats *stats) +static void brute_timestamps_free(struct list_head *timestamps) { struct brute_timestamp *timestamp, *next; - list_for_each_entry_safe(timestamp, next, &stats->timestamps, node) { + if (list_empty(timestamps)) + return; + + list_for_each_entry_safe(timestamp, next, timestamps, node) { list_del(×tamp->node); kfree(timestamp); } +} +/** + * brute_stats_free() - Deallocate a statistics structure. + * @stats: Statistics to be freed. + * + * Deallocate all the last crashes timestamps list entries and then the + * statistics structure. The statistics to be freed cannot be NULL. + * + * Context: Must be called with stats->lock held and this function releases it. + */ +static void brute_stats_free(struct brute_stats *stats) +{ + brute_timestamps_free(&stats->timestamps); spin_unlock(&stats->lock); kfree(stats); } @@ -426,6 +448,104 @@ static u64 brute_get_crash_period(struct brute_timestamp *new_entry, return jiffies64_to_msecs(jiffies); } +/** + * brute_disabled() - Test the fork brute force attack detection disabling. + * @stats: Statistical data shared by all the fork hierarchy processes. + * + * The fork brute force attack detection enabling / disabling is based on the + * last crashes timestamps list current size. A size of zero indicates that this + * feature is disabled. A size greater than zero indicates that this attack + * detection is enabled. + * + * The statistical data shared by all the fork hierarchy processes cannot be + * NULL. + * + * It's mandatory to disable interrupts before acquiring the lock since the + * task_free hook can be called from an IRQ context during the execution of the + * task_fatal_signal hook. + * + * Return: True if the fork brute force attack detection is disabled. False + * otherwise. + */ +static bool brute_disabled(struct brute_stats *stats) +{ + unsigned long flags; + bool disabled; + + spin_lock_irqsave(&stats->lock, flags); + disabled = !stats->timestamps_size; + spin_unlock_irqrestore(&stats->lock, flags); + + return disabled; +} + +/** + * brute_disable() - Disable the fork brute force attack detection. + * @stats: Statistical data shared by all the fork hierarchy processes. + * + * To disable the fork brute force attack detection it's only necessary to empty + * the last crashes timestamps list. So, a list size of zero indicates that this + * feature is disabled and a list size greater than zero indicates that this + * attack detection is enabled. + * + * The statistical data shared by all the fork hierarchy processes cannot be + * NULL. + * + * Context: Must be called with stats->lock held. + */ +static void brute_disable(struct brute_stats *stats) +{ + brute_timestamps_free(&stats->timestamps); + stats->timestamps_size = 0; +} + +/** + * brute_kill_offending_tasks() - Kill the offending tasks. + * @stats: Statistical data shared by all the fork hierarchy processes. + * + * When a fork brute force attack is detected it is necessary to kill all the + * offending tasks involved in the attack. In other words, it is necessary to + * kill all the tasks that share the same statistical data but not the ones that + * have the same group_leader that the current task since the latter are in the + * path to be killed. + * + * When the SIGKILL signal is sent to the offending tasks, this function will be + * called again from the task_fatal_signal hook due to a small crash period. So, + * to avoid kill again the same tasks due to a recursive call of this function, + * it is necessary to disable the attack detection for this fork hierarchy. + * + * The statistical data shared by all the fork hierarchy processes cannot be + * NULL. + * + * Context: Must be called with stats->lock held. + */ +static void brute_kill_offending_tasks(struct brute_stats *stats) +{ + struct task_struct *p; + struct brute_stats **p_stats; + + if (refcount_read(&stats->refc) == 1) + return; + + brute_disable(stats); + read_lock(&tasklist_lock); + + for_each_process(p) { + if (p->group_leader == current->group_leader) + continue; + + p_stats = brute_stats_ptr(p); + if (READ_ONCE(*p_stats) != stats) + continue; + + do_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_PID); + pr_warn_ratelimited("Offending process %d (%s) killed\n", + p->pid, p->comm); + } + + read_unlock(&tasklist_lock); +} + /** * brute_task_fatal_signal() - Target for the task_fatal_signal hook. * @siginfo: Contains the signal information. @@ -433,7 +553,8 @@ static u64 brute_get_crash_period(struct brute_timestamp *new_entry, * To detect a fork brute force attack is necessary that the list that holds the * last crashes timestamps be updated in every fatal crash. Then, an only when * this list is large enough, the application crash period can be computed an - * compared with the defined threshold. + * compared with the defined threshold. If at this moment an attack is detected, + * all the offending tasks must be killed. * * It's mandatory to disable interrupts before acquiring the lock since the * task_free hook can be called from an IRQ context during the execution of the @@ -450,6 +571,9 @@ static void brute_task_fatal_signal(const kernel_siginfo_t *siginfo) if (WARN(!*stats, "No statistical data\n")) return; + if (brute_disabled(*stats)) + return; + new_entry = brute_new_timestamp(); if (WARN(!new_entry, "Cannot allocate last crash timestamp\n")) return; @@ -461,8 +585,10 @@ static void brute_task_fatal_signal(const kernel_siginfo_t *siginfo) crash_period = brute_get_crash_period(new_entry, old_entry); kfree(old_entry); - if (crash_period < (u64)brute_crash_period_threshold) + if (crash_period < (u64)brute_crash_period_threshold) { pr_warn("Fork brute force attack detected\n"); + brute_kill_offending_tasks(*stats); + } } spin_unlock_irqrestore(&(*stats)->lock, flags); From patchwork Sun Oct 25 13:45:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Wood X-Patchwork-Id: 11855363 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 27629697 for ; Sun, 25 Oct 2020 17:47:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0676D21D41 for ; Sun, 25 Oct 2020 17:47:52 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=gmx.net header.i=@gmx.net header.b="c+Zd4Om1" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1417812AbgJYRrU (ORCPT ); Sun, 25 Oct 2020 13:47:20 -0400 Received: from mout.gmx.net ([212.227.17.22]:51073 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1417807AbgJYRrS (ORCPT ); Sun, 25 Oct 2020 13:47:18 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1603648010; bh=46tXLmeDZxoZnd9ahI0FOEafCPrksfhcOGGheXfG4Fw=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=c+Zd4Om1zzKBFjABTliqLmRqPj3WdxTb3CFuN7H9d0IbjW35o0JRcVlhOdbLs0ah9 YwmS8vN2zEfdsqoW+/BtBN7SPMRB+N3z6sZKpF7ZbVSpIKTgvWhQ8v67hFiGJBg1Ia MsOzUsfXMZTM5VHL04oV7RZLQrqNb90mb7ktKkgs= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from localhost.localdomain ([83.52.231.59]) by mail.gmx.com (mrgmx105 [212.227.17.174]) with ESMTPSA (Nemesis) id 1MZkpb-1krwAH1Mfv-00Wjuw; Sun, 25 Oct 2020 18:46:50 +0100 From: John Wood To: Kees Cook , Jann Horn Cc: John Wood , Jonathan Corbet , James Morris , "Serge E. Hallyn" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Subject: [PATCH v2 6/8] security/brute: Add prctls to enable/disable the fork attack detection Date: Sun, 25 Oct 2020 14:45:38 +0100 Message-Id: <20201025134540.3770-7-john.wood@gmx.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201025134540.3770-1-john.wood@gmx.com> References: <20201025134540.3770-1-john.wood@gmx.com> MIME-Version: 1.0 X-Provags-ID: V03:K1:y8g9unw+ZDo8pqpejS21tJx/xOIOj5BtZUmKSvK51UzvxtkRlyz M51rNcVIJMjG5KMuMCQ/GlRzamPn6rXzGJXIb0TfgDRGOw/1a8bKGkmO6vU/qTtmu6orNyi vDzRVK+t2A5xqsBKAtAn1HBgGrU0uEBTJYMSzlWrQeySLPr+PqexeXwWZ718Kaz7j+9mlMH EUUVbNJPWp5PIeeFu1eQg== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1;V03:K0:y3MmYaVH5K8=:2AqjJSdIUPwhC76kMXt0Kl oREs/VdbnYEA5cEbe2g+9SagWZF0hlFZXmAfdBX3kUiwYgKLbkJ766hP/50LJzZjxwGIGVlll 1jTzYfURwRcTqGfxIR4hJG4iGdPwdV4Oym1loHEEf3NisqcV/YablzEoRy+7/1d2egl4W0GLl fY2YGcM/RfoBvC0xCWI4AUpeJAXvFvfUBYXDSXgb8ivyCBS35JG8t455hvlrqcjWh7QwHl09/ X2QloSo7la2SxX/eZIR4CMvmQkBulZGoBN6aFNYP6sRJ59czVuaUknx/9y+F4JWUcNjSyzxuP YypthcKsyVCtcTI9P2IoqjpSf2b1RSKJuqgYblB+C04Mz5fMN20tgrWPxJGH3PPhgz5DoZAfc bZ2A6fYxNNua6PyaJCkFHTLU5WTIEA7GlTFouV5tHYs0wGiQ0ZstUTzjxVMSMqtsoRqb0moxY OphjVmtcx2HzedEoSJ/CsOXZksiwHdK0PwdJfioVrMcAN4XxDYhNBgr0LvIOiJc2PGBNltV49 4w8chzkKFHwi9uqlW4UydP22gtdxkLhKsm/msKdW3870U2JkOgZ/YfCE8OjS9CAfMX0udC39K IrqkAHPcVPu2L38Leq3IIZBIzvOvYFP5sBsuLThZjaQgAdmQRbjDN8N9qeKgeQyJeOroAnXYN uQQphWlBnMC7KywJEzLSMY7od5N8mKp7VM8WwPSu0tgMQtBHO/tgYwAcLadLxk+O6InP3LYC6 ZDuP+TXHu4RvgS9790U3l7B4cxjAM49P/8PRiaOQdj4YaOJ2F6P/n/IUFVJCQQ+ovf4OArJtt KK4JmtdUev4PKNngrT6JaZVCT+DoGQYEn0712PZNRgbSk+Yy4BpM8+smIfr4L5okkQO5bYGdL Hb8iCgOU2eFFMZm+2QAg== Precedence: bulk List-ID: To allow that a process can turn off or turn on the detection and mitigation of a fork brute force attack when required, add two new defines to the prctl interface. All the arguments passed to the prctl system call are ignored for the two new cases. To enable the attack detection make the last crashes timestamps list not empty. To disable the detection use the already created brute_disable() function. Signed-off-by: John Wood --- include/brute/brute.h | 16 +++++++++ include/uapi/linux/prctl.h | 4 +++ kernel/sys.c | 8 +++++ security/brute/brute.c | 71 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 include/brute/brute.h -- 2.25.1 diff --git a/include/brute/brute.h b/include/brute/brute.h new file mode 100644 index 000000000000..da6fca04f16b --- /dev/null +++ b/include/brute/brute.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BRUTE_H_ +#define _BRUTE_H_ + +#include + +#ifdef CONFIG_SECURITY_FORK_BRUTE +int brute_prctl_enable(void); +int brute_prctl_disable(void); +#else +static inline int brute_prctl_enable(void) { return -EINVAL; } +static inline int brute_prctl_disable(void) { return -EINVAL; } +#endif + +#endif /* _BRUTE_H_ */ + diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index 07b4f8131e36..01f5033210d0 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -238,4 +238,8 @@ struct prctl_mm_map { #define PR_SET_IO_FLUSHER 57 #define PR_GET_IO_FLUSHER 58 +/* Enable/disable the detection and mitigation of a fork brute force attack */ +#define PR_SECURITY_FORK_BRUTE_ENABLE 59 +#define PR_SECURITY_FORK_BRUTE_DISABLE 60 + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/sys.c b/kernel/sys.c index ab6c409b1159..35dae4e2f59a 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -72,6 +72,8 @@ #include #include +#include + #include "uid16.h" #ifndef SET_UNALIGN_CTL @@ -2530,6 +2532,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, error = (current->flags & PR_IO_FLUSHER) == PR_IO_FLUSHER; break; + case PR_SECURITY_FORK_BRUTE_ENABLE: + error = brute_prctl_enable(); + break; + case PR_SECURITY_FORK_BRUTE_DISABLE: + error = brute_prctl_disable(); + break; default: error = -EINVAL; break; diff --git a/security/brute/brute.c b/security/brute/brute.c index a1bdf25ffcf9..6f85e137553c 100644 --- a/security/brute/brute.c +++ b/security/brute/brute.c @@ -676,3 +676,74 @@ DEFINE_LSM(brute) = { .blobs = &brute_blob_sizes, }; +/** + * brute_prctl_enable() - Enable the fork brute force attack detection. + * + * To enable the fork brute force attack detection the last crashes timestamps + * list must not be empty. So, if this list already contains entries nothing + * needs to be done. Otherwise, initialize the last crashes timestamps list with + * one entry set to now. This way, the application crash period can be computed + * at the next fault. + * + * It's mandatory to disable interrupts before acquiring the lock since the + * task_free hook can be called from an IRQ context during the execution of the + * prctl syscall. + * + * Return: -EFAULT if the current task doesn't have statistical data. -ENOMEM if + * the allocation of the new timestamp structure fails. Zero otherwise. + */ +int brute_prctl_enable(void) +{ + struct brute_stats **stats; + struct brute_timestamp *timestamp; + unsigned long flags; + + stats = brute_stats_ptr(current); + if (!*stats) + return -EFAULT; + + timestamp = brute_new_timestamp(); + if (!timestamp) + return -ENOMEM; + + spin_lock_irqsave(&(*stats)->lock, flags); + + if (!list_empty(&(*stats)->timestamps)) { + kfree(timestamp); + goto unlock; + } + + list_add_tail(×tamp->node, &(*stats)->timestamps); + (*stats)->timestamps_size = 1; + +unlock: + spin_unlock_irqrestore(&(*stats)->lock, flags); + return 0; +} + +/** + * brute_prctl_disable() - Disable the fork brute force attack detection. + * + * It's mandatory to disable interrupts before acquiring the lock since the + * task_free hook can be called from an IRQ context during the execution of the + * prctl syscall. + * + * Return: -EFAULT if the current task doesn't have statistical data. Zero + * otherwise. + */ +int brute_prctl_disable(void) +{ + struct brute_stats **stats; + unsigned long flags; + + stats = brute_stats_ptr(current); + if (!*stats) + return -EFAULT; + + spin_lock_irqsave(&(*stats)->lock, flags); + brute_disable(*stats); + spin_unlock_irqrestore(&(*stats)->lock, flags); + + return 0; +} + From patchwork Sun Oct 25 13:45:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Wood X-Patchwork-Id: 11855381 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7FA6F61C for ; Sun, 25 Oct 2020 18:27:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 55F6122269 for ; Sun, 25 Oct 2020 18:27:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=gmx.net header.i=@gmx.net header.b="gx2M5Ibx" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1418053AbgJYS1O (ORCPT ); Sun, 25 Oct 2020 14:27:14 -0400 Received: from mout.gmx.net ([212.227.17.21]:44987 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1418049AbgJYS1N (ORCPT ); Sun, 25 Oct 2020 14:27:13 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1603650413; bh=YBgtMfwJmAL5xT3k46Wa4vvB+Oniw3l18Xi6Adps7Bc=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=gx2M5IbxuA/STiTr7+7WRKOkMO76F22KonZwtZAzY+eT/R93QxuJJLRTfzOV+Z2hA FjgvcePMLYv5Spd9Jdv1pe0+tKPDUhB46iokruuFHfFaJtus9GLmErCZOY1maUlbS+ JF0KKikw7ZdX3W8kPdfxFi+RiCEeO6PEmM3v+Joc= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from localhost.localdomain ([83.52.231.59]) by mail.gmx.com (mrgmx104 [212.227.17.174]) with ESMTPSA (Nemesis) id 1MEm6F-1kdgTX3R3j-00GM8r; Sun, 25 Oct 2020 19:26:52 +0100 From: John Wood To: Kees Cook , Jann Horn Cc: John Wood , Jonathan Corbet , James Morris , "Serge E. Hallyn" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Subject: [PATCH v2 7/8] Documentation: Add documentation for the Brute LSM Date: Sun, 25 Oct 2020 14:45:39 +0100 Message-Id: <20201025134540.3770-8-john.wood@gmx.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201025134540.3770-1-john.wood@gmx.com> References: <20201025134540.3770-1-john.wood@gmx.com> MIME-Version: 1.0 X-Provags-ID: V03:K1:Cz3+eZlgwFAv6f5xHTA4nCXky6FI3BQn9S+EHC231hZb0xz0I6J +lavJK3hNxGWnTLOhuYbKVho/t2MC6+tZ4wceYFUzI9ALP6VLisk4OZzyvQymG1z7dpH6Kh ZzkSxRDlE2J91ifVTth/f/NashXwCpu9U1kENqxJ4dF4Uig3fiuVuG87PI46uJW2zYj6x0b iDJVAsK6IJ266XUmRcIyg== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1;V03:K0:tmic9nfnEuk=:/Vidx7QpmAsEj70awUAUmV wsqrWv8AHeZqX9GnoXKwo2sFlzU3AHFYG/jJnLIEyf/R9meSUIaKyvQj0nCuu2sX4q9+v6lXL Vsw2MYKW3fAu6di+R1xo5SagAvqTdhOTsPLBDxnPqa6VTTZlMub3S4vTHDdh491keZ4l/MIfO yAtVtYqrqHlhFFsJJd7mDs/kzlFsDYshy7dsCqKQ62TR0DXBeJM6JXj+aNJ8IJfOn8iTCvVDA /CDVp+DOKOz3m4PxeNNdovSly97f6vwEPUzmFUqGCmTwiyqYCcFl+RBZwJNSFdJQgoP/ODaJW 4gvTK703KBWTPNnj1EM974ukzylK9M5YQnmiqvLV9/wsL6qA0CLNDmsXJS1Qfo2JiIg8HQC82 +xhmISUy/4mR/DeNJQOrsO0sHa0ISJtygLppcbfFB6KljibcrPB5QiGGrYateiqTBlD/vhaj/ xzHk8Vd/svjcHAQ4RoYBemhiRIdf0zCNxrrkiq53xl392oXP/f0URDzZDdRkxxlCVYQXugu6x 0cQRgXCGBFIKbEU4MHrY+lybYPmnyUvRZr7hxl/MHCdwdM0MR6Nj39kCBOJPldmfjk8gcd64w igecjOT3EzXbNhEI22tNjk9K0Bma01JHv66tYUJ+4TCXW58KrSXDCGxY28P1bu2rvYsbiSzRQ eyAf2iqz7Zx0ZRWOugJt29BNb1IU8AUe+fbNrJ9UAVxzdcJ2JHMMXiskczHohg/FdEHaS49O7 pQ5DJQUESQxstFbx6+SoDhQ6NCUjrS1BfluwhdcZ9ihGy3fznQ8BPzTMLHeqLBssdoL626WOk 6tPvuqCsz44kYJDG6i9pWgbuAWkE1GBjgSgec2N5ZisGhYYwkzsFBR0t/8jQ27f/tFQ7cZjhV kPtuuZ2EHpD449w06XBw== Precedence: bulk List-ID: Add some info detailing what is the Brute LSM, its motivation, weak points of existing implementations, proposed solutions, enabling, disabling and fine tuning. Signed-off-by: John Wood --- Documentation/admin-guide/LSM/Brute.rst | 118 ++++++++++++++++++++++++ Documentation/admin-guide/LSM/index.rst | 1 + security/brute/Kconfig | 3 +- 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 Documentation/admin-guide/LSM/Brute.rst -- 2.25.1 diff --git a/Documentation/admin-guide/LSM/Brute.rst b/Documentation/admin-guide/LSM/Brute.rst new file mode 100644 index 000000000000..20c6ccbd625d --- /dev/null +++ b/Documentation/admin-guide/LSM/Brute.rst @@ -0,0 +1,118 @@ +.. SPDX-License-Identifier: GPL-2.0 +=========================================================== +Brute: Fork brute force attack detection and mitigation LSM +=========================================================== + +Attacks against vulnerable userspace applications with the purpose to break ASLR +or bypass canaries traditionaly use some level of brute force with the help of +the fork system call. This is possible since when creating a new process using +fork its memory contents are the same as those of the parent process (the +process that called the fork system call). So, the attacker can test the memory +infinite times to find the correct memory values or the correct memory addresses +without worrying about crashing the application. + +Based on the above scenario it would be nice to have this detected and +mitigated, and this is the goal of this implementation. + + +Other implementations +===================== + +The public version of grsecurity, as a summary, is based on the idea of delay +the fork system call if a child died due to a fatal error. This has some issues: + +Bad practices +------------- + +Add delays to the kernel is, in general, a bad idea. + +Weak points +----------- + +This protection can be bypassed using two different methods since it acts only +when the fork is called after a child has crashed. + +Bypass 1 +~~~~~~~~ + +So, it would still be possible for an attacker to fork a big amount of children +(in the order of thousands), then probe all of them, and finally wait the +protection time before repeat the steps. + +Bypass 2 +~~~~~~~~ + +This method is based on the idea that the protection doesn't act if the parent +crashes. So, it would still be possible for an attacker to fork a process and +probe itself. Then, fork the child process and probe itself again. This way, +these steps can be repeated infinite times without any mitigation. + + +This implementation +=================== + +The main idea behind this implementation is to improve the existing ones +focusing on the weak points annotated before. The solution for the first bypass +method is to detect a fast crash rate instead of only one simple crash. For the +second bypass method the solution is to detect both the crash of parent and +child processes. Moreover, as a mitigation method it is better to kill all the +offending tasks involve in the attack instead of use delays. + +So, the solution to the two bypass methods previously commented is to use some +statistical data shared across all the processes that can have the same memory +contents. Or in other words, a statistical data shared between all the fork +hierarchy processes after an execve system call. + +The purpose of these statistics is to compute the application crash period in +order to detect an attack. This crash period is the time between the execve +system call and the first fault or the time between two consecutives faults, but +this has a drawback. If an application crashes once quickly from the execve +system call or crashes twice in a short period of time for some reason, a false +positive attack will be triggered. To avoid this scenario the shared statistical +data holds a list of the i last crashes timestamps and the application crash +period is computed as follows: + +crash_period = (n_last_timestamp - n_minus_i_timestamp) / i; + +This ways, the size of the last crashes timestamps list allows to fine tuning +the detection sensibility. + +When this crash period falls under a certain threshold there is a clear signal +that something malicious is happening. Once detected, the mitigation only kills +the processes that share the same statistical data and so, all the tasks that +can have the same memory contents. This way, an attack is rejected. + +Per system enabling +------------------- + +This feature can be enabled at build time using the CONFIG_SECURITY_FORK_BRUTE +option or using the visual config application under the following menu: + +Security options ---> Fork brute force attack detection and mitigation + +Per process enabling/disabling +------------------------------ + +To allow that specific applications can turn off or turn on the detection and +mitigation of a fork brute force attack when required, there are two new prctls. + +prctl(PR_SECURITY_FORK_BRUTE_ENABLE, 0, 0, 0, 0) -> To enable the feature +prctl(PR_SECURITY_FORK_BRUTE_DISABLE, 0, 0, 0, 0) -> To disable the feature + +Fine tuning +----------- + +To customize the detection's sensibility there are two new sysctl attributes +that allow to set the last crashes timestamps list size and the application +crash period threshold (in milliseconds). Both are accessible through the +following files respectively. + +/proc/sys/kernel/brute/timestamps_list_size +/proc/sys/kernel/brute/crash_period_threshold + +The list size allows to avoid false positives due to crashes unrelated with a +real attack. The period threshold sets the time limit to detect an attack. And, +since a fork brute force attack will be detected if the application crash period +falls under this threshold, the higher this value, the more sensitive the +detection will be. + diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst index a6ba95fbaa9f..1f68982bb330 100644 --- a/Documentation/admin-guide/LSM/index.rst +++ b/Documentation/admin-guide/LSM/index.rst @@ -41,6 +41,7 @@ subdirectories. :maxdepth: 1 apparmor + Brute LoadPin SELinux Smack diff --git a/security/brute/Kconfig b/security/brute/Kconfig index 1bd2df1e2dec..334d7e88d27f 100644 --- a/security/brute/Kconfig +++ b/security/brute/Kconfig @@ -7,6 +7,7 @@ config SECURITY_FORK_BRUTE vulnerable userspace processes. The detection method is based on the application crash period and as a mitigation procedure all the offending tasks are killed. Like capabilities, this security module - stacks with other LSMs. + stacks with other LSMs. Further information can be found in + Documentation/admin-guide/LSM/Brute.rst. If you are unsure how to answer this question, answer N. From patchwork Sun Oct 25 13:45:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Wood X-Patchwork-Id: 11855391 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C0042697 for ; Sun, 25 Oct 2020 19:07:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 816CA222D9 for ; Sun, 25 Oct 2020 19:07:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=gmx.net header.i=@gmx.net header.b="iKiHad+o" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1418415AbgJYTHQ (ORCPT ); Sun, 25 Oct 2020 15:07:16 -0400 Received: from mout.gmx.net ([212.227.17.20]:57935 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732698AbgJYTHQ (ORCPT ); Sun, 25 Oct 2020 15:07:16 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1603652816; bh=OzBPTWP4HniyfRVhLuAvnT82ocK2l9QTQfTuhozWfks=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=iKiHad+oFYBEo2S4MDbobe8L98THGorbfMtWMACwPoO8Dw97vLJaBmG9OLmlgSKQx +ghSM309QDA31mODmy4e7gatEmuMovE9YgK0nGPM/C5QHQFBTv5Bw6ACD1yrVjOkhr 10iBRZXuDykHriiTPgyDwBRisAB3xWd9BXeSaA6A= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from localhost.localdomain ([83.52.231.59]) by mail.gmx.com (mrgmx105 [212.227.17.174]) with ESMTPSA (Nemesis) id 1N8ofO-1kIr973kgg-015ob9; Sun, 25 Oct 2020 20:06:56 +0100 From: John Wood To: Kees Cook , Jann Horn Cc: John Wood , Jonathan Corbet , James Morris , "Serge E. Hallyn" , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, kernel-hardening@lists.openwall.com Subject: [PATCH v2 8/8] MAINTAINERS: Add a new entry for the Brute LSM Date: Sun, 25 Oct 2020 14:45:40 +0100 Message-Id: <20201025134540.3770-9-john.wood@gmx.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201025134540.3770-1-john.wood@gmx.com> References: <20201025134540.3770-1-john.wood@gmx.com> MIME-Version: 1.0 X-Provags-ID: V03:K1:XIkBc5Sdz3SQJ6bMKfEKk7IVGMhrkpkVRcigKF7B9QVK89FXQOG 0OXXB3iAd6TG2csWcgUkrut1+4mdxqWBqpFO83i3YUtRy96voZE6A9FMgIZ/yQDRvxXmkRS HGMpll8CeJps9oY3WGx9ISJlW5QCpU/dGSm4rYvOqqTUX4F+l8LfJhR8dyAGu9J0DIlpxTP plBlxnkMnCnQWNLeICoig== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1;V03:K0:Ads6fhSOCR4=:3aznbMalMEe0vjYLyMH/EA E/VnASCMh9PuPYgko4bSmr76hG594GPyUWhJC+LzestMxYRLMtE5a9kbL8Erdz/4Zz7nJlgVA D+D70QFEDrWD00lU69ZZawkE6jw2uVm8/4KQlbWzdYgNRP7WXqIIbzdiiMxhj3T8ErTBGf/eP VZXhTzu1HXkdBCrwVh8tN3D7RYYXFTxoktpmCT0HX6n8AmvVViHKW2Mk8INbwdY34LMNc5iEO s8do4oXXxrXhGoa+yAor7VK8nLsVxPKT66j65Hq8JGnRSAXgXNJbxJ/Pew/DtUr/BgcYf4vfg O3nZzwxJLw28X+TuoBSLFdE+67SIzxxb5dLzKqTKMgLQvYKrz7FNeWAYxAOLyA+JvHn+Cfp5K gF43XgJBwrOxQcUWP4cccUAG6B8J5pmNpVAzapUZtLBLPn+GLiqFcep1PFPDfMUz+2ZaAGi59 A/yCVU8MKPpqd3ZdzMBENw3/++klckQt7h5Dqp2/BhgIRLtHhGcsS2J4DsE1N6arr42SuNoIB ng7PiDh+No/qMh+xNIDCAnIxFQu5mjqczemIAvpifbYrtnou6t2qpZ1SJvsmVmfkzOXWWEJ2J tUzySvpoIIurMlxr4Pbb3JRleKUtzdKHjQJCxEfl/1+Hqe72y82JiwcFtriR5FZnF+GR4SWfv pP1HOxunDZf4Hq/nL8rFKjnz5vAv3tJDaBtIxZVrXQx33r5IvUH1R4TQIF3FahULszB0kglJX VRM2IoZWPfwMwaos8aDNWV0A9ifg4J2EP9+g9SxqDaSvqIvYX4jyyEesKpMfkgRl95fcyoR1C fqWANcuXVvbzyT8k3K0vCNXxe4VnbjlIbXpxjf4iYG0s4wTEq2mwXadK8+2eJBTRWwgCVAdLz hL46VgBWfWhB2DKAvZxA== Precedence: bulk List-ID: In order to maintain the code for the Brute LSM add a new entry to the maintainers list. Signed-off-by: John Wood --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) -- 2.25.1 diff --git a/MAINTAINERS b/MAINTAINERS index 867157311dc8..3d3b34f87913 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3734,6 +3734,13 @@ L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/brocade/bna/ +BRUTE SECURITY MODULE +M: John Wood +S: Maintained +F: Documentation/admin-guide/LSM/Brute.rst +F: include/brute/ +F: security/brute/ + BSG (block layer generic sg v4 driver) M: FUJITA Tomonori L: linux-scsi@vger.kernel.org