ret2libc对策 – ASLR

ASLR(Address Space Layout Randomization)

  • 主要是地址随机化
  • 现代Linux内核中默认开启

  • ret2libc成功的原因是,libc之类的读取地址是固定的

  • 因此如果每次运行时,libc之类的地址随机,是一个比较好的方式

  • 1
    2
    3
    4
    5
    miao# echo 2 > /proc/sys/kernel/randomize_va_space             
    miao# ldd stack6 | grep libc
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7d94000)
    miao# ldd stack6 | grep libc
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7dd4000)
  • 如上所示,每次运行地址都不同

ASLR绕过 - 方式1

ASLR中,并非所有地址都是随机的

  • PLT/GOT之类的地址是固定的,可以利用这一点

    img

  • 实际的内存映射大概是这样

  • 红框中是固定地址,PLT和GOT在这里

    img

  • GOT中存储ASLR随机化后的地址

  • 因此出现了有名的的根据这些信息计算libc地址的方法

GOT leak

  • GOT中包含重要地址

    • printf的外部地址

    img

  • 如果能够读取更新后的(printf@got.plt)这个值,那么就可能计算出libc.so的加载地址

GOT overwrite

  • 因为GOT位于RW区域,因此可能覆写

    • 使用某种漏洞将printf的GOT替换为另一个地址

    img

  • 默认情况下可以重写GOT的值。通过某种方式将其修改为其他函数的地址,当调用printf函数的时候会调用修改后的其他函数。这种方式叫做GOT overwrite。

NX+ASLR绕过1 – memory leak + ret2plt + GOT overwrite

  • 假设存在栈溢出

  • 通过ret2plt等方式,显示出printf@got的地址(write)

    • 泄漏地址
    • 攻击者可以通过泄漏的地址计算libc的基地址,加上偏移量计算出system的地址

    img

  • 通过ret2plt等方式,向printf@got读入system的地址(read)

    • 下次调用printf的时候,会实际调用system

    img

其他的泄漏

除GOT leak之外,其他能够泄漏libc地址的情况

  • 通过(Stack/Heap)缓冲区溢出读取造成的泄漏
  • 无序参考,负数索引,类型(主要是结构体)的混淆导致的泄漏
  • 字符串末尾无终止字符造成的泄漏
  • 格式化字符串问题
  • Use After Free,Double Free
  • 条件竞争等

通过泄漏想要获取的值

  • stack区域上__libc_start_main的返回地址
  • 指向bss区域中与libc相关的变量的指针(例如FILE *之类的)
  • 指向堆管理区域(元数据)中的bin/fastbins的指针
    • bin/fastbins无任何连接时适用
  • 与libc相关的所有其他地址

其他的指针

GOT之外其他可写的函数指针

  • C++ class的vtable
    • 在C++中,class有method,在内部实现了一个函数指针表
  • .fini_array(旧的.dtor区域)
    • gcc编译具有__attribute__((destructor))的函数时,会在这里注册
  • 由atexit()注册的列表
    • 明确指定析构函数时的函数指针
    • 但是它与Thread Local Storage中奇怪的值XOR(PTR_MANGLE)之后进行注册