章节大纲

  • 以下是一个看似无害的程序(vulp.c),它包含一个竞态条件漏洞。
     
    #include <stdio.h>
    #include<unistd.h>
    
    int main()
    {
       char * fn = "/tmp/XYZ";
       char buffer[60];
       FILE *fp;
    
       /* 获取用户输入 */
       scanf("%50s", buffer );
    
       if(!access(fn, W_OK)){                 🅰
          fp = fopen(fn, "a+");               🅱
          fwrite("\n", sizeof(char), 1, fp);
          fwrite(buffer, sizeof(char), strlen(buffer), fp);
          fclose(fp);
       }
       else printf("No permission \n");
    }

    该程序是一个拥有 root 权限的 setuid 程序,它会在临时文件 /tmp/XYZ 的末尾添加用户输入的内容。由于代码以 root 权限运行(有效用户 ID 为 0),因此可以覆盖任何文件。为了防止自己意外覆盖他人的文件,程序首先检查自己的真实用户 ID 是否具有对文件 /tmp/XYZ 的修改权限,这就是第 🅰 行 access()调用的目的。如果确实有权限,程序会在第 🅱 行打开该文件并往其中添加用户输入的内容。
     
    乍一看似乎这个程序没有问题。但是,在检查(access)和使用(fopen)之间存在一个时间窗口,在这段时间里,被 access() 检查的文件可能与被 fopen() 使用的文件不是同一个,尽管它们具有相同的文件名 /tmp/XYZ。如果攻击者能够在该时间窗口内使 /tmp/XYZ 成为指向 /etc/passwd 的符号链接,则可以导致用户输入被添加到 /etc/passwd中,由此获得 root 权限。由于该漏洞运行在 root 权限下,因此它有权限修改任何文件。
     
    我们首先编译上述代码,并将可执行程序转换为一个由 root 拥有的 setuid 程序:
     
    $ gcc vulp.c -o vulp
    $ sudo chown root vulp
    $ sudo chmod 4755 vulp