CSAPP:bufbomb实验

声明:这个是本人自己探索稍微参考了上网上的。如有不对之处欢迎指正哦。

2018.6.15更新一些说得不清楚的地方(标红了)

1.运行makecookie生成属于自己的身份标识:

 

2.攻击五个缓冲区的四个地方,使我这个身份标识出现在它不该出现的地方。

3.Gets函数与gets函数类似,但不能判断数组越界,只能简单的将字符串复制到一个32个字节大小的数组。这也是本次hack的关键。

4. -u cookie 确保不同的人用不同的ID做题,并攻击不同的地址。-h用于打印这几个操作的内容

Level0:

第一关是让你修改getbuf函数保存的函数返回地址,本来执行完getbuf应该返回到test函数去的,但现在让你返回到smoke函数。

题目给了几条建议:

1.bufbomb进行反汇编;

2.注意字节顺序;

3.使用GDB进行检查;

4.getbuf在栈的位置与gcc的版本有关。

解题过程:

输入一个超出32字节的字符串,覆盖ebp的内容,因为ebp存储的是test函数的返回地址,将其修改为smoke函数的首地址即可。但如何准确命中?

先来看getbuf函数的反汇编代码:

 

据此画出其栈结构图:

 

根据汇编代码可知最终espebp-40这个位置,也就是说要想修改getbuf函数返回地址,要一个长40+8的字符串。由于地址是向上增大(注意是小端),因此函数的返回地址需要放在后44-48位。

先来看smoke函数的反汇编代码:

 

首地址是0x0804e0a,卧槽有个0a(被这里坑了),前面说了输入的字符串不能包含0a,不然那个Gets函数会以为你输入\n,就会结束字符串的输入。我想着能不能用smoke首地址的下一个地址来替代,然而是可以的。

测试字符串如下:

 

运行一哈看看:

 

Level1:

这一关跟第一关类似,也是修改getbuf的函数保存的函数返回地址让它进入一个叫fizz的函数。但是,需要自己传入一个参数,就是多了这么点东西。又来修改一下ebp的返回值?试试看。为什么我要强调进入而不是调用,进入跟调用是有区别的,函数在被调用之前,系统会将调用者的返回地址(也就是调完函数之后要执行的下一条指令的地址)保存入栈,就像test调用getbuf的时候,test函数的返回地址被保存在getbuf的栈中,因为执行完getbuf之后需要返回到test继续执行下一条指令,如何返回到test,就需要一个指引,也就是保存的那个函数返回地址。但进入由于是强行修改的,并不会保存函数的返回地址,所以进入的那个函数的堆栈结构会跟调用的函数不太一样,少了四字节来保存函数的返回地址。因此fizz取参的位置并不是ebp+8,而是多了四个字节的偏移量,在ebp+12处。详细分析看下面的图:

先来看看fizz函数的反汇编代码:

 

画出其堆栈结构:

 

汇编代码mov了一个立即数0x804a2e0,用GBD查看是什么玩意:

 

额。。是这么个东西。。

从栈结构图可以看出,fizz函数是从ebp+8处取参数,所以套路还是差不多的,前44个字节随便填点东西,45-48fizz函数的入口地址,49-52填自己的cookie,因为fizz是从这个位置取参的。看看效果如何:

测试字符串:

 

最后一行是ebp+8的参数

 

???并没有返回我的cookie

以下是我从网上查阅的资料:

test函数中调用getbu函数的堆栈结构:

 

 

修改返回地址为fizz函数的首地址后的堆栈结构:

 

getbuf函数执行完毕,执行leave语句:

 

执行ret语句后:

 

 进入到fizz函数后:

  

由于我们是直接进入的fizz函数,而不是调用,因此系统并没有保存test函数 的返回地址。所以fizz堆栈里面并没有函数的返址。也就是说getbufebpfizzebp并不一样。因此并不是在ebp+8取参数,由于没有返址,在getbuf函数就多了4个字节的偏移量,因此要将我的cookie放在ebp+12这个位置。说白了就是fizzebp位置与其它函数的不一样。因此ebp+8并不是参数位置。

测试字符串:

 

运行看看:


原来对参数的设置也是有要求的。要设置为自己的身份标识。怪不得不提示我通关。

Levle2

