前面一段时间,这个编号为CVE-2016-5195的漏洞刷爆了各个安全相关的博客和网站,这个漏洞可以对任意可读文件进行写操作从而导致提权,通杀了包括Android在内的绝大多数linux版本,,影响不可为不大,试着分析一下。

一:漏洞分析

  这个漏洞逻辑并不复杂,分析的最大难点是流程复杂,容易绕晕在代码迷宫里,所以先梳理一下流程,流程如下(初略扫过即可),根据下面分析来查看上面流程):

//进行写操作
mem_write
    mem_rw  

  1.      access_remote_vm
  2. __access_remote_vm
  3. //用于获取页
  4. get_user_pages
  5. __get_user_pages
  6. retry:
  7. follow_page_mask(...,flag,...);
  8. //通过内存地址来找到内存页
  9. follow_page_pte(...,flag,...);
  10. //如果获取页表项时要求页表项所指向的内存映射具有写权限,但是页表项所指向的内存并没有写权限。则会返回空
  11. if ((flags & FOLL_WRITE) && !pte_write(pte))
  12. return NULL
  13. ////获取页表项的请求不要求内存映射具有写权限的话会返回页表项
  14. return page
  15. if (foll_flags & FOLL_WRITE)//要求页表项要具有写权限,所以FOLL_WRITE为1
  16. fault_flags |= FAULT_FLAG_WRITE;
  17. //获取页表项
  18. if (!page) {
  19. faultin_page(vma,...); //获取失败时会调用这个函数
  20. handle_mm_fault();
  21. __handle_mm_fault()
  22. handle_pte_fault()
  23. if (!fe->pte)
  24. do_fault(fe);
  25. ////如果不要求目标内存具有写权限时导致缺页,内核不会执行COW操作产生副本,ers
  26. if (!(fe->flags & FAULT_FLAG_WRITE))
  27. do_read_fault(fe, pgoff);
  28. __do_fault(fe, pgoff, NULL, &fault_page, NULL);
  29. ret |= alloc_set_pte(fe, NULL, fault_page)
  30. //如果执行了COW,设置页表时会将页面标记为脏,但是不会标记为可写。
  31. if (fe->flags & FAULT_FLAG_WRITE)
  32. entry = maybe_mkwrite(pte_mkdirty(entry), vma);
  33. //如果要求目标内存具有写权限时导致缺页,目标内存映射是一个VM_PRIVATE的映射,内核会执行COW操作产生副本
  34. if (!(vma->vm_flags & VM_SHARED))
  35. do_cow_fault(fe, pgoff);
  36. new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, fe->address);
  37. ret = __do_fault(fe, pgoff, new_page, &fault_page, &fault_entry);
  38. copy_user_highpage(new_page, fault_page, fe->address, vma);
  39. ret |= alloc_set_pte(fe, memcg, new_page);
  40. if (fe->flags & FAULT_FLAG_WRITE)
  41. if (!pte_write(entry))
  42. do_wp_page(fe, entry)//VM_FAULT_WRITE置1
  43. if ((flags & FAULT_FLAG_WRITE) && reuse_swap_page(page))
  44. maybe_mkwrite(pte_mkdirty(entry), vma);
  45. if (likely(vma->vm_flags & VM_WRITE))
  46. pte_mkwrite(pte);
  47. flags &= ~FAULT_FLAG_WRITE;
  48. ret |= VM_FAULT_WRITE;
  49. if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
  50. *flags &= ~FOLL_WRITE;
  51. ,== goto retry        
            if (write)
              copy_to_user_page

  进行逐步分析,并在分析中参考上文代码流程。

  漏洞发生在调用write函数时,经过一系列调用(write->mem_write->mem_rw->access_remote_vm->__access_remote_vm),通过在__access_remote_vm的get_user_pages来获得页,copy_to_user_page来讲内容复制进页中。而漏洞就发生在get_user_pages中。下面来分析get_user_pages的具体流程。

  get_user_pages中主要部分是一个循环,直到正确找到页,其中有两个函数极为重要,follow_page_mask和faultin_page,其中follow_page来找到页,dofault_page在寻页失败的时候建立映射为下次调用follow_page_mask来做准备。

  第一次执行,由于mmap对第一次对映射内存进行操作,所以并不能直接从页表中找到。get_user_page,因为我们要求页表项要具有写权限,所以FOLL_WRITE为1,设置FAULT_FLAG_WRITE然后调用了faultin_page,之后依次调用了handle_mm_fault->__handle_mm_fault->handle_pte_fault->do_fault->do_cow_fault,此时利用COW来生成了页表,建立了映射。

  第二次执行,follow_page_mask会通过flag参数的FOLL_WRITE位是否为1判断要是否需要该页具有写权限,以及通过页表项的VM_WRITE位是否为1来判断该页是否可写。由于Mappedmem是以PROT_READ和MAP_PRIVATE的的形式进行映射的。所以VM_WRITE为0,而FOLL_WRITE为1,返回null,进而调用faultin_page函数,此时由于已经找到了页表,不再调用_do_fault,而是调用了do_wp_page,在do_wp_page中,将FAULT_FLAG_WRITE置0,同时,将ret的VM_FAULT_WRITE置1,表示已经执行过COW,在faultin_page之后的判断中,由于ret中VM_FAULT_WRITE置1,则flag的FOLL_WRITE置0,而FOLL_WRITE置0代表着也页表项不需要写权限。

  第三次执行,此时调用follow_page_mask时可以正确找到页了。由于进行了COW,所以写操作并不会涉及到原始内存。

  但是正如POC,如果madvice发生在get_user_page第二次执行之后,madvice即取消内存的映射关系,那么第三次执行会follow_page_mask函数会失败,进入dofault_page函数,此时的调用流程会和第一次有一定的区别,由于FAULT_FLAG_WRITE置0,所以直接执行do_read_fault。而do_read_fault函数调用了__do_fault,由于标志位的改变,此时直接与文件内存进行映射。

