本文主要参考: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;