一、简介
dirtyCOW(编号CVE-2016-5195)是一个常用于Linux本地提权的漏洞,可以修改操作系统中的任意文件,包括系统存储的账户信息文件,影响的Linux内核版本在2.6.22 到 4.9.x之间。
二、基本原理
dirtyCOW的触发基于race condition,通过内核错误处理机制的逻辑缺陷实施攻击。攻击过程需要父子进程之间的合作。当子进程向一个只读文件发出写请求时,发起write()系统调用,write()中调用get_user_pages获取进程内存中请求的文件页面,某个进程如果需要修改硬盘中的文件,需要把该文件映射到内存中,再通过get_user_pages获得页面后,才可以进行修改。
第一次调用时内存中并没有进程指定的文件,所以会发生缺页处理,在处理缺页的过程中内核得知发起该请求的是一个子进程,所以使用COW(copy on write写时复制)处理,把该文件复制到父子进程的内存区域,然后单独复制一个副本给子进程,包括该文件的权限。
第一次缺页处理完成后,返回到__get_user_page函数,第二次尝试获得该文件对应的页面,检查到该文件对于进程而言原本只有写权限,所以又一次发生缺页处理,此次缺页处理经过逻辑判断,会认为这次要处理的是一个发生了COW的页面权限问题,所以将继续使用已经复制到内存的页面,并删除某些关于权限的重要标志,为该进程设置对该页面的写权限,接着再返回到__get_user_pages函数继续获取页面。
这样做本身没什么问题,因为子进程获得的内存中的文件页面只是一个COW副本,对其进行修改并不会保存到原始文件中。但是,如果此时父进程发起madvice()调用,向内核建议置空该页面,也就是把该页面到文件的映射取消,只留下页表项(包含了子进程的权限信息,此时已经被设置为可写),在进行页面检查前把该页面置空,那么在__get_user_pages函数继续执行时,会发生第三次缺页处理。
本次缺页处理会因为之前的一些重要标志的删除,把这次请求视作一个不需要写权限的缺页处理,调用do_read_fault函数,该函数会把原始文件直接映射到该页表项对应的内存区域中。因为内核认为这次缺失的页面进程只需要对其进行读操作,为了节省内存,所以把该文件直接映射,这样做本身没有什么问题,但是此时的页表项是之前对于COW副本的页表项,仍然具有写权限,导致子进程可以对该页表项对应的原始文件进行写操作,修改后的数据会被保存回硬盘中,最终导致了越权写操作。
如果该文件是/etc/passwd,保存了系统上所有用户的权限设置信息,对其进行越权写,就可以使得攻击者获得root权限。
三、控制流分析
以下是整个控制流代码的简化版(分为四次执行过程):
//接口函数,负责把参数传递给处理函数 get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, unsigned long nr_pages, int write, int force, struct page **pages, struct vm_area_struct **vmas) { return __get_user_pages_locked(tsk, mm, start, nr_pages, write, force, pages, vmas, NULL, false, FOLL_TOUCH); }; 第一次:调用_
已在FreeBuf发表 0 篇文章
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022