首页 > 代码库 > buflab实验

buflab实验

一:准备工作

1,三个二进制文件

bufbomb:一个有缓冲区溢出漏洞的程序

makecookie:可以根据用户不同的userid生成的唯一的cookieuserid不同,cookie不同,所要解决的方法就不同。

hex2raw:使编写的缓冲区利用代码的转换为一个字符串的格式,只有经过转换以后才可以输入到getbuf

2,生成cookie的方法

技术分享

3bufbomb函数原型及介绍

技术分享

getbuf函数类似于gets函数,它是读入一段字符串,字符串以\n或者eof表示结束,并把存储起来,但是getbuf提供的缓冲区只有32个字符大小,但是getbuf本身又对输入的字符是否超过缓冲区大小进行安全检查,从而带来了缓冲区溢出漏洞。

使用方法:

 技术分享

参数:

-u,确保不同的userid用不同的cookie

-n为了level4,栈基址随机化模式的时候使用。

-s上传到服务器进行打分

4,提交方式

 技术分享

5,提示

 技术分享

二:实验

Level 0

实验描述:

test函数调用getbuf函数,调用完以后还返回test函数,现在我们要做的是调用getbuf函数后,通过输入我们的exploit,使得调用完以后不返回test函数了而是执行smoke函数。

test函数:

 技术分享

解决方法:

反汇编:

 技术分享

在反汇编结果中找到getbuf函数,smoke函数:

Getbuf函数:

 技术分享

画出test函数调用getbuf函数的栈帧结构:

 技术分享

由反汇编结果可知,给输入的字符串分配的空间是从%ebp-0x28开始的,换为10进制就是40个字节,而返回地址是在%ebp+0x4处,push %ebp本身又占了四个字节,所以结构为:

返回地址

4字节

Getbuf的,从%ebp到输入字符串的空间为44个字节

 

Smoke函数:

 技术分享

由反汇编可得smoke函数的入口地址为0x08048e0a

综上,我们需要做的就是把上面的44个字节随意填满(不要填换行),然后把原来的返回地址改为smoke函数的入口地址。0x0a是换行\nASCII值,所以不可以输入,那么我们就输入0x8048e0b来代替。

 技术分享

技术分享

Level1

实验描述:

test函数调用getbuf函数,调用完getbuf以后不返回getbuf的调用者test而是去执行fizz函数。fizz函数要求传入参数,参数必须是cookie

fizz函数:

 技术分享

解决方法:

反汇编bufbomb找到fizz函数:

 技术分享

level0类似,通过上一题已经知道了栈帧结构,所以我们需要做的还是把那44个字节填满,然后再填写fizz函数的入口地址(0x08048daf)用来覆盖原来的返回地址。

关键:找到fizz函数的参数从栈中的什么地方传入的,然后我们把我们的cookie写进这个fizz会获取参数的地方。实参只在主调函数中有效,形参只在被调函数中有效,我们要做的就是修改实参,它的位置就是在返回地址的上面4个单位。而返回地址已经被我们破坏,会默认它上面四个字节为返回地址,然后再向上4个字节来取参数。

 技术分享技术分享技术分享技术分享技术分享

                                           执行leave

所以答案如下:

技术分享

技术分享

Level2

实验描述:

执行完getbuf()后,不返回到test,而是去执行函数bang,但是区别是bang也要传入参数,且参数是是一个全局变量。

bang函数:

 技术分享

解决方法:

反汇编:

 技术分享

bang函数的反汇编代码

 技术分享

可以看到,bang、函数的入口地址为0x08048d52.

接下来要做的就是改变全局变量global_value的值,使他的值为cookie

由汇编代码,第四行 mov 0x804d10c,%eax 可以知道,global_value存放的位置是0x804d10c

由此写下汇编代码:

首先把我们的cookie写到全局变量的地址中,然后在把bang的入口地址入栈,通过ret指令来执行bang函数

 技术分享

然后把.s文件变成字节码:

