这几天复习操作系统来谈谈竞争在安全中的应用
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'] ;
$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)