lkml.org 
[lkml]   [2020]   [Sep]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    Date
    From
    SubjectRe: [RFC PATCH 6/6] security/fbfam: Mitigate a fork brute force attack
    On Thu, Sep 10, 2020 at 01:21:07PM -0700, Kees Cook wrote:
    > From: John Wood <john.wood@gmx.com>
    >
    > 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 function fbfam_handle_attack()
    > that is called every time a core dump is triggered, only is needed to
    > kill the others tasks that share the same statistical data, not the
    > current one as this is in the path to be killed.
    >
    > When the SIGKILL signal is sent to the offending tasks from the function
    > fbfam_kill_tasks(), this one will be called again during the core dump
    > due to the shared statistical data shows a quickly crashing rate. So, to
    > avoid kill again the same tasks due to a recursive call of this
    > function, it is necessary to disable the attack detection.
    >
    > To disable this attack detection, add a condition in the function
    > fbfam_handle_attack() to not compute the crashing rate when the jiffies
    > stored in the statistical data are set to zero.
    >
    > Signed-off-by: John Wood <john.wood@gmx.com>
    > ---
    > security/fbfam/fbfam.c | 76 +++++++++++++++++++++++++++++++++++++++---
    > 1 file changed, 71 insertions(+), 5 deletions(-)
    >
    > diff --git a/security/fbfam/fbfam.c b/security/fbfam/fbfam.c
    > index 3aa669e4ea51..173a6122390f 100644
    > --- a/security/fbfam/fbfam.c
    > +++ b/security/fbfam/fbfam.c
    > @@ -4,8 +4,11 @@
    > #include <linux/errno.h>
    > #include <linux/gfp.h>
    > #include <linux/jiffies.h>
    > +#include <linux/pid.h>
    > #include <linux/printk.h>
    > +#include <linux/rcupdate.h>
    > #include <linux/refcount.h>
    > +#include <linux/sched/signal.h>
    > #include <linux/signal.h>
    > #include <linux/slab.h>
    >
    > @@ -24,7 +27,8 @@ unsigned long sysctl_crashing_rate_threshold = 30000;
    > * struct fbfam_stats - Fork brute force attack mitigation statistics.
    > * @refc: Reference counter.
    > * @faults: Number of crashes since jiffies.
    > - * @jiffies: First fork or execve timestamp.
    > + * @jiffies: First fork or execve timestamp. If zero, the attack detection is
    > + * disabled.
    > *
    > * The purpose of this structure is to manage all the necessary information to
    > * compute the crashing rate of an application. So, it holds a first fork or
    > @@ -175,13 +179,69 @@ int fbfam_exit(void)
    > }
    >
    > /**
    > - * fbfam_handle_attack() - Fork brute force attack detection.
    > + * fbfam_kill_tasks() - Kill the offending tasks
    > + *
    > + * When a fork brute force attack is detected it is necessary to kill all the
    > + * offending tasks. Since this function is called from fbfam_handle_attack(),
    > + * and so, every time a core dump is triggered, only is needed to kill the
    > + * others tasks that share the same statistical data, not the current one as
    > + * this is in the path to be killed.
    > + *
    > + * When the SIGKILL signal is sent to the offending tasks, this function will be
    > + * called again during the core dump due to the shared statistical data shows a
    > + * quickly crashing rate. So, to avoid kill again the same tasks due to a
    > + * recursive call of this function, it is necessary to disable the attack
    > + * detection setting the jiffies to zero.
    > + *
    > + * To improve the for_each_process loop it is possible to end it when all the
    > + * tasks that shared the same statistics are found.
    > + *
    > + * Return: -EFAULT if the current task doesn't have statistical data. Zero
    > + * otherwise.
    > + */
    > +static int fbfam_kill_tasks(void)
    > +{
    > + struct fbfam_stats *stats = current->fbfam_stats;
    > + struct task_struct *p;
    > + unsigned int to_kill, killed = 0;
    > +
    > + if (!stats)
    > + return -EFAULT;
    > +
    > + to_kill = refcount_read(&stats->refc) - 1;
    > + if (!to_kill)
    > + return 0;
    > +
    > + /* Disable the attack detection */
    > + stats->jiffies = 0;
    > + rcu_read_lock();
    > +
    > + for_each_process(p) {
    > + if (p == current || p->fbfam_stats != stats)
    > + continue;
    > +
    > + do_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_PID);
    > + pr_warn("fbfam: Offending process with PID %d killed\n",
    > + p->pid);

    I'd make this ratelimited (along with Jann's suggestions). Also, instead
    of the explicit "fbfam:" prefix, use the regular prefixing method:

    #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt


    > +
    > + killed += 1;
    > + if (killed >= to_kill)
    > + break;
    > + }
    > +
    > + rcu_read_unlock();

    Can't newly created processes escape this RCU read lock? I think this
    need alternate locking, or something in the task_alloc hook that will
    block any new process from being created within the stats group.

    > + return 0;
    > +}
    > +
    > +/**
    > + * fbfam_handle_attack() - Fork brute force attack detection and mitigation.
    > * @signal: Signal number that causes the core dump.
    > *
    > * The crashing rate of an application is computed in milliseconds per fault in
    > * each crash. So, if this rate goes under a certain threshold there is a clear
    > * signal that the application is crashing quickly. At this moment, a fork brute
    > - * force attack is happening.
    > + * force attack is happening. Under this scenario it is necessary to kill all
    > + * the offending tasks in order to mitigate the attack.
    > *
    > * Return: -EFAULT if the current task doesn't have statistical data. Zero
    > * otherwise.
    > @@ -195,6 +255,10 @@ int fbfam_handle_attack(int signal)
    > if (!stats)
    > return -EFAULT;
    >
    > + /* The attack detection is disabled */
    > + if (!stats->jiffies)
    > + return 0;
    > +
    > if (!(signal == SIGILL || signal == SIGBUS || signal == SIGKILL ||
    > signal == SIGSEGV || signal == SIGSYS))
    > return 0;
    > @@ -205,9 +269,11 @@ int fbfam_handle_attack(int signal)
    > delta_time = jiffies64_to_msecs(delta_jiffies);
    > crashing_rate = delta_time / (u64)stats->faults;
    >
    > - if (crashing_rate < (u64)sysctl_crashing_rate_threshold)
    > - pr_warn("fbfam: Fork brute force attack detected\n");
    > + if (crashing_rate >= (u64)sysctl_crashing_rate_threshold)
    > + return 0;
    >
    > + pr_warn("fbfam: Fork brute force attack detected\n");
    > + fbfam_kill_tasks();
    > return 0;
    > }
    >
    > --
    > 2.25.1
    >

    --
    Kees Cook

    \
     
     \ /
      Last update: 2020-09-11 01:57    [W:4.126 / U:0.032 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site