栈
- 栈是一种典型的后进先出(Last in First Out)的数据结构,其操作主要有压栈(push)与出栈(pop)两种操作,程序的栈是从进程地址空间的高地址向低地址增长的。
栈溢出原理
指程序向栈中某个变量写入的字节超过了这个变量本身所申请的字节数,导致与其相邻的栈中的变量的值被改变,属于缓冲区溢出漏洞的一种,类似的还是堆溢出、bss段溢出等。
栈溢出发生条件:
1、程序必须向栈上写入数据
2、写入的数据大小没有被良好地控制
最典型的栈溢出利用就是覆盖返回地址为攻击者所控制的地址。例:
- 输入函数:gets(无限输入真到遇到回车),scanf,vscanf
- 输出函数:sprint
- 字符串操作函数:strcpy(字符复制,遇’
′k00′停止)、strcat(字符拼接,遇’“x00′停止)、bcopy
函数调用栈
32位函数参数在函数返回地址的上方(高地址方向),64位函数参数前6个依次在寄存器rdi、rsi、rdx、r8、r9中,第7个以上的压入栈中
- 注:需要注意的是,32位程序在进行多次函数调用时要保持栈平衡;例:callme32、callme64(讲完基础栈溢出就可以做了)
ROP
ROP(Return Oriented Programming)是栈溢出攻击的主要方法,其主要思想是在栈溢出的基础上,利用程序中已有的小片段(gadgets)来改变某些寄存器或者变量的值,从而控制程序的执行流程
利用条件:
1、程序存在栈溢出且可控制返回地址
2、可以找到满足条件的gadgets以及gadgets的地址
相关的攻击类型有ret2text、ret2shellcode、ret2syscall、ret2libc
re2_dl_runtime_resolve
原理:要想弄懂这个ROP利用技巧,需要首先理解ELF文件的基本结构以及动态链接的基本过程。
具体利用方法如下:
1、控制程序执行 dl resolve 函数:给定 Link map 以及 index 两个参数;或者我们可以直接给定plt0对应的汇编代码,这时,我们只需要一个index就足够了。
2、控制index的大小,以便于指向自己所控制的区域,从而伪造一个指定的重定位表项。
3、伪造重定位表项,使得重定位表项所指的符号也在自己可以控制的范围内4、伪造符号内容,使得符号对应的名称也在自己可以控制的范围内。
栈迁移
栈迁移:劫持栈指针指向攻击者所能控制的内存处,然后再进行ROP 一般在以下情况下用到:
- 可以控制的栈溢出的字节数较少,很难构造较长的ROP链
- 其它漏洞难以利用,我们需要进行转换,比如说劫持到堆空间。
栈迁移利用条件:- 可以控制程序流、可以控制sp指针,常见的gadgets有:pop rsp/esp、leave;ret(leave ret可以相当于mov espebp;pop ebp;ret)
保护机制
- CANARY(栈保护):是栈溢出攻击的缓冲手段(可绕过)
- NX:即No-eXecute(不可执行),开启后数据所在内存页标识为不可执行RELRO(read only relocation),RELRO为Partial RELRO时说明我们对GOT表具有写权限,为Full RELRO时GOT表不可写
- PIE(ASLR):地址空间随机化,开启后堆、栈、共享库地址随机化,程序每一次运行时,三者的地址都会变化,运行过程中不变,由操作系统决定开不开启,所以通常所有程序都有
默认情况下NX和PIE会同时工作,linux关闭pie命令如下:
sudo sh-c" echo 0>/ proc/ sys/ kernel/ randomize_va_space"