章节大纲

      • 要利用目标程序中的缓冲区溢出漏洞,最重要的是要知道缓冲区起始位置与返回地址存储位置之间的距离。我们将使用调试方法来找出该距离。 由于我们有目标程序的源代码,我们可以用调试标志将其编译出来,这样会更方便调试。

        我们需要向 gcc 命令添加 -g 标志,因此二进制文件中包含了调试信息。如果你运行 make,已经创建了调试版本。我们将使用 gdb 来调试 stack-L1-dbg。在运行程序之前需要先创建一个名为 badfile 的文件。

        $ touch badfile                                       ← 创建一个空的 badfile
        $ gdb stack-L1-dbg
        gdb-peda$ b bof                                       ← 在函数 bof() 设置断点
        Breakpoint 1 at 0x124d: file stack.c, line 18.
        gdb-peda$ run                                         ← 开始执行程序
        ...
        Breakpoint 1, bof (str=0xffffcf57 ...) at stack.c:18
        18  {
        gdb-peda$ next                                        ← 执行下一条语句
        ...
        22      strcpy(buffer, str);
        gdb-peda$ p $ebp                                      ← 查看 ebp 的地址
        $1 = (void *) 0xffffdfd8   
        gdb-peda$ p &buffer
        $2 = (char (*)[100]) 0xffffdfac                       ← 查看缓冲区的地址
        gdb-peda$ quit

        1. 当 gdb 停在 bof() 函数内部时,它会在设置 ebp 寄存器以指向当前栈帧之前停止,因此如果我们在此处打印出 ebp 的值,将会获得调用者的 ebp 值。我们需要使用 next 执行几条指令,并在修改了 ebp 寄存器以指向 bof() 函数的栈帧之后停止。 gdb 的行为与 SEED 书籍基于 Ubuntu 16.04,因此书中没有包含 next 步骤。
        2. 请注意,从 gdb 获取的框架指针值在实际执行时是不同的,因为 gdb 在运行调试程序之前将一些环境数据推入栈中。 当直接运行程序而不是使用 gdb 时,栈不包含这些数据,所以实际的帧指针值更大。当你构建你的 payload 时请记住这一点。
      • 要利用目标程序中的缓冲区溢出漏洞,我们需要准备一个 payload,并将其保存在 badfile 中。 我们将使用 Python 程序来实现此目的。我们提供了一个名为 exploit.py 的示例程序,其包含于实验设置文件中。 代码不完整,你需要替换一些重要的值。

        #!/usr/bin/python3
        import sys
        
        shellcode= (
          ""                    # 需要更改
        ).encode('latin-1')
        
        # 填充内容使用 NOP 指令
        content = bytearray(0x90 for i in range(517))
        
        ##################################################################
        # 将 shellcode 放在 payload 的某个地方
        start = 0               # 需要更改
        content[start:start + len(shellcode)] = shellcode
        
        # 决定返回地址的值
        # 并将该值放在 payload 中的某处
        ret    = 0x00           # 需要更改
        offset = 0              # 需要更改
        
        L = 4     # 使用 4 来表示32位地址,使用8来表示64位地址
        content[offset:offset + L] = (ret).to_bytes(L,byteorder='little')
        ##################################################################
        
        # 将内容写入一个文件中
        with open('badfile', 'wb') as f:
          f.write(content)

        完成后运行该程序,这将生成 badfile 的内容。然后运行易受攻击的程序 stack。如果您的 exploit 实现正确,则您应该能够获得一个 root shell:

        $./exploit.py     // 创建坏文件
        $./stack-L1       // 通过运行漏洞程序启动攻击
        # <---- 中彩票了!你获得了root shell!

        在你的实验报告中,除了提供截图来展示你的调查和攻击外,还需要解释你在 exploit.py 文件中使用的值是如何决定的。这些值是攻击中最重要的一部分,因此详细的说明可以帮助老师来评估你的报告。仅演示成功的攻击而不解释为什么这个攻击起作用将不会获得很多分数。