__do_fault部分代码如下:

  1. if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) {
  2. if (unlikely(anon_vma_prepare(vma)))
  3. return VM_FAULT_OOM;
  4. cow_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);
  5. if (!cow_page)
  6. return VM_FAULT_OOM;
  7. if (mem_cgroup_newpage_charge(cow_page, mm, GFP_KERNEL)) {
  8. page_cache_release(cow_page);
  9. return VM_FAULT_OOM;
  10. }
  11. } else
  12. cow_page = NULL;
  13.  
  14. if (flags & FAULT_FLAG_WRITE) {
  1. if (!(vma->vm_flags & VM_SHARED)) {
  2. page = cow_page;
  3. anon = 1;
  4. copy_user_highpage(page, vmf.page, address, vma);
  5. __SetPageUptodate(page);
  6. } else
                ...
    }

    

  在第四次执行的时候,follow_page_mask直接获得文件内存页从而对其进行读写。

二:补丁分析

  这个漏洞是Linux的创始人Linus亲自修复,简略补丁如下:

  1. +static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)
  2. +{
  3. + return pte_write(pte) ||
  4. + ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte));
  5. +}
  6. +
  7. static struct page *follow_page_pte(struct vm_area_struct *vma,
  8. unsigned long address, pmd_t *pmd, unsigned int flags)
  9. {
  10. @@ -, +, @@ retry:
  11. }
  12. if ((flags & FOLL_NUMA) && pte_protnone(pte))
  13. goto no_page;
  14. - if ((flags & FOLL_WRITE) && !pte_write(pte)) {
  15. + if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) {
  16. pte_unmap_unlock(ptep, ptl);
  17. return NULL;
  18. }
  19. @@ -, +, @@ static int faultin_page(struct task_struct *tsk, struct vm_area_struct *vma,
  20. * reCOWed by userspace write).
  21. */
  22. if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
  23. - *flags &= ~FOLL_WRITE;
  24. + *flags |= FOLL_COW;
  25. return ;
  26. }

  Linus在这里新添加了一个FOLL_COW的标志位,来表明已经进行了COW。同时在get_follow_mask判定权限的时候同时利用dirty位来判定FOLL_COW是否有效。

参考文献:

http://bobao.360.cn/learning/detail/3132.html

https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=19be0eaffa3ac7d8eb6784ad9bdbc7d67ed8e619

https://bugzilla.suse.com/show_bug.cgi?id=1004418#c14

