现在我们来介绍一下最简单的栈溢出吧
首先先来下载一个程序
下载完之后,我们用32位的ida打开
反编译看到main函数
这里很明显就有一个栈溢出
我们把程序复制进虚拟机中
然后会显示./pwn_level1: No such file or directory
这是因为我们装的是64位系统,而这个程序是32位的
我们可以执行下面的命令,安装一些东西来运行32位程序
1 | apt-get install lib32z1 lib32ncurses5 |
安装完之后,再运行程序
这样就能正常的运行和调试了
第一步,计算控制程序eip所需要的字符数量
到了这里,需要有一些汇编的基础
这里给下几篇简单入门的教程,不过最好可以系统的学一下,书我推荐王爽的《汇编语言》
看完上面那两个教程之后,我们来计算一下控制程序eip所需要的字符数量
cd 到Desktop,然后输入
1 | vim level1.py |
对了,这里还需要简单学一下vim的操作
简单学一下基础就可以了,不用学得太深
我们在level1.py里面写入下面的代码
1 | from pwn import * |
保存,然后在terminal下面运行
1 | python level1.py |
运行之后会出现
在gdb那个窗口下面输入c,然后回车
发现程序停在了这里,这是因为我们栈溢出,控制了eip,eip变成0x65616161,但是这个地址跳转不了
1 | print(cyclic_find(0x65616161)) |
在代码后面加上这句
然后再运行一次,打印出13,看来偏移是13
所以我们只要填充13个字符就能控制到eip了
第二步,跳转到后门
我们在ida里面看到
有一个后门函数,地址是在804849A开始的
我们如果想跳转到这里,只要填充13个字符,再加上p32(0x804849A)就可以了
所以最终的payload是
1 | from pwn import * |
运行一下,在运行脚本那个terminal输入ls,可以看到打印了桌面的文件
简单了解一下这样做的原理
我们用gdb调试程序,在80484C4这个地址下个断点
这里是call vulnerable_function
输入r运行
我们可以看下这个时候,ebp和esp的值
再输入s
可以看到esp减少了0x4
而且栈顶现在是0x80484c9,是call vulnerable_function下一条指令的地址
所以我们可以知道
1 | call function |
等价于1
2push eip+x
jmp function
这样做是为了调用完函数之后,能顺利的返回
然后我们看下vulnerable_function函数里面的东西
前3行是将ebp压入栈,然后将当前的esp转移到ebp,再将esp减去0x18
最后三行是将esp加上0x10
1 | leave |
1 | retn = pop eip |
然后我们将这个流程用图示来显示
这个是一开始停在call vulnerable_function那里的栈的大概示意图
这个是刚进到函数里面时的情况
这是执行完push ebp后的情况
这个是执行完mov ebp,esp之后
执行完sub esp,0x18后
然后我们可以在gdb调试一下,然后发现执行完add esp,0x10之后,栈的状况又回到了
执行完leave后
执行完ret后
这个是正常的流程,假如我们用cyclic(0x100)来栈溢出呢?
执行完read之后就变成这样了
原本保存ebp和返回地址的地方现在被我们输入的内容给覆盖,这里可能有点画得不准确,大概明白就行
执行leave之后就变成
再执行retn就变成
这个就是为什么我们栈溢出能控制程序eip的原因了
我们只需要把0x65616161替换成我们想跳转的地址,就能跳转到那个地方