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 |