栈劫持(栈迁移)

发布于 2023-03-28  353 次阅读


前序:

栈劫持概念:当存在栈溢出但溢出长度不够容纳payload时,就需要采用栈劫持。一般这种情况下,溢出时仅能够且刚好覆盖ebp,ret,但还需要设置参数。这时原来的栈空间不足,所以要构造一个新的栈空间来放payload,可以理解为劫持程序执行流跳转到自己构造的payload的栈中执行程序,也叫做栈迁移。

原理:函数调用时形成栈帧,栈执行命令时是从esp指针处向ebp指向的位置依次执行,正常情况下退栈的操作是:esp指向ebp的位置,ebp指向ebp里的内容所指向的位置;当遇见leave ret 命令时,就相当于执行mov esp,ebp ; pop ebp;(手动分隔) ret;pop,eip ;作用就是将ebp指向的位置给esp,ebp指向ebp里的内容所指向的位置。 如果我们提前控制了ebp的值那么当遇见leave ret指令的时候,ebp的值就会给到esp,由于esp里的内容会给到eip,所以就可以通过控制esp里的内容,实现控制程序执行流。

漏洞利用

存在栈溢出,但可利用栈空间不够,采用栈劫持

栈溢出长度不足来直接 ROP,把栈迁移到别的地方来构造新的ROP链,一般利用leave_ret来进行栈迁移

leave

//move esp ebp 将ebp指向的地址给esp

//pop ebp 将esp指向的地址存放的值赋值给ebp

ret

//pop eip 将esp指向的地址存放的值赋值给eip

举个栗题:

ciscn_2019_es_2

32位程序,并且没有开启栈保护。

ida分析main函数。

调用read函数向s中读入0x30长度的数据,但s缓冲区长度为0x28,只剩下0x8长度供我们覆盖ebp和返回地址,
但system参数不是/bin/sh,还需要来设置system的参数,ebp覆盖需要0x4长度,返回地址需要0x4长度,设置参数还需要,0x8长度明显不够,这时就要采用栈劫持方法。

但是栈劫持的前提是知道缓冲区s一开始读入的栈地址,但s是一个定义在函数里的局部变量char s[40],我们无法知道其地址,但是我们知道s地址与函数基地址ebp的偏移量是一定的,不会变化,因此只要知道了ebp地址就能知道s的地址,也同时可以通过计算知道我们输入的内容的具体位置。

继续思路:

存在栈溢出漏洞,但是由于可供溢出的空间太小,我们选择采用栈劫持,采用栈迁移必须要使用到leave, ret组合指令。
程序中存在system函数,我们可以在内存中存放一个字符串/bin/sh,并且我们可以泄露ebp地址来计算出该字符串在内存中的地址,我们就可以构造并执行system('/bin/sh')。

那么首先要做的就是利用第一次read泄露ebp地址。

1:第一次read泄露ebp地址

注意要用send来发送payload,若用sendline的话 发送到payload后会多一个”\n“,破坏ebp,导致接收到的不是ebp的地址。

我们都知道,ebp(我们将此称为ebp1)所指向的内存中存放的是上一次压入的ebp(我们将此称为ebp2),我们可以通过第一次的read函数和printf函数将该内容打印出来,但是打印出来之后的ebp2无法直接来使用,原因是我们知道ebp1到buf的距离是0x28,而我们不知道ebp2到s起始地址的距离是多少,这时候我们需要动态地调试一下程序,找到ebp2到s起始地址之间的距离。

可以看到ebp1的地址为0xffffd328,ebp1存放的内容为ebp2的地址0xffffd338,并且能看到我们输入的aaaa,距离ebp2的偏移量为0x38,那么ebp的真实地址减去0x38即为s的起始地址。


2:第二次read利用leave ret劫持程序流到s起始地址,这里也就是劫持到ebpaddr-0x38的地址。

aaaa是第二次leave_ret 时 pop ebp中赋给ebp的值 ,之后pop eip 即ret=system,"aaaa"是执行system后的返回地址,ebp-0x28就是/bin/sh的地址,可以通过gdb调试得到。

现在来找下leave ret 地址:

也可以这样找

完整exp如下:

剖析一下程序流执行:

发送的payload2数据流的初始状态:

第一次执行leave_ret : mov esp ,ebp

第一次执行leave_ret :pop ebp

pop ebp之后,ebp指向了地址ebpaddr-0x38的位置,也就是s的起始地址。此时esp加4,向下移一格,esp=esp+4指向leave_ret。

第一次leave ret :pop eip:

eip寄存器存的是leave ret的地址,程序接下来又要执行leave ret 开始第二次leave ret:

第二次leave ret :mov esp ,ebp

第二次leave ret:pop ebp

第二次leave ret :pop eip


穿过云层我试着努力向你奔跑