深入分析CVE-2016-5195 Dirty Cow的更多相关文章

  1. Linux内核[CVE-2016-5195] (dirty COW)原理分析

    [原创]Linux内核[CVE-2016-5195] (dirty COW)原理分析-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com https://bbs.pediy.com/ ...

  2. dirty cow exp

    公司搞底层的改了一下,说做到了几个不死机 /* * (un)comment correct payload first (x86 or x64)! * * $ gcc cowroot.c -o cow ...

  3. [提权] 脏牛漏洞 Dirty COW CVE-2016-5195 2.6.22 < 3.9 (x86/x64)

    /* * (un)comment correct payload first (x86 or x64)! * * $ gcc cowroot.c -o cowroot -pthread * $ ./c ...

  4. 提权 EXP

    windows: 漏洞列表 #Security Bulletin #KB #Description #Operating System CVE-2017-0213 [Windows COM Eleva ...

  5. 记一次 lampiao渗透(Drupal+脏牛提权)

    vulnhub|渗透测试lampiao 题记 最近在打靶机,发现了一个挺有意思的靶机,这里想跟大家分享一下. 环境准备 vulnhub最近出的一台靶机 靶机(https://www.vulnhub.c ...

  6. Linux内核通杀提权漏洞CVE-2016-5195 - 内核升级方法

    如题,对于脏牛(Dirty COW)漏洞的修复方式已经在上篇文章中有介绍过如何验证,这里对如何升级内核给出修复建议. (注意:为避免不必要的生产风险的发生,请审核自己的实际环境而决定采用什么方法进行升 ...

  7. linux 内核提权

    不经意间找到了大牛总结的一些Linux提权exp 我直接借花献佛分享给大家 #CVE #Description #Kernels CVE-2017-1000367 [Sudo] (Sudo 1.8.6 ...

  8. abcdocker 的博客

    技术参考总结 abcdocker 的博客 09月 3 篇 20日 Centos7 图形化创建KVM 10日 Nginx 代理Google 进行*** 10日 mac 安装装逼神器cmatrix 08月 ...

  9. 大牛总结的Linux提权Exp合集

    https://github.com/SecWiki/linux-kernel-exploits #CVE #Description #Kernels CVE-2017-1000367 [Sudo] ...

随机推荐

  1. HTML5 FileReader接口学习笔记

    1.FileReader概述 FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据. 其中F ...

  2. Linux认知之旅【03 进一步了解Linux命令】!

    再仔细的研究一下命令,你会进一步提高提高对Linux的操作! 看完本文有空http://man.linuxde.net/转转!这是个好网站! 一.命令是什么? 计算机术语[command]:形容在对计 ...

  3. dpkg.cfg

  4. java细节篇(==和equals的区别)

    1)当==两边是对象时(String,Integer...),两边比的都是地址2)当==两边是基本类型时(int,float),两边比的都是值3)默认equals比的是对象的地址,但是重写的话可以改变 ...

  5. linux中帮助参数 man whatis which info区别?

    在linux终端,面对命令不知道怎么用,或不记得命令的拼写及参数时,我们需要求助于系统的帮助文档: linux系统内置的帮助文档很详细,通常能解决我们的问题,我们需要掌握如何正确的去使用它们: 在只记 ...

  6. .net网站数据抓取

    最新项目需要抓取人民币汇率中间价的数据,所以就写了个简单的爬虫抓取数据.抓取的网站为:http://www.safe.gov.cn/wps/portal/sy/tjsj_hlzjj_inquire # ...

  7. VB.NET视频总结——基础篇

    VB.NET视频是台湾讲师曹祖胜和林煌章共同带来的经典视频,视频中老师的台湾腔特别重,听起来有些别扭.而且对于计算机方面的术语翻译的与大陆有很大差异,所以刚开始看视频的时候总是进入不了状态,一头雾水的 ...

  8. 【bzoj2770】YY的Treap 权值线段树

    题目描述 志向远大的YY小朋友在学完快速排序之后决定学习平衡树,左思右想再加上SY的教唆,YY决定学习Treap.友爱教教父SY如砍瓜切菜般教会了YY小朋友Treap(一种平衡树,通过对每个节点随机分 ...

  9. thinkPHP判断是否修改成功

    thinkPHP中使用save方法来更新数据的save方法的正常执行时返回值是影响的记录数,出错时返回false,返回为0和返回false在很多业务场景下都是不同的. 而当修改的内容和原有内容一致的时 ...

  10. Offer 收割编程练习赛 87B 方圆距离

    与坐标轴平行的矩形和圆的位置关系. 分两种情况. 圆与矩形交集不为空 此时答案为零.问题归结为如何判断圆与矩形交集不为空. 先排除矩形顶点在圆内或圆心在矩形内. 此时,若矩形与圆交集不为空,则必有矩形 ...