章节大纲

  • 虽然 system() 和 execve() 都可以用来运行新程序,但如果在特权程序(例如 Set-UID 程序)中使用 system(),则可能非常危险。我们已经看到 system() 如何受 PATH 环境变量的影响,因为该变量会影响 shell 的行为。而 execve() 没有这个问题,因为它不会调用 shell。调用 shell 还有另一个危险的后果,这次与环境变量无关。让我们看以下场景。

    Bob 在一家审计机构工作,他需要调查一家公司是否存在欺诈行为。为了调查,Bob 需要能够读取公司 Unix 系统中的所有文件。但是为了保护系统的完整性,Bob 不应该能够修改任何文件。为此,系统的超级用户 Vince 编写了一个特殊的 set-root-uid 程序(见下文),并授予 Bob 可执行权限。该程序要求 Bob 在命令行输入一个文件名,然后它将运行 /bin/cat 来显示指定的文件。由于程序以 root 权限运行,它可以显示 Bob 指定的任何文件。然而,由于该程序没有写的操作,Vince 确信 Bob 无法使用这个特殊程序修改任何文件。

    int main(int argc, char *argv[])
    {
      char *v[3];
      char *command;
    
      if(argc < 2) {
        printf("Please type a file name.\n");
        return 1;
      }
    
      v[0] = "/bin/cat"; v[1] = argv[1]; v[2] = NULL;
      command = malloc(strlen(v[0]) + strlen(v[1]) + 2);
      sprintf(command, "%s %s", v[0], v[1]);
    
      // 仅使用以下两种方法之一。
      system(command);
      // execve(v[0], v, NULL);
    
      return 0 ;
    }
      • 编译上述程序,使其成为一个 root 拥有的 Set-UID 程序。程序将使用 system() 调用命令。如果你是 Bob,你能破坏系统的完整性吗?例如,你是否可以删除系统里的任何文件?

      • 注释掉 system(command) 语句,取消注释 execve() 语句,程序将使用 execve() 来运行命令。编译程序并使其成为一个 root 拥有的 Set-UID 程序。在步骤 1 中的攻击是否仍然有效?请描述并解释你的观察。