Race Condition漏洞

Race Condition

Race Condition(竞争条件)是一种情形,在该情形下系统或者程序的输出受其他不可控事件的顺序或事件的影响。软件中的Race Condition通常出现在两个并发线程访问同一个共享资源。因而存在Race Condition漏洞的特权程序,通过对不可控事件施加影响,攻击者能够影响特权程序的输出。

常见的Race Condition漏洞有一下两类

  • Time-of-Check Time-of-Use

这是一种存在于软件中的Race Condition漏洞在软件开发中,Time-of-Check Time-of-Use(TOCTOU、TOCTTOU或TOC/TOU)是一类由竞态条件引起的软件bug,其中包括对系统的一部分(例如安全凭据)的状态检查和对检查结果的使用。顾名思义,这种漏洞由检查时间和使用时间之间的间隔产生,如下代码:

if(!access("/tmp/XYZ",W_OK)){
    f=open("/tmp/XYZ",O_WRITE);
    write_to_file(f);
}
else{
    fprintf(stderr,"Permission Denied");
}

若以上代码执行在setuid程序中(特权程序),便产生了race condition 漏洞。access()检查了real uid(真实用户)是否具有对/tmp/XYZ文件的写权限(W_OK),若real uid拥有该权限其返回0,open()系统调用同样也检查了用户的权限,但是不同于access()检查real uid,open()检查effective uid。在文件被检查和被使用(打开open)期间,存在一段窗口期,攻击者可以利用该窗口期进行攻击,例如在运行该程序前,攻击者在/tmp目录下创建一个属于自己的文件,此时可以通过access()检查,在指令流到达open()前,将该文件通过符号链接指向任意root权限文件(如/etc/passwd),便可以通过open()检查,打开原本只有root权限才可以打开的文件,最终实现对任意文件写的权限。

  • Dirty Cow

Dirty Cow漏洞2016年10月被披露,该漏洞影响了所有基于linux的操作系统,如android。Dirty Cow可以造成非常严重的后果:攻击者可以通过该漏洞获取root权限,可以修改任何受保护的文件,即使他对该文件只有可读权限。

了解DirtyCow漏洞,需要首先了解部分linux系统函数,如下

void mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset)

mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。mmap在用户空间映射调用系统中作用很大。头文件 <sys/mman.h>

参数说明:

  • start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。

  • length:映射区的长度。//长度单位是 以字节为单位,不足一内存页按一内存页处理

  • prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起

    PROT_EXEC //页内容可以被执行

    PROT_READ //页内容可以被读取

    PROT_WRITE //页可以被写入

    PROT_NONE //页不可访问

  • flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体

    MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。

    MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。

    MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。

    MAP_DENYWRITE //这个标志被忽略。

    MAP_EXECUTABLE //同上

    MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。

    MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。

    MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。

    MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。

    MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。

    MAP_FILE //兼容标志,被忽略。

    MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。

    MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。

    MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。

  • fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。

  • off_toffset:被映射对象内容的起点。

  • 返回值:成功映射,则mmap()返回被映射区的指针,失败时返回MAP_FAILED

mmap有许多应用,

  1. 进程间通信:两个进程想彼此通信,可以使用mmap映射同一个文件到其各自的内存空间中,一个进程对其自己内存空间中该文件的映射进行改动,另一个进程可以立即感知(flags是MAP_SHARED),这种映射形如两个进程间的共享内存。
  2. 提高性能:通常我们访问一个文件使用read()和write()系统调用,这两个函数需要在内核态和用户态间转换和复制数据,速度不快,通过使用内存映射,使得访问一个文件变成内存操作(在用户态进行),这使得访问文件的时间花销大大减小。

一旦一个文件被映射到内存,我们可以通过对内存读写来实现访问文件的目的。文件的内容会被加载到物理内存上,且会被映射到调用mmap()函数的进程的虚拟内存中(通过页机制)。不同的进程映射同一文件,尽管他们各自将该文件映射到的虚拟内存不同,但是在物理内存上相同。

针对mmap()函数,flags字段在Dirty Cow漏洞中,需要尤其主要MAP_SHAREDMAP_PRIVATE

  • MAP_SHARED:任何进程对自己的虚拟内存的修改,都会影响到物理内存上的文件内容,因而所有其他映射了该文件的进程都可以观察到修改。
  • MAP_PRIVATE:文件被映射到调用mmap()的进程的私人内存中,对该内存的内容的修改都不会影响文件的原始内容。因为复制内容到私人内存中需要消耗时间,因而在linux内核中采用Copy on Write方法,即只有当文件内容需要修改时才会复制其内容到新的一块内存,所以即使flags是MAP_PRIVATE,若没有对文件进行写,则系统不会将该文件复制到新的私有内存。

例如如图(参考《Computer Security:A Hands-on Approach 》):

从该图可以看出,新创建的物理内存不再映射原始文件,因此对该内存的改动不会影响原始文件内容,MAP_PRIVATE实现的便是该功能。

 

int madvise(void *addr,size_t length,int advice)

madvise函数建议内核,在从 addr 指定的地址开始,长度等于 len 参数值的范围内,该区域的用户虚拟内存应遵循特定的使用模式。内核使用这些信息优化与指定范围关联的资源的处理和维护过程。如果使用 madvise() 函数的程序明确了解其内存访问模式,则使用此函数可以提高系统性能。

参数说明:

  • advice :指出给内核的建议,在Dirty Cow漏洞中需要尤其注意MADV_DONTNEED

当使用MADV_DONTNEED时,内核会释放指示的内存空间的资源,例如上图,进程2在write操作前,其页表指向①物理内存,在copy on write之后,其页表指向②物理内存,使用MADV_DONTNEED之后,其页表又重新指向物理内存①,物理内存②被释放。

 

 

write()函数可以向映射的内存写入数据,通常情况下普通用户对于只读文件是不能进行修改的,但是在linux中,若文件通过MADV_DONTNEED映射到内存,则允许内核通过调用wirte()帮助用户在只读的内存上写数据。

 

 

在了解以上知识以后,我们可以来了解Dirty Cow漏洞。针对copy on write类型的内存,write()系统调用需要经过三个重要步骤

  1. 复制映射内村
  2. 更新进程页表,使其指向新挂载的物理内存
  3. 向内存写数据

不幸的是,上述三个操作不是原子操作,因而存在dirty cow漏洞,在2和3操作之间,若使用madvise且第三个参数设置为MADV_DONTNEED,则第2步更新页表的操作便成为徒劳之举,页表重新指向原始内存,此时若再执行第3步,则数据便重新写入原始内存(不管这个文件映射到内存,是不是read-only,这个数据都会写入)。这样便导致任何普通用户都可以对root权限才可以更改的文件进行更改(只要该用户对该文件有可读权限)。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值