sockaddr->sa_len的痛

admin 2020年8月3日15:04:19评论534 views字数 2880阅读9分36秒阅读模式
sockaddr->sa_len的痛

0x00引言

sockaddrxnu内核中一个很普通的数据结构,用于描述socket地址的基本属性,包括地址长度及其所属family类型。结构体具体定义如下:

sockaddr->sa_len的痛

由于xnu支持多种socket类型,不同类型的socket使用的sockaddr长度可能不同,xnu中为每种sockaddr都有具体定义。例如,下面分别是sockaddr_in、sockaddr_in6、sockaddr_un、sockaddr_ctl 的结构。

sockaddr->sa_len的痛

每种sockaddr_*的头部结构都是sockaddr,其中第一个字节即sa_len表示该结构的长度,第二个字节sa_family表示地址类型。内核使用struct sockaddr*指针类型时,需要根据sa_family将其转换成struct sockaddr_in6*、struct sockaddr_in*等具体类型。可以看到,当内核处理由用户态提交的sockaddr数据时,如果对sa_family或者sa_len检查不严格时,就可能导致安全漏洞。尤其是sa_len,描述了数据长度,如果检查不当,就可能引起内存越界访问等问题。

sockaddr->sa_len的痛

0x01漏洞介绍

近些年xnu中陆续披露了一些与sockaddr相关的安全漏洞,其中最为著名的,是Google Project 0团队Ian Beer在mptcp模块中发现的一个漏洞。这里,我们先详细介绍一下这个漏洞,理解这个漏洞的成因对挖掘新漏洞很有帮助。


漏洞回顾

Ian Beer发现的mptcp漏洞位于mptcp_usr_connectx函数中。mptcp_usr_connectx在处理用户态传入的sockaddr数据时,认为其类型只可能是AF_INET或者AF_INET6,mptcp_usr_connectx严格检查了sockaddr是这两种类型时的sa_len字段。然而,这里的逻辑缺陷是,一旦传入不是AF_INET或者AF_INET6类型的sockaddr,sa_len字段就没有检查。mptcp_usr_connectx使用sa_len字段调用memcpy的时候发生堆溢出。更详细的漏洞分析见链接:Issue 1558: XNU kernel heap overflow due to bad bounds checking in MPTCP

sockaddr->sa_len的痛

Ian Beer对这个漏洞的利用技巧也非常精彩。我们暂不关心漏洞的利用过程,再分析一下这个漏洞特征。可以看到在这个漏洞代码里,开发者虽然有意识的检查了sockaddr数据,但只检查了特定类型和相应长度的匹配关系;这导致如果传入的sockaddr数据是别的类型,其sa_len字段并没有有效检查。


sockaddr->sa_len的痛  漏洞1 ==> CVE-2020-XXXX

看过Ian Beer这个漏洞后,我们开始思考,xnu中是否还存在类似的问题:对传入的sockaddr仅做了部分类型和长度匹配检查,对其它类型的sockaddr未作检查而继续使用?

带着这个问题,我们继续审计xnu代码。很快我们就在ioctl的处理函数(in_control函数)里发现了一个新的信息泄漏漏洞。

该漏洞原因是inctl_ifdstaddr函数在处理SIOCSIFDSTADDR命令时,只处理了family为AF_INET时的sin_len,因此当family为其他值(比如AF_INET6)的时候,sin_len未被检查,可以为任意值。

如下所示,ifr指向用户可控的数据,当inctl_ifdstaddr函数在处理SIOCSIFDSTADDR命令时,先将用户可控的结构体ifr全部拷贝到ia里,然后在a处,处理family为AF_INET的情况:将ia->ia_dstaddr.sin_len设置为sockaddr_in的结构体大小。

但是,当family为其他值,比如为AF_INET6时,inctl_ifdstaddr函数没有做任何处理,所以ia->ia_dstaddr.sin_len就仍是从ifr里面拷贝过来的用户控制的length,范围为0~0xff。

sockaddr->sa_len的痛

到这里,我们可以在ia->ia_dstaddr填入一个非AF_INET类型的sockaddr并任意设定sin_len。接下来的问题是,这个ia->ia_dstaddr在哪里会被使用?

我们继续审计代码,在sysctl_iflist函数中找到了对ia->ia_dstaddr的使用。下面代码中,ifa->ifa_dstaddr就是inctl_ifdstaddr里设置的ia->ia_dstaddr。在b处这个sockaddr被存入到rti_info里,然后传入到rt_msg2函数中。

sockaddr->sa_len的痛

我们来看rt_msg2的实现。rt_msg2就循环遍历rtinfo数组,当遍历到RTAX_BRD时,sa就是ifa->ifa_dstaddr,那么如e处所示,dlen就是之前用户可控的length,最大可达到0xff。rt_msg2调用bcopy函数做内存复制时,发生内存越界读,最大可拷贝出255字节的数据,这些泄漏出来的数据里可能包含函数指针,导致内存泄漏。

sockaddr->sa_len的痛

我们的POC运行结果如下,越界读取函数指针后,即可计算kernel slide。

sockaddr->sa_len的痛


sockaddr->sa_len的痛  漏洞2 ==> CVE-2020-9811

上面的信息泄漏不是孤例。很明显,开发者犯了mptcp里同样的错误。我们再把漏洞特征放宽一些,看看其它xnu模块中对sa_len字段的检查。

很快,在flow_divert_is_sockaddr_valid函数中,我们看到了下面的代码。

sockaddr->sa_len的痛

通过函数名字,不难推测flow_divert_is_sockaddr_valid就是用来验证sockaddr是否合法的。flow_divert_is_sockaddr_valid明确限定了sockaddr只能是AF_INET或者AF_INET6。然而,在长度检查中,flow_divert_is_sockaddr_valid犯了一个低级错误: flow_divert_is_sockaddr_valid函数只检查了addr->sa_len不要小于结构体的实际大小,但是却没考虑到addr->sa_len可能大于结构体实际大小的情况。

因此,只要传入的sockaddr类型是AF_INET或者AF_INET6,攻击者就可以设置过长的sa_len,导致flow_divert后继使用sockaddr的时候发生内存越界访问。感兴趣的朋友可以尝试一下自行构造POC代码。

sockaddr->sa_len的痛

0x02总结

这篇文章里,我们分享了我们如何在IanBeer公布mptcp漏洞后,分析漏洞成因、总结漏洞特征、到根据漏洞特征挖掘新漏洞的过程。漏洞挖掘很考验研究者"举一反三"的能力。在大量代码中针对性的快速定位疑似漏洞代码会大大提高漏洞挖掘的效率。而从历史漏洞中总结分析,对定位疑似漏洞代码大有裨益。此外,sockaddr一个如此简单的数据结构,但在大量的类型转换过程中,一旦类型和长度检查逻辑不完备,就可能导致更严重的安全问题。在我们分享的这两个漏洞之外,我相信也能找到其它相似问题。


最后,感谢您的阅读。:)


sockaddr->sa_len的痛sockaddr->sa_len的痛

扫描二维码

关注盘古实验室



  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2020年8月3日15:04:19
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   sockaddr->sa_len的痛http://cn-sec.com/archives/80702.html

发表评论

匿名网友 填写信息