这几天一个叫做"Dirty COW"的linux内核竞争条件漏洞蛮火的,相关公司不但给这个漏洞起了个洋气的名字,还给它设计了logo(见下图),首页,Twitter账号以及网店。恰逢周末,闲来无事的我也强势围观了一波,在这之前,好好的学习了一下竞争条件以及看了一下近几年关于竞争条件漏洞。

  

  1:什么是竞争条件以及竞争条件为什么会产生漏洞

    竞争条件是系统中的一种反常现象,由于现代Linux系统中大量使用并发编程,对资源进行共享,如果产生错误的访问模式,便可能产生内存泄露,系统崩溃,数据破坏,甚至安全问题。竞争条件漏洞就是多个进程访问同一资源时产生的时间或者序列的冲突,并利用这个冲突来对系统进行攻击。一个看起来无害的程序如果被恶意攻击者利用,将发生竞争条件漏洞。

    用简单代码讲的更清楚:    

//myThreadTest
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> int i = ;
void *mythread1()
{
if(i == ){ <a>
  sleep();
  if(i == ) <b>
  printf("hack it!\n");   else
  printf("you can try again!\n");
}
}
void *mythread2()
{
sleep();
i=;
} int main(int argc, const char *argv[])
{
pthread_t id1,id2; pthread_create(&id1, NULL, (void *)mythread1,NULL);
pthread_create(&id2, NULL, (void *)mythread2,NULL); pthread_join(id1,NULL);
pthread_join(id2,NULL); return ;
} 

    单独看mythread1()函数,显然肯定是输出  "you can try again!\n",但是,由于i是全局共享的资源可以通过线程的方式来使i值在<a>的之后<b>之前进行改变,可以改变函数的流程

      

    为了使结果清晰明了,所以手动利用sleep()函数进行设置了,实际利用过程中,可能是一些会耗时较长的函数,如memcpy()

  流程如下:

      

  这也是竞争条件的核心思想,这也是一个数据竞争引发条件竞争的典型例子。

  2:以cve-2014-0496为例

    由于Android的崛起起,Linux内核的安全性受到了极大地考验,很多潜藏的安全也慢慢被安全研究眼发现。这个漏洞的主要成因是因为在pty/tty设备驱动中在访问某些资源的时候没有正确的加锁处理,准确来说,加锁的范围较小。首先,先看一下理解这个漏洞必要的两个数据结构

