章节大纲

  • 你可以使用我们预构建的 Ubuntu 虚拟机来执行实验任务。Ubuntu 和其他 Linux 发行版已经实现了几个安全机制,使得缓冲区溢出攻击变得困难。为了简化我们的攻击,我们需要先禁用它们。

    1. 地址空间随机化:Ubuntu 和其他几个基于 Linux 的系统使用地址空间随机化来随机化堆和堆栈的起始地址,使得猜测确切地址变得困难。猜测地址是缓冲区溢出攻击的关键步骤之一。在此实验中,我们使用以下命令关闭地址空间随机化:
     
    $ sudo sysctl -w kernel.randomize_va_space=0

    2. 堆栈保护方案 StackGuard:gcc 编译器实现了一个名为 StackGuard 的安全机制来防止缓冲区溢出。在这种保护存在的情况下,缓冲区溢出攻击无法工作。我们可以通过在编译期间使用 -fno-stack-protector 选项来禁用此保护。例如,要编译一个禁用 StackGuard 的程序 example.c,我们可以这样做:
     
    $ gcc -m32 -fno-stack-protector example.c

    3. 不可执行堆栈:Ubuntu曾经允许可执行堆栈,但现在已改变。程序(和共享库)的二进制映像必须声明它们是否需要可执行堆栈,即它们需要在程序头中标记一个字段。内核或动态链接器使用这个标记来决定是否使此运行程序的堆栈可执行或不可执行。这个标记是由 gcc 自动完成的,默认情况下,堆栈被设置为不可执行。要改变这一点,请在编译程序时使用以下选项:
     
    % 可执行堆栈
    $ gcc -m32 -z execstack  -o test test.c
    
    % 不可执行堆栈
    $ gcc -m32 -z noexecstack  -o test test.c
     
    由于本实验的目标是展示不可执行堆栈保护不起作用,你应该在本实验中使用 "-z noexecstack" 选项来编译你的程序。

     
    4. 配置 /bin/sh:在 Ubuntu 20.04 中,/bin/sh 符号链接指向 /bin/dash。dash 有一个对策,可以阻止自己在 Set-uid 进程中执行。如果 dash 在 Set-uid 进程中执行,它会立即将有效用户 ID 更改为进程的实际用户 ID,实质上放弃了其权限。
     
    由于我们的受害者程序是一个 Set-uid 程序,我们的攻击使用 system() 函数来运行我们选择的命令。这个函数不会直接运行我们的命令,它调用 /bin/sh 来运行我们的命令。因此,/bin/dash 在执行我们的命令之前就放弃了 Set-uid 权限,使我们的攻击更加困难。要禁用此保护,我们将 /bin/sh 链接到另一个没有这种对策的 shell。我们在 VM 中安装了一个名为 zsh 的 shell 程序。我们使用以下命令将 /bin/sh 链接到 zsh:
     
    $ sudo ln -sf /bin/zsh /bin/sh

    应当注意,dash 中实现的对策是可以绕过的。我们将在后续任务中进行。