本文主要参考:https://paper.seebug.org/828/

漏洞解析

CVE-2018-12232

要想了解CVE-2019-8912,首先我们必须要回到去年的一个commit:6d8c50dcb029872b298eea68cc6209c866fd3e14,该commit主要目的是为了修复sock_close()与sockfs_setattr()之间的条件竞争漏洞,具体原因fcownat()没有保持fd的引用计数,fcownat()函数的功能是改变文件的owner或者group,在进入内核后会调用sockfs_setattr()去设置uid属性
如果我们在它调用之前调用sock_close()函数将sock->sk释放并置空,那么就会产生NULL pointer引用漏洞,为了解决这个问题,内核开发者
作出了如下修复:

__sock_release()拥有一个inode参数,当其不为空时,使用inode_lock()上锁,再调用sock->ops->release()释放sock,->release()只是一个函数指针,取决于socket的具体类型,释放完成后inode_unlock()解锁

sockfs_setattr()增加了对sock->sk是否为空的检查

简单验证poc:

#include <pthread.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>

pthread_t t;
volatile int fd;

void *close_thread(void *arg)
{
    for (;;) {
        usleep(rand() % 100);
        close(fd);
    }
}

int main()
{
    pthread_create(&t, NULL, close_thread, NULL);
    for (;;) {
            fd = socket(rand() % 50, rand() % 11, 0);
        fchownat(fd, "", 1000, 1000, 0x1000);
        close(fd);
    }
}

CVE-2019-8912

Linux Crypto 模块

根据kernel文档的描述,Crypto模块在2.5.45版本引入内核,并在2.6.38版本添加了用户层接口,该API通过AF_ALG类型的socket供调用.

漏洞解析

CVE-2018-12232看起来很完美的修复,为什么还会有CVE-2019-8912,原因是这个修复有效的前提是->release()函数会将sock->sk置空,不幸的是,crypto模块的af_alg_release()并没有将sk设为NULL,从而可以绕过sockfs_setattr()对sock->sk的检查

In the Linux kernel af_alg_release() in crypto/af_alg.c neglects to set a NULL value for a certain structure member, which leads to a use-after-free (UAF) in sockfs_setattr.
A local attacker can use this flaw to escalate privileges and take control of the system. Other vendors have considered this a 'network' accessible attack, this claim is unsubstantiated at this time.

KASAN has found use-after-free in sockfs_setattr.
The existed commit 6d8c50dcb029 ("socket: close race condition between sock_close()
and sockfs_setattr()") is to fix this simillar issue, but it seems to ignore
that crypto module forgets to set the sk to NULL after af_alg_release.

修复方式也很简单,commit:9060cb719e61b685ec0102574e10337fa5f445ea,补丁在af_alg_release()添加了一行代码:sock->sk = NULL;