CVE-2019-11815:关于CVSS评分的思考

阅读量    63291 |   稿费 200

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

0x00 前言

Linux内核中的漏洞并不罕见。现在Linux已经大约有2600万行代码,仅在2018年就添加了3,385,121行代码,移除了2,512,040行代码,。在这种量级的代码复杂度下很难不存在漏洞。然而有些漏洞还是比较少见,比如未授权远程代码执行(RCE)漏洞:这是每个系统管理员都不想看到的关键问题。

2019年5月8日,美国国家漏洞数据库(NVD)公布了Linux内核漏洞(CVE-2019-11815)的详细细节,通用漏洞评分系统(CVSS)3.0的基本评分为8.1。漏洞细节包括:可以通过“网络”进行攻击、无需特权、能获得管理员级别的代码执行,该漏洞在机密性、完整性和可用性(CIA)方面影响程度都“较高”。如果只看该评分,这似乎是非常严重的问题。然而在评估漏洞影响范围方面,我们不应当只参考CVSS基础评分所给出攻击方式、权限级别以及CIA标准。

CVSS 3基础评分的一个因素就是攻击复杂度,该漏洞在这方面也获得了“高”评级。这意味着如果想成功利用漏洞,攻击者需要依赖一些难以满足的特定条件。根据CVSS 3.0标准,该评级意味着“漏洞成功利用依赖于攻击者无法控制的一些条件”以及“漏洞无法随意利用,需要攻击者针对漏洞组件在前期准备和执行方面投入较多精力,才可能成功攻击”。

详细查看漏洞细节后,我们就可以知道为什么这个评分在技术层面上是正确的(特别是要考虑到攻击复杂度方面因素),但并不能完全代表企业和用户所面临的实际风险。

 

0x01 漏洞分析

根据NVD的描述,该漏洞存在于“内核版本低于5.0.8之前的Linux发行版,具体位于net/rds/tcp.crds_tcp_kill_sock函数中”,并且存在“竞争条件,可以导致uaf(释放后使用)问题,与net命名空间清理有关”。从代码角度而言,这个漏洞描述非常准确且简洁,但由于提到TCP这个词,并且缺乏关键信息,这可能容易因此不必要的警报。

该漏洞涉及到的第一个组件是RDS(Reliable Datagram Sockets),这是由Oracle开发的一个套接字接口和协议,方便单个套接字向大量不同的端点发送和接受数据。当TCP作为底层传输协议时,该漏洞就涉及到RDS:RDS头中的应用数据被封装后通过TCP传输,通常会发送到16385端口,然后解封装并传输至RDS套接字。

除了Oracle的官方文档和维基百科上非常简短的一个页面之外,我们找不到关于RDS和典型用法的太多资料。由于该协议存在模糊性,并且之前还存在本地提权漏洞,因此多年来大多数Linux发行版(如Ubuntu)已经将与RDS有关的内核模块列入黑名单中,这样也迅速将这类漏洞的潜在风险控制在一定范围内。

那么当rdsrds_tcp内核模块处于启用状态会出现什么情况呢?

当在TCP协议上使用RDS时,底层TCP传输完全由内核来控制。这意味着当客户端建立一个新的RDS套接字时,内核就会在tcp_connect.c中通过rds_tcp_conn_path_connect()打开TCP套接字,而该函数由threads.c中的rds_connect_worker()线程函数负责调用。

图1. threads.c中的rds_connect_worker()调用rds_tcp_conn_path_connect()

当客户端的底层TCP套接字持续无法连接时,就会出现与RDS有关的问题。当TCP的connect()失败时,系统就会调用rds_tcp_restore_callbacks()函数,将rds_tcp_connection结构中的t_sock指针设置为NULL,这是非常合理的行为。

图2. rds_tcp_conn_path_connect()调用rds_tcp_restore_callbacks()

图3. 在rds_tcp_restore_callbacks()中将t_sock设置为NULL

当引入第二个漏洞组件时就会出现问题:网络命名空间(network namespaces)。网络命名空间可以为指定的命名空间使用独立的一组接口和路由表,其中传统意义上整个操作系统与其他每个进程共享相同的接口和路由表。Docker等平台会使用这个命名空间功能为容器提供网络隔离机制。

rds_tcp_init()初始化RDS-TCP套接字时,就会调用网络命名空间函数register_pernet_device(),传入指向pernet_operations结构体的一个指针(rds_tcp_net_ops),其中包含待执行的初始化和退出函数,当初始化或删除网络命名空间以及激活套接字时就会调用这些函数。

图4. 调用register_pernet_device()注册网络命名空间设备

图5. rds_tcp_exit_net()为网络命名空间设备的退出函数

退出函数rds_tcp_exit_net()会调用rds_tcp_kill_sock(),该函数用来清理RDS-TCP套接字中涉及的各个部分。其中有个清理过程会创建待清理的连接列表:tmp_list

内核会检查每个连接,判断在用的TCP套接字对应的t_sock指针是否为NULL,如果满足条件,就不会将t_tcp_node添加到“清理列表”中。这样处理的结果就是,内核不会针对这些节点调用rds_conn_destroy(),也不会执行许多“清理”操作。

图6. 如果t_sockNULLrds_tcp_kill_sock()不会执行清理操作

最重要的是,rds_connect_worker()线程不会停止,会继续尝试重新连接。最终,作为清理命名空间的一个环节,底层网络结构会被释放,而正在运行的rds_connect_worker()可能还会用到该结构,这样就会触发释放后重用(use-after-free)问题。从技术角度上来讲,这个缺陷可以描述为:无需特殊权限,如果该漏洞成功被利用就可以实现管理员级别的代码执行。

这个问题修复起来也非常简单:系统管理员只需要确保存在漏洞的模块已被禁用,或者已安装了新版内核。

 

0x02 风险评估

分析完CVE-2019-11815后,该漏洞对用户而言意味着什么?潜在的受害者首先需要加载通常处于黑名单中的rdsrds_tcp模块,如果这些模块没被加载,那就不需要后续操作。如果攻击者终于找到了极其罕见的目标,由于TCP的connect()操作只由RDS-TCP客户端发起,跟服务端无关,因此攻击者需要诱导目标从网络命名空间中连接到攻击者可控的RDS-TCP套接字。

攻击者下一个任务就是触发底层TCP连接失败,与此同时触发目标用户的网络命名空间被系统清理:这是远程攻击者几乎无法完成的任务。除此之外,竞争条件(利用非预期时间点触发的缺陷来影响其他操作)通常也非常难以利用,需要大量尝试才有可能成功。

将这些条件考虑在内后,我们可以认为该漏洞“未经身份验证被远程利用”的概率近乎为零。该漏洞被用来提升本地权限的概率也非常低,需要系统加载通常处于黑名单中的rdsrds_tcp模块。

虽然从技术角度上来看这个漏洞CVSS评分没有问题,但用户应该了解到该风险同样依赖于攻击成功的可能性,因为攻击者能否成功实际上受利用复杂度和条件所限。在实际环境中这个漏洞很难利用成功,绝大多数Linux服务器在远程环境中根本不受影响。

分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多