pwn系列之栈漏洞相关(三)
栈漏洞以及保护机制
1栈介绍
程序运行的时候为了实现函数之间的互相隔离,需要在进入新函数之前保存当前函数的状态,而这些状态信息都保存在栈上。函数栈的边界就是栈顶指针(sp)和栈底指针(bp)所指的区域,sp主要指esp(32位)和rsp(64位),bp主要指ebp(32位)和rbp(64位)。在函数调用时,首先将参数入栈,然后再压入返回地址和栈底指针寄存器bp,其中压入返回地址是通过call实现的。在函数结束时,将sp重新指向bp的位置,并且弹出原来的bp给现在的bp和弹出返回地址给专门记录执行指令的寄存器
32位实例如下:

64位示例如下:

光看汇编代码可能有点看不懂(大佬除外)
我们来点图片

你看这个图片,红色的那个是返回地址,红色上面的那些先是本函数参数然后是父亲函数的信息不用管它,这个红色的非常关键(划重点)

接下来这个红色的存的是父亲函数的栈底位置,然后呢我们的栈底指针就指到这啦,这个位置也挺重要的,因为在ida中变量旁边都会标注这个变量离栈底多远,方便我们覆盖到它

我们再来看看这个,现在这个红色的呢就是我们的变量了,这个是将所有调用函数定义的局部变量(注意只有局部变量)根据大小分配一下空间,这个也被我们称为溢出区,那我们想想,我们定义了一个长度为10的字符串,可是我们输入的时候可以无限输入,那么我们是不是可以多输入点,那么我们多输入的就会接着输入到栈上的位置,那么我们是不是可以修改刚刚说的返回地址,然后让他返回地址变成我们想要的
然后我们执行完了这个函数后就会开始清空栈了

你们看,我们先把变量给清出去了,当然并不是删除,而是指针移动,这样也相当于清除,

然后我们弹出父亲函数的栈底把他赋值给ebp,你看看这个指针位置是不是回到了原来父亲栈底的位置

然后再把返回地址的值赋值给eip,再弹出,接下来呢就会去执行eip所指的指令了,刚刚我们说了,可以修改返回地址,那么我们不就可以控制程序进行的流程去执行我们的shellcode了嘛,这个就是栈溢出的基本
2、保护机制之canary
1、canary保护机制介绍
canary中文翻译金丝雀,据说是当时进入地窖的时候会放只金丝雀进去看看有没有毒,然后呢程序员就想到了,我们ebp前面放个金丝雀(就是不知道哪里拐来的一串数字,以00结尾),然后你要是栈覆盖肯定会修改掉我们的金丝雀,那它就被毒死了,我们在弹出局部变量之后就会比较一下金丝雀,看看它值有没有改,有改掉的话就终止程序,这样你就没办法pwn了
2、canary绕过
既然有保护,那咱肯定有绕过的方法,最直观的就是它既然要确认一下这个数字对不对,那么我们覆盖的时候特地地给他覆盖成正确的值不就好了吗?那我们就要知道原来它是多少,它出现在两个地方,一个是栈上,还有一个是我刚刚说的不知道哪里拐来的(这个是能找的,不过难度太大了,咱还不会),所以我们果断去栈上找,那我们就要一个输出函数帮我们输出出来,然后呢我们可以用格式化字符串漏洞输出,这个我们之后会讲,来个比较简单的就是printf函数,printf("%s",a),这个是把a变量当做字符串输出,遇到/0的时候停下来,那么我们只需要把溢出区全部覆盖成不为/0的东西,然后我们不就会把canary给输出出来了吗,当然要注意我们一般是小端序(不知道的可以去看看第一篇博客)存的,所以我们记得要把canary后面两个0也给覆盖然后获得canary的时候记得把后面两个0覆盖回去,因为后面的两个0会变成/00这样的形式
当然还有一些奇奇怪怪的绕过方式,比如说劫持got表之类的,因为有点难,挺多东西没学,所以后面再补,下次一定
3、pie,随机地址保护
1、pie介绍
顾名思义,就是地址每次运行时随机的,因为我们覆盖返回地址,那你得知道我们要执行的代码要去哪对吧,所以你要知道shellcode的地址,那你开地址随机化,你不就没办法知道了吗?具体来说就是ida中只给你地址的后三位数字,然后程序运行的时候会随机出一个程序基地址,然后程序基地址后三位是0,这样两个相加就是真实地址了
2、pie绕过
我们可以有两个思路,第一个就是我们一旦泄露出一个变量的地址,那这不就稍微加减计算一下就可以得到所有的地址了吗,第二个就是我们覆盖地址只覆盖2字节,也就是4个数字,也就是说我们第4个数字不知道,1个数字那不就10种可能嘛,一个个试,不过呢这个方法只适合小端序的程序

浙公网安备 33010602011771号