这一关跟前面的关卡也差不多。也是执行完getbuf函数后不返回到test,而是调用bang函数(注意这里是调用并不是单纯的进入,所以它后面要求你将bang函数的首地址压栈保存),它还多了一个全局变量作位参数。这一关就是要求你将bang函数首地址压入堆栈,然后通过ret来执行跳转。目前一脸懵逼完全不知道要干嘛?读完它给出的建议,大概意思是自己要写一个汇编代码然后生成机器码,再通过hex2raw传入给bufbomb。指导书后面给了个例子,大概意思跟我前面说的差不多,就是写一个汇编然后反汇编得到它的机器码,然后将getbuf的返址修改为它(它指的是你写的这个机器码)的首地址,就会执行先修改global_value的值然后进入bang函数。说白了你现在要写一个函数,具备以下功能:将全局变量设为你的cookie,然后调用bang函数判断有没有设置成功。

咋个写这个汇编呢?先来看bang函数的反汇编代码:

 

GDB查看mov 0x804d10c到底是个什么玩意?

 

后面又和0x804d104的值进行比较。(联系下bang函数里面的if判断就应该能联想到这里是取参和gobal_value进行比较)

所以我猜想这个0x804d10c应该是全局变量 global_value 的地址。

所以要修改gobal_value的值只需把我的cookie赋给这个地址再push bang函数的首地址再ret一下应该就可以了。

下面是我的汇编代码:

 

这里就不要借助eax寄存器了,因为会使你的机器码变长,位数不符合要求。你可能会疑问,源操作数和目的操作数不是不能同时为两个内存地址啊?但注意,这里是一个立即数和内存地址,这种方法不太常见,我查阅了相关资料,发现是可以的。当时写博客忘记改这里了,实在抱歉。

编译+反汇编后得到一个d文件:相关指令在指导书给出了,自己翻一番。

 

那么需要的机器码就是这一堆东西了:c7 05 0c d1 04 08 95 1a 0e 48 68 52 8d 04 08 c3

接下来是怎么找到我写的这个函数的入口地址找到呢?

指导书给出方法了,用GBD设置断点调试,看下getbuf的汇编:

 

call gets函数前,执行了对eax的一系列的操作。紧接着又来了一个mov  %eax,%esp),这是ret也就是调用结束后esp返回到这里,所以我猜想此时的eax应该保存着调用函数的首地址。那么我写的函数的首地址应该也保存在这里。用GBD查看:

 

得到此时eax的内容,将getbuf函数的返址修改为它即可。

测试字符串如下:

 

运行一哈看看:

 

由于忘记是小端模式,将eax里面的地址内容填反了。给我来了个better luck next time

Level3

第三关有点难。题目都读大半天。跟level2一样也要自己写汇编代码转换成机器码。这一关大概就是让你输入一个字符串,让getbuf函数返回自己的身份标识(cookie),说白了就是让你写一个函数具备以下功能:能将自己的cookie设置为getbuf函数的返回值且将test函数的首地址压入栈,然后通过ret来跳转。题目给的建议我就不巴拉巴拉了,跟level2的差不多。

先来看test函数这个小妖怪:

 

 

这个Boom就是用来检测你修改得对不对,不是判断失败的。我被这句代码卡了很久。

函数的返回值一般存储在eax寄存器中,所以通过修改eax的内容就可以修改返回值了。题目还有一个要求,不能改变它的旧ebp,因为它要继续执行test函数,才能判断你是否正确修改了返回值。所以level3有两种办法,第一种修复ebp(此处指的是新的ebp,第二种就是将test的地址压入栈然后再ret,此时再修改ebp的值(仿照调用bang函数的方法)。本人选择第二种。

先看test函数的反汇编:

 

调用getbuf后,执行的下一句语句,将这条指令的地址压入栈即可,因为getbuf函数被调用结束后就会顺着这个地址继续执行下去,也就达到了题目的要求了。

汇编代码如下:


这里输错了cookie。应该是0x480e1a95的!!粗心害死人)

然后按照之前的步骤,把机器代码弄出来:

 

接下来是如何找到旧ebp的值呢?很简单,getbuf函数的第一个ebp存储的肯定是旧ebpGBD查看:

 

这么个妖怪。

然后是找到我们编写的函数的地址:

level2一样没有改变。

 

测试字符串如下:

 

运行:

 

Fuck!错了!错哪了??错哪了??

回去检测一遍,发现是ebp的值查看错了,应该是push ebp 后的值。还有一个错误,就是我输错了我的cookie。气哭啊。。。

 

如图:

 

应该是这个妖怪!

然后修改测试字符串:

 

运行一哈:

 

终于等到你!

第一种办法就是直接mov ebp的值给现在的ebp就好。其它还是一样。然后构造字符串的时候就不需要填原ebp的值了。

Level4

