章节大纲

  • 本任务的目的是在启用了 shell 的对策后发起 return-to-libc 攻击。在执行任务 1 至 3 之前,我们将 /bin/sh 重新链接到了 /bin/zsh,而不是 /bin/dash(原始设置)。这是因为一些 shell 程序,如 dash 和 bash,在 Set-UID 进程中执行时会自动放弃权限。在此任务中,我们希望击败此类对策,即即使 /bin/sh 仍然指向 /bin/dash,我们也希望获得 root shell。首先,我们将符号链接改回:
     
    $ sudo ln -sf /bin/dash /bin/sh
     
    尽管 dash 和 bash 都会放弃 Set-UID 权限,但如果 -p 选项被调用,权限将不被放弃。当我们返回到 system 函数时,这个函数会调用 /bin/sh ,但它不使用 -p 选项。因此,目标程序的 Set-UID 权限将被放弃。如果有一个函数允许我们直接执行 "/bin/bash -p", 而不是像 system 那样,我们就可以获得 root 权限。

    很多 libc 函数都可以做到这一点,例如 exec() 系列函数,包括 execl()、execle()、execv() 等。我们来看 execv() 函数:
     
    int execv(const char *pathname, char *const argv[]);
     
    这个函数接受两个参数,一个是命令的地址,第二个是命令的参数数组的地址。例如,如果我们想使用 execv 调用 "/bin/bash -p", 我们需要设置如下:
     
    pathname = address of "/bin/bash"
    argv[0]  = address of "/bin/bash"
    argv[1]  = address of "-p"
    argv[2]  = NULL (i.e., 4 bytes of zero).
     
    从前面的任务中,我们可以轻松获得字符串的地址。因此,如果我们能在堆栈上构造 argv[] 数组,并获取其地址,我们就能进行 return-to-libc 攻击,但这一次,我们将返回到 execv()函数。
     
    这里有一个难点。 argv[2] 的值必须是零(一个整数零,四个字节)。如果我们在输入中放入四个零, strcpy() 将在第一个零处终止,任何在它之后的内容都不会被复制到 bof() 函数的缓冲区中。这似乎是一个难题,但请记住,你的输入中的所有内容已经在堆栈上,它们在 main() 函数的缓冲区中。获取这个缓冲区的地址并不难。为了简化任务,我们已让易受攻击的程序为你打印出该地址。
     
    就像任务 3 一样,你需要构造你的输入,以便当 bof() 函数返回时,它返回到 execv()。后者从堆栈中获取 "/bin/bash" 字符串的地址和 argv[] 数组的地址。你需要在堆栈上准备好一切,以便当 execv() 被执行时,它可以执行 "/bin/bash -p",从而获得 root shell。在你的报告中,请描述你是如何构造输入的。