技术分享

技术分享

利用gdb调试找到我们的exploit的地址了,用我们的地址来覆盖返回地址,从而执行我们的代码。

 技术分享

所以答案如下:

 技术分享

技术分享

Level3:

修改getbuf()函数的返回值(正常状态为0x1)为你的cookie,然后让函数正常返回到test.

解决方法:

函数调用栈,函数调用结束以后,栈被释放,而返回结果会放在eax寄存器中。test不需知道调用的getbuf是怎么执行的,只需要到eax寄存器中去取返回值,所以可以在getbuf执行完以后,再把eax寄存器中的值动手脚修改为cookie

有关栈的恢复:需要两个部分,一个是ebp一个是eip的地址,一个是恢复poptest的原ebp,所以在破坏之前,用gdb调试出来test的原ebp是多少记录下来,恢复的时候在赋值给它。

gdb来调试得到ebp的值:

 技术分享

testebp0x556839f0

eip的地址,就是返回地址,也就是test中在callgetbuf函数的下一条指令的地址:

 技术分享

call的下一条指令的地址是0x8048e50

所以.s代码:

 技术分享

 

%eax的值改为cookie

%ebp的恢复,改成0x556839f0

把下一条指令地址0x8048e50压入

返回

.s文件变成字节码:

 技术分享

所以答案是:

 技术分享

第一句 第三句 ret

填充0

%ebp恢复

自己的返回地址

 技术分享

Level4

调用getbufn函数,

其缓冲区大小为512个字节, 且每次栈的位置都会变化

nop只是执行eip自加1不进行其他的操作。在无法猜测的时候,只需要找到最大的地址。

解决方法:

反汇编,查看getbufn反汇编结果。

 技术分享

buf的首地址为-0x208%ebp)为十进制520个字节大小。

每次运行testnebp都不同,所以每次getbufn里面保存的testebp也是随机的,但是栈顶的esp是不变的,我们就要找到每次随机的ebpesp之间的关系来恢复ebp。我们先通过调试来看一下getbuf里面保存的ebp的值的随机范围为多少。

技术分享

技术分享

技术分享

ebp的值              减去0x208buf的首地址

0x556839c0             0x556837b8

0x556839f0             0x556837e8

0x55683970             0x55683768

0x556839f0             0x556837e8

0x55683950             0x55683748

testn的反汇编代码:

 技术分享

call getbufn的下一条指令的地址为0x8048ce2

此外,还可以看到,mov  %esp,%ebp 此时espebp相等

push  %ebx  此时ebp=esp+0x4

sub   $0x24,%esp 这个时候执行完后,ebp=esp+0x28,这就是espebp每次的变化关系,通过esp来恢复我们的每次的ebp

由此写出以下汇编代码:

 技术分享

.s文件变成字节码:

技术分享

技术分享

509nop

15个字节码,覆盖篡改保存ebp

Buf首地址覆盖返回地址,是可能的最大的首地址

技术分享

三:总结

要想做明白这个实验,就需要弄懂缓冲区溢出原理,以及堆栈的过程,函数调用的实现过程,函数传参的底层实现等问题。并且,光理解原理也还远远不够,还需要会应用。本次实验的几个level是逐步跟进的,一点一点的深入。Level0只需要理解原理,进行覆盖地址,level1是修改参数,level2是修改全局变量,level3是恢复栈结构,level4是在level3第基础上实现随机化。在实验的过程中还需要注意避免输入换行对应的数字码等等。

另外,在做本次实验的过程中还遇到一个比较特别的问题,这个问题很隐蔽,难以发现,花费了很久才找到。在将自己写的code4.s汇编的过程中,出现了机器码生成错误的问题,mov指令对应的机器码应该是b8,而在我的机器中,level4,却生成了a1,至今还不知道为什么会出现这个问题。在与其他同学交流的过程中,发现其他同学也有过这种情况,是push指令对应的机器码发生了错误。

截图如下:

技术分享

 

buflab实验