课程实验报告课程名称:计算机组成与结构实验项目名称:buflab专业班级:姓名:学号:指导教师:完成时间:2016年5月16日信息科学与工程学院实验题目:buflab实验目的:如writeup中所说:这个实验帮助你具体地理解IA-32机器的调用过程和堆栈的组织,它会涉及到使用一系列缓冲区溢出攻击目录下的bufbomb文件这个实验中你会体验到第一次使用操作系统和网络服务下开发安全缺陷的方法,我们的目的是帮助你理解程序的运行时所发生的内容,以及理解这种安全缺陷环境,从而帮助你避免在写代码时误入歧途。实验环境:ubuntu14.04虚拟机、gdb工具实验内容及操作步骤:首先按照readme文件中所说,在linux系统下解压压缩文件,得到三个文件接下来开始阅读一下11页的pdf文件,明确这个实验的目标。结合buflab-writeup中的介绍,分析一下三个文件的作用:Bufbomb是要攻击的文件,makecookie是基于个人id生成的身份数据,我在本次实验中使用的ID为Mrspot,hex2raw文件协助进行字符串间的转化(由于还没有做实验,所以只能基于字面意思理解)接下来,按照说明文件中的方法,生成一个属于自己的cookie生成我的身份信息就是0x2026ed1c,文件中接着说,5个缓冲区攻击中的其中4个,我的目的都是要去使我的cookie值出现在它平时不该出现的地方。接着看下去,这个bufbomb程序读入一串字符串,这个字符串是根据下面定义的getbuf函数来实现的:Gets函数和标准库函数内的gets类似,读取一串以\n结尾的字符串,并将其存储于特定目标中,在getbuf函数中,这个目标地址是一个有32个字符型大小的数组。然而这个Gets的缺点是不能判断buf是否足够存入所有的输入值,它只是简单地将整个字符串赋值,经常会超出分配的存储空间。当错误信息出现时,缓冲区溢出导致程序被破坏,使存储器路径错误以下有几个控制语句:-ucookie,使用这个语句是要确保不同的人使用不同的ID做题,并攻击不同的地址。-h用于打印这几个操作的内容,-n用于Level4关卡,-s用于提交你的解决方案到服务器中。我们可以把自己写好的exploit文件写成txt格式,并用以下方式应用到bufbomb中去:另外,还有一些重要的注意事项:Exploit文件中在任何位置都不能能包含0x0A,因为这是ASCII码的换行编码,如果写了这个语句,Gets会认为你想终止字符串HEX2RAW期望输入的是空格隔开的两位16进制数,如果你想创建一个值为0的,就要输入00,另外,注意小端法输入。以上就是writeup文件针对这个实验的说明,读完大概明白了几个文件的意思,但是要去做还是一头雾水。所以我决定继续跟着这个writeup的指导看下去。Level0接下来就开始了Level0,说的是getbuf被bufbomb中的test功能调用,而这个test的c语言代码如下:Getbuf函数如下:Smoke函数如下:题目要求为:当test函数在调用getbuf函数时,本来这个程序会按照惯例返回test函数,但是我们要做的就是当getbuf函数执行结束时,返回到smoke函数中去。几条建议为:1反编译bufbomb,2小心字节顺序,3使用gdb确定自己做的对不对,4getbut在堆栈中的位置取决于gcc版本。下面开始解题:第一题很简单,应该是给大家入门的一个导引,大致的目的就是输入一个过长的字符串,把getbuf函数的返回地址覆盖掉,改成我们想要的smoke函数的地址,那么就可以在getbuf函数运行结束时返回到smoke函数中去。下面根据反汇编的代码来看这个“过长的字符串”到底要写点什么。在进行反汇编的时候可以使用objdump,但是我习惯使用gdb中的反汇编命令disassemble,虽然方法不一样但是都得到了反汇编代码:考虑这个题目的要求,是在执行完getbuf之后返回到smoke函数中去,那么考虑getbuf的堆栈结构:这里是把ebp-40的地址传给esp,所以针对调用的函数Gets,写数据的顺序就是从ebp-40开始向上写,而当前这个位置距离getbuf的返回地址有48字节,所以我们只要在前44个字节上随便写点什么,在原来的返回地址的那四个字节改写为smoke函数的入口地址即可。下面反编译smoke函数寻找入口地址可以看到,smoke函数的入口地址为0x08048e0a,那么问题来了:如果在这里输入0a,会被gets函数判断为换行符\n,从而结束字符串输入,导致这个地址错误,所以肯定不能输入0a但是本着实验精神我还是做了一下尝试,编写level0.txt如下:下面进行检测是否可以完成需求:显然报错,说明不能写入末尾为0a的地址,那么怎么办呢?我选择使用入口地址的下一行即mov%esp%ebp的地址0x08048e0b来代替,因为本来如果执行smoke函数,它会和getbuf函数一样重新建立一个栈,但是这里我们只需要去执行这个函数,并不管之后发生了什么事,况且这个push的ebp,是test函数的ebp,而这个ebp我们在上一步输入44字节的值时已经破坏掉了,所以也不会有什么影响。这个思想在writeup中也有所体现:Notethatyourexploitstringmayalsocorruptpartsofthestacknotdirectlyrelatedtothisstage,butthiswillnotcauseaproblem,sincesmokecausestheprogramtoexitdirectly.说的就是这个操作。所以,这题的答案就是:(前44字节的值随便填,只要不是0a就可以,后4字节为0b8e0408)这次通过了检测,成功返回到了smoke函数。Levle1通过了上一题,大概明白了这次实验的概要,所以下面做起来也就得心应手了。这个level1说的是:bufbomb中也有一个函数叫fizz,代码如下:类似于level0,我的任务就是让test函数返回到fizz,而不是test,然而,这次我们要传入自己的cookie作为参数。建议:要明白,程序不会真的调用fizz,而是仅仅执行它,这句话暗示了你在堆栈哪个地方要去放置自己的cookie。下面开始解题:先反编译fizz函数,现在回想一下怎么通过上一关的,上一关的过程是这样的:Gets函数从距离返回地址48个字节处开始写数据,我先写了40个字节(这40个字节是随便的),再用4个字节覆盖了旧的ebp(即test函数中的ebp),再用4个字节的地址(即smoke函数的入口地址)覆盖了getbuf函数的返回地址,使test函数运行结束后,ret语句回到的不是本应的test函数,而是去到了smoke函数。、下面仍为getbuf函数的堆栈结构下面再看看fizz函数的堆栈结构:可以看到,fizz函数会去ebp+8的位置取参数,也就是我的cookie值,考虑和上一题相类似的做法,我们不是从头开始去调用fizz函数,而是仅仅执行它,这个想法给了我很大的灵感。同level0类似,我们只需要在前44个字节(也就是包括ebp及其以下的内容)随便填,45-48字节(也就是返回地址)填写fizz函数的入口地址,再在49-52字节(也就是ebp+8,fizz函数的取参数处)写入自己的cookie,即可。然而,根据以上的做法我却得到了错误的结果:当前的原因是:我成功进入了fizz函数,但是参数值却与我的cookie值不同。百思不得其解,我在网上查了很多资料,下面是一个解析上图为test函数中调用getbuf函数的堆栈结构,使用fizz函数的入口地址替换返回地址后的堆栈如下图Getbuf函数执行完毕,执行leave语句后:接着执行ret语句,popeip的地址为fizz函数的入口地址,即继续执行fizz函数。然而,此时重点来了。我们是直接进入函数,而不是调用这个函数,所以系统不会自动压入返回地址因此,进入到fizz函数中来,压入%ebp,为栈开辟了空间后:由于要取的cookie值是在fizz的ebp+8处,此时我们把这个图和输入字符串攻击缓冲区时的作对比,终于发现了问题:由于没有调用fizz函数,没有压入返回地址,fizz和getbuf的ebp位置不一样!所以,可以看出应该在相对getbuf%ebp+8的位置随便填4个字节的内容,在%ebp+12处,填入我的cookie,才能成功解决问题。如下:这次应该不会出问题了,测试一下:完美通过,这个实验也给了我很大启发:另一种解决的方法,如果我像第一题一样,填入的不是push%ebp的地址,而是mov%esp%ebp这一句的地址呢?如图,那么现在的ebp比调用getbuf的ebp偏移了8个字节,所以这个答案又有所不同:也测试一下:发现也是可以的,所以这道题不止一个答案。Levle2下面分析level2,以下是bufbomb内的一个bang函数的c代码类似于0和1关,你的任务是使bufbomb的执行过程中,执行bomb而非回到test,在这之前,你需要定义全局变量global_value为你的cookie值,你的操作代码应该完成以下操作:在堆栈上压入bang的地址,执行ret操作跳转到bang函数。一些建议为:1可以使用gdb得到所需信息来组织你的代码,在getbuf中设置断点并运行,设置参数例如全局变量的地址和缓冲区的位置2用手写字节编码很复杂也容易带来错误,通过编写包含指令和数据的代码,你可以使用工具做所有的工作,我们可以使用objdump或者gcc将自己的代码生成可执行的机器码3注意cookie不要输错了4注意立即数和存储器的区别5不要用jmp或者call指令,可以使用ret指令读完还是感觉一头雾水,感觉大概的意思是要自己去写汇编代码,完成相应需求,再通过gcc转成机器语言作为密码,再看看文章最后给出的一个实例generatingbytecodes,明确一下汇编代码的使用要求。文末的这个实例给出了一个汇编代码example.s,内容如下使用gcc汇编、反汇编生成如下机器码所以这个汇编代码的机器码就是这个机器码再通过hex2raw生成一个输入字符串传给bufbomb就可以完成任务。读到这里我也就明白了,这次是要写个汇编代码,转成机器码之后写到txt里进行操作,这个汇编代码的内容就包括了修改变量值和调用bang函数。下面是解题过程:首先得找到全局变量global_value的内存地址,所以反编译bang函数:和c语言代码比对,可以看到这个函数先把0x804d10c地址处的值传给eax寄存器,并和0x804d104处的值做比较,看一下后面的这个地址处的值这就是我的cookie,所以,前面的那个地址就是全局变量global_value所以现在就可以开始写汇编代码了,要完成的任务有两个,一是修改变量值,二是使用ret语句完成对bang函数的调用,前一个操作很简单,就是一个mov指令传值,后一个操作在课上也没有讲过用法,但是结合ret操作的作用(将栈顶单元出栈并赋给eip以实现转移),我需要先把bang函数的入口地址压栈,再ret,即可实现调用。所以汇编代码如下:然后就是照葫芦画瓢,学着writeup中的操作转换成机器码再打开这个level2.d所以这个汇编代码的机器码就是c7050cd104081ced262068528d0408c3下面问题来了,得到这个机器码之后怎么用呢?从头开始考虑一下,这个机器码的作用是执行到它时,修改全局变量的值并进入bang函数,然而要怎么执行到这一步呢?考虑执行getbuf函数的时候,将其返回地址改为这个函数的地址(也就是buf的首地址),使getbuf执行完毕后,继续执行这个函数,执行完这个函数就自动执行bang函数,完成了题目的要求。所以,下一个问题在于,我们写的这个函数的地址在哪里?使用gdb调试,在getbuf函数中设置断点,查询buf的首地址可以从getbuf的反汇编代码中看出,在callgets函数前,eax寄存器的值就是buf的首地址,即我写