栈溢出漏洞

1.基础知识

函数与函数栈

栈是一种先进后出的特殊数据结构,用于存储 程序在运行时的临时数据和地址,用于支撑函 数的运行和嵌套调用。

栈的分配是由程序编译时确定下来的,无法由 程序员控制。

栈中存储着线程或者进程的局部变量

不同的进程或线程的栈处于不同的位置,在程 序正常运行时不同线程和进程之间不能互相访 问彼此的栈地址

函数传递参数:

函数都要通过传递进去的参数来确定其具体的 工作内容,而函数间的参数也是通过栈来传递 的 。我们看如下例子:

32位操作系统,32位的程序反汇编结果

函数需要传递的参数是直接被压入栈中的的方式,是直接 写在栈上的。

 这时ebp的下方依此是父函数的栈底地址,程序返回地址,传递的 参数1,传递的参数2,…。

由于在函数调用前通过push指令向栈中压入了数据,使得栈顶被 抬高了

所以在函数调用结束以后,将通过add esp 0x10这条指令,即增 加esp来恢复函数调用前的esp。

64位操作系统,64位的程序反汇编结果

传递的参数不再直接写在栈上,而是通过寄存器传递

这些寄存器分别是rdi, rsi, rdx, rcx, r8, r9,超出寄 存器存放数量的数据才继续在栈上存储。

64位程序反汇编结果如下:

函数传递参数不再向栈中压入,而是将参数赋值给edi 和esi。由于没有压栈的操作,所以函数执行结束以后 也就没有相应的恢复栈顶的过程了

栈中数据是如何被调用的

函数可以调用的栈上的参数主要分为两部分:传递 的参数和程序里的局部变量

这里的printf函数的两个参数分别是父函数传递过去的第 一个参数和第二个参数 最上边的mov指令的参数就是局部变量:代码中的数组a

不论是函数的参数还是局部变量,都是通过统一的 方式:栈底指针+偏移量来读取的

2.漏洞原理

由于程序员在编写代码的时候,对于数组和字 符串等变量的边界没有进行足够的检测

栈溢出漏洞的产生总会伴随着以下两个事件 :

1. 程序向栈上写入一组数据   2. 并且写入的数据总长度没有进行有效的检测。

 导致攻击者可以通过向其中写入过多的数据,从 而覆盖掉栈上的其他变量和原本不能访问到的 地址,如返回地址等。

栈溢出的危害 :

修改父函数栈底地址 修改返回地址 修改SEH链表指针

3. ROP基础知识

GOT与PLT

GOT(Global Offset Table,全局偏移表) :存放程序要引用的全局变量或函数的地址

PLT (Procedure Linkage Table,过程链接表):
Linux ELF文件中用于延迟绑定的表 每一项都是一小段代码,对应于程序要引用的一个 全局函数。

程序首次调用函数时,会先从plt表中查找这个位置。 第二次调用时,因为第一次已经写入到plt表中了,所以会直接从plt表中调用。

 

4. ROP漏洞原理

ROP的全称为Return-oriented Programming(返回导向编程) 

计算机安全漏洞利用技术:

1. 绕过可执行空间保护、代码签名等安全保护机制执 行恶意代码

2. 通过控制被调用的堆栈对程序的控制流进行劫持, 完成某些特定功能 。

多与栈溢出漏洞结合利用

覆盖栈帧的返回地址和其他变量,将控制流转移到 期望的地址中

ret2libc

加入了DEP(Data Execution Prevention)和NX(No execute)保护之后,拒绝执行堆栈上的任何代码。 ret2libc是ROP技术的一种,通过将返回地址覆写为libc中的函 数绕过NX保护。

gadget

64位处理器的发展,改变了函数的调用约定,要求 函数的前6个参数保存在寄存器中,如果还有更多 的参数才会保存在栈中。 想继续给函数传递参数将不能通过简单操作栈来操 作函数,还需要操作寄存器。由此造成ret2libc也 变的难以成功。        gadget是从可执行文件或共享库中获取的以ret为 结尾的指令序列。这种ROP技术寻找能够将栈中的 值pop到寄存器的指令片段,由此构造函数参数。

5. ROP漏洞实验

一、程序流的劫持

下面这个函数,我们关掉了DEP和NX来编译。我们使用直接覆盖返回地址,来进行劫持程序执行流程。

vuln( )函数中,buf数组的大小为128字节,但是在 read时最多能够读入256字节,容易造成缓冲区溢出, 利用这个漏洞对程序流进行劫持,执行构造好的 payload。

那么我们的具体的思路是,把payload写入buf数组中,并利用缓 冲区漏洞将返回地址修改为buf数组的地址,vuln( )函 数返回之后,就会到buf数组中执行恶意代码。

payload中除了shellcode外,要填充足够长度以 覆盖返回地址。Buf数组的地址为ebp-0x88,即buf 距离ebp有0x88字节,ebp距离返回地址又0x4字节 (32位时),覆盖返回地址前先填充0x8c个字节。

Shellcode中要填充的长度解决了,要确定覆盖返 回地址的内容,即将要覆盖为的buf数组的地址 ,在脚本中进行gdb调试获取buf数组真实地址。

我们的payload如下:图中的返回地址我们是乱写的。。。

二、 ROP绕过NX实验(32位)

本实验在实验一的基础上开启了NX保护,分别分析32 位和64位程序。 

只开启Canary栈保护:gcc rop1.c -o rop2 -m32 fno-stack-protector 查看rop2进程栈的权限为rw,不可执行,如下图:

不能在栈上执行shellcode,但程序中用到了libc库 中的read和printf函数。libc.so中保存了大量的可 用函数,考虑调用system(‘/bin/sh’)来获取shell。

获取system函数地址和字符串’/bin/sh’地址。  由于关闭了ASLR,system函数在内存中地址不会 发生变化;libc.so中也包含了’/bin/sh’字符串。 用gdb查找这两个地址。第一张图是查找system()函数的地址

第二张图是查找 /bin/sh  字符串的地址:

接下来,我们就要来构造我们的ROP:

最终的payload就是’a’*0x8c+rop。其中0xdeadbeef是 system函数的返回地址,因为获取shell后没有别的操作了, 就随意写一个0xdeadbeef作为返回地址。返回地址后面是 system函数的参数,‘/bin/sh’的地址。

三、gadgetROP实验(64位)

不同于32位程序,参数存放在寄存器中,多于6个参数 才会放在栈上。 

由于参数不会直接放在栈上,需要寻找类似于pop rdi;ret的gadget,将参数从栈中弹出到rdi寄存器 后,返回到返回地址处继续执行。本题在栈中事先 压入参数’/bin/sh’地址和system地址。

查找gadget:借助ROPgadgets工具,在libc.so 中查找可用的gadgets。先确定rop3程序使用的共享库 。ROPgadgets查找结果如下,其中0x21102是相 对于libc的偏移。我 们用之前vmmap获取的libc.so首地址加上这个偏 移,就得到了最终的地址。 需要注意的是,64位系统编译出的buf地址也发生了改变 ,需要再次查看;sys_addr和binsh_addr地址的获取和 32位系统下方法一样,但是值发生了变化 。

我们可以从上图中看到我们的ROP的构造思路:首先使用 pop rdi + /bin/sh  这一组指令,我们将/bin/sh这个字符串的地址弹出到rdi中,然后再将ret的返回地址覆盖位 system()函数的地址,这样system()执行时,他的参数就会从rdi中找到,也就是\bin\sh。这样就能执行system()函数。

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值