这几天一个叫做"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. 三 APPIUM GUI讲解(Windows版)

    本文本转自:http://www.cnblogs.com/sundalian/p/5629386.html APPIUM GUI讲解(Windows版)   Windows版本的APPIUM GUI有 ...

  2. Restful API实战

    简介:随着移动互联网的发展,客户端层出不穷,app,web,微信端等等,而后端业务逻辑基于是一致的,如何做到业务逻辑“一次编写,随时接入”?答案是通过远程调用API,而目前比较火的方案是“Restfu ...

  3. yum常规操作的基本用法

    比如说,我想用nano编辑器的,发现没有安装, 这个时候你就可以用$ yum search nano 查询nano属于哪个安装包下. 发现就有nano这个安装包, 这个时候安装一下这个安装包,$ su ...

  4. mysql Access denied for user 'root'@'localhost'问题解决

    问题描述: 系统安装mysql的过程中,没有提示配置用户名和密码相关的信息,安装完毕后,登录报错. 表现现象为: mysql -u root -p [输入root密码] 界面提示: ERROR 169 ...

  5. Python——初识Python

    本篇主要内容: • Python的特点 • Python的种类 • Python的编码 • Python的安装环境推荐 • Python的基础用法:输入输出,算术运算符,逻辑运算符,基本程序结构语法 ...

  6. android ViewGroup getChildDrawingOrder与 isChildrenDrawingOrderEnabled()

    getChildDrawingOrder与 isChildrenDrawingOrderEnabled()是属于ViewGroup的方法.   getChildDrawingOrder 用于 返回当前 ...

  7. 【BestCoder #44】

    因为这场比赛,我愉快地逃掉了晚自修. T1一开始各种SillyB,忘了40%的最低限制... T2各种想吐槽... 明明OJ警告说%lld是不行的我就换成%I64D(上面写这样的)... 结果各种WA ...

  8. Codeforces Round #421 (Div. 2) D. Mister B and PR Shifts

    Codeforces Round #421 (Div. 2) D. Mister B and PR Shifts 题意:给一个长度为\(n\)的排列,每次可以向右循环移位一次,计算\(\sum_{i= ...

  9. 命令__shell变量$#,$@,$0,$1,$2的含义解释

    linux中shell变量$#,$@,$0,$1,$2的含义解释:变量说明:$$ Shell本身的PID(ProcessID)$! Shell最后运行的后台Process的PID$? 最后运行的命令的 ...

  10. MAP的get与containskey

    前提是:Map可以出现在k与v的映射中,v为null的情况, 所以containsKey出现更加必要. map.get(key)是得到的key所对应的value值. map.contains(key) ...