理解竞争条件( Race condition)漏洞
这几天一个叫做"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)漏洞的更多相关文章
- 竞态条件 race condition data race
竞态条件 race condition Race condition - Wikipedia https://en.wikipedia.org/wiki/Race_condition A race c ...
- Golang 入门 : 竞争条件
笔者在前文<Golang 入门 : 理解并发与并行>和<Golang 入门 : goroutine(协程)>中介绍了 Golang 对并发的原生支持以及 goroutine 的 ...
- Fortify Audit Workbench 笔记 Race Condition: Singleton Member Field 竞争条件:单例的成员字段
Race Condition: Singleton Member Field 竞争条件:单例的成员字段 Abstract Servlet 成员字段可能允许一个用户查看其他用户的数据. Explanat ...
- Operating System-进程/线程内部通信-竞争条件(Race Conditions)
从本文开始介绍进程间的通信,进程间通信遇到的问题以及方式其实和线程之间通信是一致的,所以进程间通信的所有理论知识都可以用在线程上,接下来的系列文章都会以进程之间的通信为模版进行介绍,本文主要内容: 进 ...
- WEB安全新玩法 [10] 防范竞争条件支付漏洞
服务器端业务逻辑,特别是涉及数据库读写时,存在着关键步骤的时序问题,如果设计或代码编写不当就可能存在竞争条件漏洞.攻击者可以利用多线程并发技术,在数据库的余额字段更新之前,同时发起多次兑换积分或购买商 ...
- 理解 Linux 条件变量
理解 Linux 条件变量 1 简介 当多个线程之间因为存在某种依赖关系,导致只有当某个条件存在时,才可以执行某个线程,此时条件变量(pthread_cond_t)可以派上用场.比如: 例1: 当系统 ...
- Race condition
在很多门课上都接触到race condition, 其中也举了很多方法解决这个问题.于是想来总结一下这些方法. Race condition 它旨在描述一个系统或者进程的输出依赖于不受控制的事件出现顺 ...
- 【多线程同步案例】Race Condition引起的性能问题
Race Condition(也叫做资源竞争),是多线程编程中比较头疼的问题.特别是Java多线程模型当中,经常会因为多个线程同时访问相同的共享数据,而造成数据的不一致性.为了解决这个问题,通常来说需 ...
- python线程条件变量Condition(31)
对于线程与线程之间的交互我们在前面的文章已经介绍了 python 互斥锁Lock / python事件Event , 今天继续介绍一种线程交互方式 – 线程条件变量Condition. 一.线程条件变 ...
随机推荐
- CSS系列(8) CSS后代选择器和子选择器详解
一.CSS后代选择器详解 1, 生动介绍基本概念 一个标签嵌B在另一个标签A内部,B就是A的后代. 而且,B的后代也是A的后代,这就叫“子子孙孙无穷尽也”. 比如: <div> < ...
- Pytest框架介绍
Pytest框架介绍.安装 pytest是python测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,功能更强大 pytest特征 1:断言提示信 ...
- python 3 直接使用reload函数报错
reload()是python2 的内置函数可以直接使用,但是python3 直接使用此函数报错,需要导入importlib 模块 from importlib import reload
- RPG游戏黑暗之光
1.设置默认鼠标光标 PlayerSettings → Default Cursor 下设置 2.为人物创建单一类 为人物创建了PlayerAnimation.cs.PlayerDir.cs.Play ...
- Mybatis + Oracle 批量insert的问题
这个问题真的太坑了 之前用ibatis+sql server 的foreach 很容易就写出来批量insert数据,但是测试后报错:SQL结束格式错误 现在换到银行工作,数据库也换成Oracle了 特 ...
- 课时40:类与对象:一些相关的BIF
目录: 一.一些相关的BIF 二..课时40课后习题及答案 ********************** 一.一些相关的BIF ********************** 1.issubclass( ...
- tomcat启动后服务访问404
. 解决办法: 在tomcat文件中有个work文件夹.其中,tomcat属于admin用户,work属于 admin用户 ,启动服务由admin用户启动. 但是发现work文件下的目录权限属于 ...
- Horn–Schunck 光流法与其算法理解(gup cuda)
1. 基于Horn-Schunck模型的光流算法 1.1 光流的约束条件 光流 的假设条件认为图像序列,在时间t 的某一像素点与在时间t+1的这一像素点的偏移量保持不变,即 .这就是灰度值守恒 ...
- python与MySQL数据库
python与MySQL数据库 慕课网连接 我使用的软件:python2.7 + MySQL+ Navicat for MySQL + Atom 注意:你的数据库表格类型的引擎为:InnoDB :字符 ...
- [bzoj4712] 洪水 [树链剖分+线段树+dp]
题面 传送门 思路 DP方程 首先,这题如果没有修改操作就是sb题,dp方程如下 $dp[u]=max(v[u],max(dp[v]))$,其中$v$是$u$的儿子 我们令$g[u]=max(dp[v ...