PLT与GOT

PLT(Procedure Linkage Table)与GOT(Global Offset Table)

  • ELF文件调用库函数时,存在PLT入口和GOT入口

    • 注意,静态链接库没有这些
  • 是一种在libc.so等动态查找外部函数地址的机制

    • PLT解析外部地址,之后保存(缓存)在GOT中

  • 如果有PLT入口,就可以在不知道函数实际地址的情况下去调用它

    • PLT作为代理跳转到真实地址
    • PLT中的实体是汇编代码,作为缓冲函数存在

什么时候存在PLT?

  • 函数的PLT入口存在的条件是,在预编译代码中使用该函数
    • 某些情况下,gcc优化或者FORTIFY_SOURCE会变更函数,这里暂不考虑
  • 也就是说,经常使用的printf存在PLT入口

PLT在ELF中的固定地址

  • 即使开启ASLR,地址也是固定的
  • 但是,如果PIE也开启,它会变化

如果有PLT入口,就能够进行ret2plt

可以通过objdump,readelf等检查PLT/GOT入口的地址(以下是示例)

objdump -d -M intel stack3 | grep "@plt>:" -A1

左边是PLT,右边是GOT

ret2plt, ret2pop

ret2plt

  • 如果PLT与参数,返回地址等一起加载于stack上,则无法与正常的函数调用区分开

    • PLT不会检查调用来源

  • 例如使用mmap的PLT,获取具有RWX权限的内存(假设存在mmap@plt)

    • void*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset);
  • 但是,这并不能读取额外的内容

  • 我们想在mmap之后执行read(也就是说想要多次调用函数)

ret2pop

  • 使用pop从stack中除去参数

    • pop = 从stack中取出一个元素(4个字节),存放在寄存器中
    • stack减小(=esp指向的地址增大)
    • 代码中pop×N;找到ret(也可以用add esp,同样N次)

  • 像这个例子,pop6次,称为p6ret, pop6ret等

NX绕过方式1 - ret2plt链

  • 熟练使用ret2pop,可以构造出ret2plt链

  • 例如

    • 使用mmap在合适的固定地址获取RWX内存
    • 对该区域使用read()读入shellcode
    • 返回到该区域执行shellcode

  • 按照mmap -> read -> shellcode的调用顺序

    • 并不是直接执行shellcode
    • 换句话说,这也是一种Stager

ret2plt - 信息收集方法

  • 对于”pop×N; ret”地址的查找,可以使用gdb-peda秒杀

    • 使用gdb读取该二进制程序
    • start(或者run),然后输入ropgadget

  • 例如pop4ret是这样的命令列表,确实是4个pop之后ret

目前为止的技术总结

技术 推荐练习的问题
nop-sled CodeGate 2013 - pwn100
brute force CodeGate 2013 - pwn100
ret2esp CSAW CTF 2012 - pwn300 - 4842
stager Hack.lu CTF 2012 - pwn300 - braincpy
ret2plt Plaid CTF 2013 - pwn200 - ropasaurusrex
ret2pop Plaid CTF 2013 - pwn200 - ropasaurusrex