struct tty_buffer {
struct tty_buffer *next;
char *char_buf_ptr;
unsigned char *flag_buf_ptr;
int used;
int size;
int commit;
int read;
/* Data points here */
unsigned long data[];
}; struct tty_struct {
int magic;
struct kref kref;
struct device *dev;
struct tty_driver *driver;
const struct tty_operations *ops;
struct tty_bufhead buf; };

    tty_buffer是一个动态大小的数据结构,其中,char_buf_ptr指向该对象后面的第一字节,即(char_buf_ptr = tty_buffer+sizeof(tty_buffer)大小为size...                   flag_buf_ptr=tty_buffer+sizeof(tty_buffer)+size。大小为size。used为已经使用的buffer大小。tty_struct为tty的数据结构,其中ops为tty设备的一些操作函数,如open(),write()。

    该漏洞的函数调用为write(pty_fd) in userspace -> sys_write() in kernelspace -> tty_write() -> pty_write() -> tty_insert_flip_string_fixed_flag()。看 tty_insert_flip_string_fixed_flag()函数:

int tty_insert_flip_string_fixed_flag(struct tty_struct *tty,
const unsigned char *chars, char flag, size_t size)
{
int copied = ;
do {
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(tty, goal);
struct tty_buffer *tb = tty->buf.tail;
if (unlikely(space == ))
break;
memcpy(tb->char_buf_ptr + tb->used, chars, space);
memset(tb->flag_buf_ptr + tb->used, flag, space);
tb->used += space;
copied += space;
chars += space;
} while (unlikely(size > copied));
return copied;
}

  这个函数的大致流程为通过tty_buffer_request_room来申请函数空间,如果没有,则另外申请。利用memcpy进行复制内存。最后再递增used。其中memcpy调用时间较长,由于没有加锁,存在竞争条件的问题,查看tty_buffer_request_room,看是否能够利用:

int tty_buffer_request_room(struct tty_struct *tty, size_t size)
{
struct tty_buffer *b, *n;
int left;
unsigned long flags; spin_lock_irqsave(&tty->buf.lock, flags); if ((b = tty->buf.tail) != NULL)
left = b->size - b->used;
else
left = 0;
if (left < size) { <1>
/* This is the slow path - looking for new buffers to use */
if ((n = tty_buffer_find(tty, size)) != NULL) {
if (b != NULL) {
b->next = n;
b->commit = b->used;
} else
tty->buf.head = n;
tty->buf.tail = n;
} else
size = left;
}
spin_unlock_irqrestore(&tty->buf.lock, flags);
return size;
}

  这里有一个整数溢出的问题,由于程序员的疏忽size为unsigned,而left为int,根据c语言规则,left为转化为无符号,在<1>中,如果left为负数,转化为无符号数,便可以实现size大于left,即需要的空间大于空余的空间,但是仍不进行内存分配,再通过tty_insert_flip_string_fixed_flag函数里的memcpy来溢出,对内存进行读写。left = b->size - b->used,其中size不能改变,但是可以改变used的值,通过条件竞争,来对used进行连续多次写,使used大于size。

  详细流程图如下:

  由于memcpy函数将会持续较长时间,只要对一个tty连续多次利用多线程对该函数进行申请空间,使used > size,这样就可以在使用memcpy时,进行内存溢出。

  这个漏洞,是一个非常典型的竞争条件漏洞。其实也就是上面那个测试代码也是这个函数的简化版。

  3:结语

    在现代操作系统,竞争条件是不可避免的,只要两个两个执行线程访问同一个数据结构,由于我们无法预测linux调度的顺序,就会产生混合,就可能产竞争条件。我们如果需要对内核或驱动编写,应该尽量避免资源的共享,如果不可避免,需要利用锁来对原子数据进行锁定。

 

理解竞争条件( Race condition)漏洞的更多相关文章

  1. 竞态条件 race condition data race

    竞态条件 race condition Race condition - Wikipedia https://en.wikipedia.org/wiki/Race_condition A race c ...

  2. Golang 入门 : 竞争条件

    笔者在前文<Golang 入门 : 理解并发与并行>和<Golang 入门 : goroutine(协程)>中介绍了 Golang 对并发的原生支持以及 goroutine 的 ...

  3. Fortify Audit Workbench 笔记 Race Condition: Singleton Member Field 竞争条件:单例的成员字段

    Race Condition: Singleton Member Field 竞争条件:单例的成员字段 Abstract Servlet 成员字段可能允许一个用户查看其他用户的数据. Explanat ...

  4. Operating System-进程/线程内部通信-竞争条件(Race Conditions)

    从本文开始介绍进程间的通信,进程间通信遇到的问题以及方式其实和线程之间通信是一致的,所以进程间通信的所有理论知识都可以用在线程上,接下来的系列文章都会以进程之间的通信为模版进行介绍,本文主要内容: 进 ...

  5. WEB安全新玩法 [10] 防范竞争条件支付漏洞

    服务器端业务逻辑,特别是涉及数据库读写时,存在着关键步骤的时序问题,如果设计或代码编写不当就可能存在竞争条件漏洞.攻击者可以利用多线程并发技术,在数据库的余额字段更新之前,同时发起多次兑换积分或购买商 ...

  6. 理解 Linux 条件变量

    理解 Linux 条件变量 1 简介 当多个线程之间因为存在某种依赖关系,导致只有当某个条件存在时,才可以执行某个线程,此时条件变量(pthread_cond_t)可以派上用场.比如: 例1: 当系统 ...

  7. Race condition

    在很多门课上都接触到race condition, 其中也举了很多方法解决这个问题.于是想来总结一下这些方法. Race condition 它旨在描述一个系统或者进程的输出依赖于不受控制的事件出现顺 ...

  8. 【多线程同步案例】Race Condition引起的性能问题

    Race Condition(也叫做资源竞争),是多线程编程中比较头疼的问题.特别是Java多线程模型当中,经常会因为多个线程同时访问相同的共享数据,而造成数据的不一致性.为了解决这个问题,通常来说需 ...

  9. python线程条件变量Condition(31)

    对于线程与线程之间的交互我们在前面的文章已经介绍了 python 互斥锁Lock / python事件Event , 今天继续介绍一种线程交互方式 – 线程条件变量Condition. 一.线程条件变 ...

随机推荐

  1. 通过slf4j/log4j的MDC/NDC 实现日志追踪

    在分布式系统或者较为复杂的系统中,我们希望可以看到一个客户请求的处理过程所涉及到的所有子系统\模块的处理日志. 由于slf4j/log4j基本是日志记录的标准组件,所以slf4j/log4j成为了我的 ...

  2. ASP.NET Core 2.1 源码学习之 Options[1]:Configure 【转】

    原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-configure-in-asp-net-core.html 配置 ...

  3. 孤荷凌寒自学python第十八天python变量的作用范围

    孤荷凌寒自学python第十八天python函数的形参与变量的范围 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 一.在python的函数中各种不同的形式参数在定义的先后顺序上有规定: 必须 ...

  4. php中普通方法和静态方法的区别以及抽象类和接口

    实例化类产生对象.class fenbi{ //普通成员,属于对象 public $length = "10cm"; //静态成员,静态变量,属于类. public static ...

  5. Spring AOP的一个简单实现

    针对学习笔记(六)中的购买以及退货代码,我们加入AOP框架,实现同样一个功能. 首先配置XML:service采用和之前一样的代码,只是没有通过实现接口来实现,而是直接一个实现类.transactio ...

  6. 为啥shmem不回收 | drop_caches

    内核在哪里禁止对tmpfs中内存页的回收 mem.limit_in_bytes同样会触发shrink_zones过程! shrink_zones是代码中的直接内存回收路径 1.try_to_free_ ...

  7. 【bzoj2238】Mst 最小生成树+树链剖分+线段树

    题目描述 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在) 输入 第一行两 ...

  8. vue前端开发那些事(1)

    如上图所示,用vue开发一个小型网站所涉及到的知识点.这只是前端部分已经这么多了.接下来我分解开来说. 1.Node 当我们开发vue项目的时候,首先要安装Node.js,那么我们即使当时不理解为什么 ...

  9. spark与storm比对与选型

    大数据实时处理平台市场上产品众多,本文着重讨论spark与storm的比对,最后结合适用场景进行选型. 一.spark与storm的比较 比较点 Storm Spark Streaming 实时计算模 ...

  10. 《c程序设计语言》读书笔记-递归实现快速排序算法

    #include <stdio.h> void swap(int v[],int i,int j) { int temp; temp = v[i]; v[i] = v[j]; v[j] = ...