最后一关了,想必很难。。自从bomb实验。。对闯关实验都留下阴影了。这一关要求你用-n这个指令,我也不知道这啥,先看题目,大概就是用-n运行bufbomb的时候,不再调用getbuf函数,而是调用一个叫getbufn的函数。两者类似,但getbufn限制了缓冲区的大小为512个字节,然后再-n模式下bufbomb会对你输入的测试字符串执行五次,每次都会有不同的堆栈偏移。你要一个汇编程序使得这五次执行都要返回你的cookie。。吗的好难的感觉。。然后它还要求getbufn函数返回你的cookietest而不是返回1cookie。。唉不难。修改下eax就好。判断你的cookie返回到test它会输出kaboom!然后又要通过ret来实现。。。。

它给的建议提是你用hex2raw发送多次字符串。指令给出了。然后就是要用同一个字符串攻击getbufn五次,还提示你用很多很大的nop指令来提高你的命中率,就是不停的nop,直到找到你写的那个机器代码的入口地址,这是我的理解。

getbufn这个小妖怪每次调用会在栈中随机分配一段存储地址,也就是它的ebp不是确定的。但我们的写的跳转地址是固定的,所以就需要大量的nop使得我们的攻击代码滑倒ebp那一段区域去,提高命中率。因此跳转代码要位于有效机器代码入口地址前的nop机器指令填充区,说白了,跳转代码和有效机器代码之间要填充大量的nop

因为栈的机器代码是由低地址向高地址执行,因此有效的机器代码尽量后移。(所谓有 效机器代码就是咱们的攻击程序)

说了这么多废话。开始解题:

先看getbufn函数的反汇编代码:

 

此处将eax赋为ebp-0x208,也就是咱们要写的函数的首地址。每执行一次getbufn函数,testnebp都会改变,但是testn栈顶esp的位置却不变,所以我们现在要通过espebp的关系来确定testnebp位置。然后修改这个位置的返回值就可以实现攻击了。

 

先来看testn这个小妖怪的汇编代码:(为什么是testn?-n模式下都有专用的函数)

 

从这里可以得到两个有用的信息:首先是ebpesp的关系:ebp=esp+0x24+0x4(多了一个ebx,修正),这是ebp的真值。也就是esp+0x28ebp的正确位置。然后第二个信息就是getbufn函数的返回地址0x08048ce2

到这里攻击的汇编代码已经可以写出来了:

 

得出机器代码:

 

 

现在就差最后一个东西了,咱们写的这个汇编程序的入口地址。一开始的时候说了首地址为ebp-0x208.现在要找到一个最大的ebp,使得五次调用的时候都能跳到这个汇编程序的入口地址(我也不知道咋证明,大概跟nop有关) 

这里我补充一下为什么要选取最大的ebp:首先咱们写的函数的首地址是固定的了,如何保证不同ebp的时候都能跳到这个入口地址呢?先来看个原理图:

入口地址
.......
nop
ebp(max)
........      
ebp(min)

如果咱们选取ebp(min),这个时候第一个nop填充的位置就是ebp(min)下一个栈位置,但是这个位置和最大的ebp位置差了一定的位置(就暂且设为4吧),由于nop的数量是固定的了,那么nop填充完之后,函数的入口地址就会跟最后一个nop差了四个位置,也就是没有命中!显然这样子是不可以的。那么我选取最大的ebp,第一个填充的nop就为它的下一个位置,填完所有的nop后,最后一个nop恰好会是函数的入口地址,因此不管你ebp如何变化,我最后一个nop一定能命中函数的入口地址。一句话概括就是:我的nop数量大于等于最大ebp和函数入口地址的栈长度。大概就是这么个意思。

那么通过查看五次调用getbufn函数的ebp就能确定最大的ebp值。 

第一次:

 

第二次:

 

第三次:

 

第四次:

 

第五次:

 

 

有两次是一样的。不过没关系。取最大的ebp为:0x55683020,再去减去0x208得到:0x55682E18。因此最高的函数入口地址为:0x55682E18

接下来如何构造我们的测试字符串呢?

共有520+4(覆盖旧ebp+4(修改返回地址)=528字节。其实就是三个部分:nop,攻击代码,跳转地址。根据前面得到机器代码共15个字节+入口函数的地址4个字节=19个字节。那么需要填冲的nop528-19=509个字节。

 

坑得一笔!怎么加都算不准509个!找了两个小时。。才真的够509nop!(nop对应的十六进制为0x90)

运行一下:

 

嘛哟。看了n多次Better luck next time!!终于看到它了!激动啊啊啊!!

由于是第一篇博客,并不怎么会写,有些地方写得确实不好,现在回来改一下。希望能帮助到有需要的人。

如有错误欢迎留言指出。

 

 

 

 

 

 

 


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值