xorl %eax, %eax

CVE-2009-1338: Linux kernel Signals Security Bypass

leave a comment »

This bug was initially reported on 17 July 2008 by Daniel Hokka Zakrisson and finally fixed in 2.6.28 release of the Linux kernel. As Sukadev Bhattiprolu stated, the design flaw was that users could issue a command similar to:

kill <sig> -1

In order to send signal <sig> to all processes in the system, in all namespaces! As we can read from kill(1) man page:

-1
    All processes with pid larger than 1 will be signaled.

However, this implies that it will signal all the processes in the current namespace. Not the entire system. Here is the vulnerable routine from 2.6.27 release of the Linux kernel:

1117 /*
1118  * kill_something_info() interprets pid in interesting ways just like kill(2).
1119  *
1120  * POSIX specifies that kill(-1,sig) is unspecified, but what we have
1121  * is probably wrong.  Should make it like BSD or SYSV.
1122  */
1123
1124 static int kill_something_info(int sig, struct siginfo *info, pid_t pid)
1125 {
       ...
1128        if (pid > 0) {
       ...
1139        } else {
1140                int retval = 0, count = 0;
1141                struct task_struct * p;
1142
1143                for_each_process(p) {
1144                        if (p->pid > 1 && !same_thread_group(p, current)) {
1145                                int err = group_send_sig_info(sig, info, p);
1146                                ++count;
1147                                if (err != -EPERM)
1148                                        retval = err;
1149                        }
1150                }
1151                ret = count ? retval : -ESRCH;
1152        }
       ...
1156 }

This function is part of kernel/signal.c and as you can see it uses a task_struct pointer (line 1141) to iterate through each process (line 1143) as long as that process is neither init process (PID=1) and it belongs to the same thread group (line 1144). If this is the case, it sends the signal using group_send_sig_info() (line 1145) and increments the counter (line 1146). Even this seems to be correct, it does not limit the user from sending signals to different namespaces. To fix this behavior the following patch was committed:

                for_each_process(p) {
-                       if (p->pid > 1 && !same_thread_group(p, current)) {
+                       if (task_pid_vnr(p) > 1 &&
+                                       !same_thread_group(p, current)) {
                                int err = group_send_sig_info(sig, info, p);

Now, the check for the init process:

if (p->pid > 1)

Was changed to:

if (task_pid_vnr(p) > 1)

This is a macro from include/linux/sched.h which is defined like this:

1390 static inline pid_t task_pid_vnr(struct task_struct *tsk)
1391 {
1392        return pid_vnr(task_pid(tsk));
1393 }

Where, pid_vnr() is part of kernel/pid.c source code file and returns this value:

447 pid_t pid_vnr(struct pid *pid)
448 {
449         return pid_nr_ns(pid, current->nsproxy->pid_ns);
450 }
451 EXPORT_SYMBOL_GPL(pid_vnr);

As you can see it uses pid_nr_ns() apart from returning the PID of the requested process, this checks whether it belongs to a different namespace or not as you can read from kernel/pid.c:

434 pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns)
435 {
436        struct upid *upid;
437        pid_t nr = 0;
438
439        if (pid && ns->level <= pid->level) {
440                upid = &pid->numbers[ns->level];
441                if (upid->ns == ns)
442                        nr = upid->nr;
443        }
444        return nr;
445 }

This issue affects Linux kernel from the introduction of namespaces in PIDs which was on 2.6.24 kernel up to 2.6.28 where this issue was fixed.

Written by xorl

May 18, 2009 at 13:37

Posted in bugs, linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s