本任务的目的是在启用了 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。在你的报告中,请描述你是如何构造输入的。