Race Condition

这几天复习操作系统来谈谈竞争在安全中的应用

Race Condition

WEB

文件上传

学过操作系统后 我们了解了 读者-写者问题

如果没有对两个进程加锁 则容易导致竞争问题的出现

比如下面的文件上传

走网上顺了一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
echo "xxx";
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['xxx_file']['name'] ;
// var_dump($file_name);
$temp_file = $_FILES['xxx_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = 'upload' . '/' . $file_name;
echo "yes...<br>".$file_name.$file_ext."<br>";
//这里直接进行文件上传的操作
if(move_uploaded_file($temp_file, $upload_file)){
echo "upload success..<br>".$upload_file;
//然后再检测合法性
if(in_array($file_ext,$ext_arr)){
$img_path = 'upload' . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
echo "had been removed!!!";
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传出错!';
}
}
?>

该段代码并没有对读文件和删除文件加锁 如果我们能够在删除文件的时候读取该文件 那么改代码将无法删除我们已经上传过的文件 他的过滤就失效了

所以我们可以这样写

甚至可以考虑用python中的多线程来解决

1
2
3
4
脚本1
不停地读取shell
脚本2
不停地上传shell

PWN

考完试再研究

忍不住了

这里我们主要考虑计算机程序方面的条件竞争。当一个软件的运行结果依赖于进程或者线程的顺序时,就可能会出现条件竞争。简单考虑一下,可以知道条件竞争需要如下的条件

  • 并发,即至少存在两个并发执行流。这里的执行流包括线程,进程,任务等级别的执行流。
  • 共享对象,即多个并发流会访问同一对象。常见的共享对象有共享内存,文件系统,信号。一般来说,这些共享对象是用来使得多个程序执行流相互交流。此外,我们称访问共享对象的代码为临界区。在正常写代码时,这部分应该加锁。
  • 改变对象,即至少有一个控制流会改变竞争对象的状态。因为如果程序只是对对象进行读操作,那么并不会产生条件竞争。

由于在并发时,执行流的不确定性很大,条件竞争相对难察觉,并且在复现和调试方面会比较困难。这给修复条件竞争也带来了不小的困难。

条件竞争造成的影响也是多样的,轻则程序异常执行,重则程序崩溃。如果条件竞争漏洞被攻击者利用的话,很有可能会使得攻击者获得相应系统的特权。

直接来看一个题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
void showflag() { system("cat flag"); }
void vuln(char *file, char *buf) {
int number;
int index = 0;
int fd = open(file, O_RDONLY);
if (fd == -1) {
perror("open file failed!!");
return;
}
while (1) {
number = read(fd, buf + index, 128);
if (number <= 0) {
break;
}
index += number;
}
buf[index + 1] = '\x00';
}
void check(char *file) {
struct stat tmp;
if (strcmp(file, "flag") == 0) {
puts("file can not be flag!!");
exit(0);
}
stat(file, &tmp);
if (tmp.st_size > 255) {
puts("file size is too large!!");
exit(0);
}
}
int main(int argc, char *argv[argc]) {
char buf[256];
if (argc == 2) {
check(argv[1]);
vuln(argv[1], buf);
} else {
puts("Usage ./prog <filename>");
}
return 0;
}

可以看出程序的基本流程如下

  • 检查传入的命令行参数是不是 “flag”,如果是的话,就退出。
  • 检查传入的命令行参数对应的文件大小是否大于 255,是的话,就直接退出。
  • 将命令行参数所对应的文件内容读入到 buf 中 ,buf 的大小为 256。

看似我们检查了文件的大小,同时 buf 的大小也可以满足对应的最大大小,但是这里存在一个条件竞争的问题。

如果我们在程序检查完对应的文件大小后,将对应的文件删除,并符号链接到另外一个更大的文件,那么程序所读入的内容就会更多,从而就会产生栈溢出。

reference

Race Condition - CTF Wiki (ctf-wiki.org)