当前位置:首页 > 电脑常识 > 正文

深入解读脏牛Linux本地提权缝隙(CVE

11-22 电脑常识

该缝隙是Linux的一个本地提权缝隙,发明者是Phil Oester,影响>=2.6.22的所有Linux内核版本,修复时间是2016年10月18号。该缝隙的原因是get_user_page内核函数在措置惩罚惩罚Copy-on-Write(以下使用COW暗示)的过程中,可能产出竞态条件造成COW过程被粉碎,导致呈现写数据到进程地点空间内只读内存区域的机会。当我们向带有MAP_PRIVATE符号的只读文件映射区域写数据时,会孕育产生一个映射文件的复制(COW),对此区域的任何改削都不会写回本来的文件,如果上述的竞态条件产生,就能告成的写回本来的文件。好比我们改削su或者passwd措施就可以到达root的目的。
0x01 POC分析
POC的地点如下:[https://github.com/dirtycow/dirtycow.github.io/blob/master/dirtyc0w.c], 下面是POC关键部分的伪代码:
Main:
fd = open(filename, O_RDONLY)
fstat(fd, &st)
map = mmap(NULL, st.st_size , PROT_READ, MAP_PRIVATE, fd, 0)
start Thread1
start Thread2

Thread1:
f = open("/proc/self/mem", O_RDWR)
while (1):
lseek(f, map, SEEK_SET)
write(f, shellcode, strlen(shellcode))

Thread2:
while (1):
madvise(map, 100, MADV_DONTNEED)
首先打开我们需要改削的只读文件并使用MAP\_PRIVATE符号映射文件到内存区域,然后启动两个线程:
此中一个线程向文件映射的内存区域写数据,这时内核给与COW机制。
另一个线程使用带MADV_DONTNEED参数的madvise系统挪用将文件映射内存区域释放,到达滋扰另一个线程的COW过程,孕育产生竞态条件,当竞态条件产生时就能写入文件告成。
还有一种要领:使用ptrace系统挪用的PTRACE_POKETEXT参数来写文件映射的内存区域,参考见[https://github.com/dirtycow/dirtycow.github.io/blob/master/pokemon.c]。
0x02 缝隙道理分析
先附上一份[https://github.com/dirtycow/dirtycow.github.io/wiki/VulnerabilityDetails]中的源代码分析功效:
faultin_page
handle_mm_fault
__handle_mm_fault
handle_pte_fault
do_fault
do_cow_fault
alloc_set_pte
maybe_mkwrite(pte_mkdirty(entry), vma)
# Returns with 0 and retry
follow_page_mask
follow_page_pte
(flags & FOLL_WRITE) && !pte_write(pte)

faultin_page
handle_mm_fault
__handle_mm_fault
handle_pte_fault
FAULT_FLAG_WRITE && !pte_write
do_wp_page
PageAnon()
reuse_swap_page
wp_page_reuse
maybe_mkwrite
ret = VM_FAULT_WRITE
((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))

# Returns with 0 and retry as a read fault
cond_resched -> different thread will now unmap via madvise
follow_page_mask
!pte_present && pte_none
faultin_page
handle_mm_fault
__handle_mm_fault
handle_pte_fault
do_fault
do_read_fault
Copy-on-Write(COW)
当我们用mmap去映射文件到内存区域时使用了MAP\_PRIVATE符号,我们写文件时会写到COW机制孕育产生的内存区域中,原文件不受影响。此中获取用户进程内存页的过程如下:
1. 第一次挪用follow_page_mask查找虚拟地点对应的page,带有FOLL_WRITE符号。因为地址page不在内存中,follow_page_mask返回NULL,第一次掉败,进入faultin_page,最终进入do_cow_fault分配不带_PAGE_RW符号的匿名内存页,返回值为0。
2. 从头开始循环,第二次挪用follow_page_mask,带有FOLL_WRITE符号。由于不满足((flags & FOLL_WRITE) && !pte_write(pte))条件,follow_page_mask返回NULL,第二次掉败,进入faultin_page,最终进入do_wp_page函数分配COW页。并在上级函数faultin_page中去失FOLL_WRITE符号,返回0。
3. 从头开始循环,第三次挪用follow_page_mask,不带FOLL_WRITE符号。告成得到page。
以下代码以liux 4.7([https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.7.tar.xz])的源码为例,具体解读一下流程。首先从关键的获取用户进程内存页的函数函数get_user_pages看起,get_user_pages系列函数用于获取用户进程虚拟地点地址的页(struct page),返回的是page数组,该系列函数最终城市挪用\__get_user_pages。
long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
unsigned long start, unsigned long nr_pages,
unsigned int gup_flags, struct page **pages,
struct vm_area_struct **vmas, int *nonblocking)

温馨提示: 本文由杰米博客推荐,转载请保留链接: https://www.jmwww.net/file/pc/13416.html

博客主人杰米WWW
杰米博客,为大家提供seo以及it方面技巧喜欢的朋友收藏哦!
  • 11365文章总数
  • 1378074访问次数
  • 建站天数
  •