导语:我们会在不同产品中识别漏洞,这也是我们工作的一部分,因此,我们会不定时地检查Linux内核,目的主要是在不同的驱动程序中寻找漏洞。在本例中,我们检查了试图擅自利用mmap()函数的驱动程序。
我们会在不同产品中识别漏洞,这也是我们工作的一部分,因此,我们会不定时地检查Linux内核,目的主要是在不同的驱动程序中寻找漏洞。
在本例中,我们检查了试图擅自利用mmap()函数的驱动程序。
由于在组织中,很少有QA人员会检查其代码,并将安全问题作为其编写的程序一部分。因此,重新实现内核功能的想法很可能会导致错误。
在这种情况下,我们发现并揭示了几个问题,我们发现的具体的bug实际上是一个存在了8年的驱动程序漏洞,可以利用其在最新的内核版本(4.16-rc3)中升级特权。
下面是关于这些漏洞的一个例子。
MMAP处理程序
对于驱动程序来说,实现其自身版本的文件操作功能并不少见;这可以在一个驱动的file_operations结构中进行标识:
scsi\sg.c 模块中的 file_operations结构
在搜索了关于这些类型的文件操作的更多信息之后,我们发现了MWR实验室给出的非常有用的指南:内核驱动程序mmap处理程序的使用。虽然指南详细解释了应该如何使用mmap()处理程序,但也强调了这些驱动程序的常见漏洞,包括缺乏输入验证和错误溢出(Integer-Overflows)。有了这些新信息,我们决定在整个内核中搜索可能存在的易受攻击的mmap()处理程序。
在开始搜索之前,我们需要对将要寻找的漏洞进行定义。一个典型的驱动程序应该具备下面两个特点:
该驱动程序要有一个内部缓冲区,该缓冲区代表与外围设备共享内存区域。
该驱动程序应该只允许用户访问该缓冲区内的内存范围。
现在,我们再检查一遍用户空间里mmap()函数的原型(从man中获取的):
正如我们所看到的,有大量的字段攻击者可以控制,因此开发人员应该执行以下检查,并且同时尽量避免可能的错误溢出,以避开这种陷阱:
1.Region start: 0 <= offset < buffer’s end 2.Region end: buffer’s start <= offset + length <= buffer’s end 3.Region start <= Region End
实际上,由于Linux内核将会对所提供的长度进行查杀,因此最后的检查可以省略。这使得当正在通过第三次检查时,几乎不可能通过前两个检查。
既然我们已经知道如何发现可能存在的漏洞,我们还需要知道在哪里可以找到这些漏洞。该指南指出,remap_pfn_range()是一个重要的函数,因为该函数将把物理内存页映射到用户。
用最简单的方法,对“remap_pfn_range(”进行GREP,来发现一些东西。在试图理清最初的158个结果时,我们可以很好地了解一个普通驱动程序所做的检查,从而更容易发现可能的易受攻击的潜在对象。在将潜在的易受攻击对象名单缩小到6个之后,进一步的调查显示了最有可能的攻击对象:
drivers\gpu\drm\udl\udl_fb.c – udl_fb_mmap()
DisplayLink驱动程序——CVE 2018-8781
内核中的video/drm模块定义了一个默认的mmap()包装器,它调用由特定驱动程序定义的real mmap()处理程序。在我们的例子中,漏洞在内部mmap()里,mmap()是在fb_helper文件操作中被定义的,该文件操作属于“DisplayLink”的“udl”驱动程序。
说明:这是一个典型的错误溢出的例子。因为偏移量是无符号的,程序员跳过了check #1,直接check #2。然而,计算“偏移量+大小”可能会处理成低值,从而能够使我们能够在仍然使用非法的“偏移”值的同时绕过检查。
注意:在64位机器上,只有48比特的可访问内存,这意味着如果我们使用一个比较大的“偏移量”来绕过该检查,我们还必须确保“info->fix.smem_start + offset”将环绕式处理到一个有效的可映射的物理地址。
证明
为了验证该漏洞,我们使用了一个Ubuntu 64位虚拟机,并上传了一个模拟的易受攻击的驱动程序。在每个测试中,驱动程序的mmap()处理程序包含了我们想要检查的实现。用户模式代码在易受攻击的驱动程序上对mmap()执行了两个连续调用:
1.length = 0x1000, offset = 0x0 -> sanity check 2.length = 0x1000, offset = 0xFFFFFFFFFFFFFFFF – 0x1000 + 1 -> vulnerability check
当在内核/dev/随机数生成器实现的页面对齐物理地址中设置缓冲区的地址时,输出(在这两种情况下)都是预期的结果:
1.The correct physical page: 0x1531000 2.The previous physical page: 0x1530000
其他检查表明,用户可以从映射的页面中读取和写入,从而给攻击者提供了一个强大的原语,可以用该原语在内核空间中触发代码执行。
信息披露时间:
2018年3月18日——漏洞被披露给Linux内核。
2018年3月18日——Linux发行了一个补丁,并要求我们验证它。
2018年3月18日——我们证实了这一补丁,并发出了“绿灯”继续的信号。
2018年3月21日——为CVE 2018-8781发布了一个官方的Linux补丁。
2018年3月21日——补丁被整合到Linux内核中。
总结
该漏洞允许本地用户访问易受攻击的特权驱动程序,读取并写入敏感的内核内存,从而导致本地特权升级。虽然该漏洞是通过一个简单的搜索找到的,但八年前该漏洞就已经被引入到内核中。通过该事件我们知道,即使是在一个热门的开源项目中,如Linux内核,如果知道在哪里搜索,我们总能找到漏洞。
参考
1.MWR实验室 Mmap 使用指南: https://labs.mwrinfosecurity.com/assets/BlogFiles/mwri-mmap-exploitation-whitepaper-2017-09-18.pdf
2.CVE 2018-8781 patch: https://patchwork.freedesktop.org/patch/211845/
3.NVD – https://nvd.nist.gov/vuln/detail/CVE-2018-8781