以下是一个看似无害的程序(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