计算机系统实验-BufLab

一.实验题目及目的

1.实验题目

       程序运行在linux环境中,从输入获得一个字符串,将这个字符串放入指定的buf处,buf的大小为32,需要分析栈帧、buf位置等信息,通过输入字符串使缓冲区溢出,完成指定的函数调用等目标。

2.实验目的

       通过缓冲区溢出,熟悉函数调用的过程和栈帧结构的建立等。

二.实验内容

1.实验文件及实验准备

       实验文件共有三个,bufbomb,hex2raw,makecookie。bufbomb是需要进行缓冲区溢出攻击的文件。实验过程需要用到userid和对应的cookie,cookie使用makecookie生成,hex2raw用于将16进制数据转换为字符串输入。

       在实验中使用学号202004061409作为userid,cookie为0x73b099ff,并将bufbomb反汇编保存到文件中进行分析。

2.实验过程

       getbuf函数如下,缓冲区大小为32,调用了Gets()。

(1)Level0:Candle

       要求输入字符串使getbuf()不正常返回,而是执行smoke()函数.

       getbuf()函数在test中调用。

       getbuf的汇编代码如下:

       getbuf中调用了Gets,并将缓冲区地址作为参数传入,根据getbuf的汇编代码,buf的起始位置为ebp-40的位置,而根据栈帧建立的规则,返回地址的位置在ebp+4,故只需要向缓冲区写入48个字节的内容,就可以覆盖修改返回地址,为了调用smoke()函数,将返回地址修改为smoke()的地址,即08048e0a。

       因此输入的数据最后四个字节为0a 8e 04 08。0a会被当作\n,因此修改为smoke的第二条指令地址,即0a修改为0b。

       新建文件输入16进制数据如下:

       使用hex2raw将其转换为字符串数据输入,成功调用smoke()。

(2)Level1:Sparkler

       Level1要求输入字符串后调用fizz()函数,并需要将cookie值作为参数传入,代码如下:

       Fizz()的汇编代码如图所示,userid对应的cookie值位于0x804d104,传入的参数cookie在ebp+8的位置。需要找到fizz建立的栈帧的ebp的值。在getbuf返回前,执行了leave指令,leave指令等同于mov ebp,esp;pop ebp。然后执行ret指令进入fizz还会执行pop eip,因此在进入fizz()前,esp的值为原getbuf()函数的ebp+8。由于fizz不是由call指令调用的,没有返回地址入栈的过程,进入fizz()后,ebp入栈使esp的值-4,因此getbuf的ebp+8-4就是新的ebp的值,即新的ebp为getbuf的ebp+4。所以只要将getbuff的ebp +12的位置写入正确的cookie值,就可以向fizz()传入正确的cookie值。

       因此向缓冲区写入56个字节的内容,45-48字节为fizz()的地址,53-56字节为0x73b099ff,新建文件,16进制数据如下:

       转换为字符串输入,成功调用了fizz()并传入了正确的cookie值。

(3)Level2:Firecracker

       这一次需要调用的是bang()函数,如下:

       bang函数使用了全局变量,需要将全局变量global_val设置为cookie值。

       需要在getbuf返回时先跳转到一段代码,在这段代码中设置global_val为cookie值,然后将bang的地址入栈,通过ret指令跳转执行bang()。最后的跳转使用ret是因为call和jmp指令使用PC相对寻址,难以设置正确。

       bang()的汇编代码如下,global_value的地址为0x804d10c,需要把cookie值写入这个地址。

       把cookie值写入global_value并跳转执行bang()需要执行以下指令:

       将这段代码编译,再反汇编得到这段代码的机器码:

       只需要将机器码写入buf,在getbuf返回时跳转到buf的起始位置,执行这些指令就可以了。buf的位置是esp-40,只能通过gdb调试找到运行时的位置。

       gdb调试将断点打在0x804926b的位置,此时正在向Gets()传参,eax中的值为esp-40。这个值为0x556834e8。

       同(1)中相同,向缓冲区写入48个字节的数据,开头为需要执行指令的机器码,,且机器码不需要考虑大小端,44-47字节为buf的首地址0x556834e8:

       转换为字符输入,调用bang()成功。

(4)Level3:Dynamite

       Level3需要完成的任务是在执行getbuf()后,将getbuf()的返回值修改为cookie值,并返回到test()函数,同时恢复被破坏的栈。

       类似于level2,通过缓冲区溢出在getbuf()返回时跳转到一段代码,在这段代码中完成getbuf返回值的修改,栈的恢复,最后使用ret指令返回到test()当中。恢复到test()函数的栈帧,需要恢复ebp的值,在getbuf()返回前会在leave指令中pop ebp恢复旧的ebp值,但是这个值被复制进缓冲区的字符覆盖掉了,因此需要找到ebp的值并恢复。

       修改返回值只需要将cookie值放入eax寄存器就可以了。恢复test()函数的ebp的值需要使用gdb调试找到,将断点设置在0x8048e40处,此处test()函数的栈帧已经建立。找到ebp的值为0x55683540。在指令中给ebp赋值或者在输入的字符串直接将这个值写入存放旧的ebp的位置都可以完成栈的恢复。

       最后是使用ret指令返回test(),返回到call getbuf的下一条指令处0x8048e50。最终需要执行的汇编代码如下:

       反汇编得到机器码如下:

       最终输入的字符串的16进制形式如下:

       转换为字符输入,结果如下:

(5)Level4:Nitroglycerin

       本关与level3中任务相同,不同的是使用-n标志运行程序,会调用testn(),在testn()中调用getbufn()五次,且缓冲区的大小为512字节。每次getbufn()的栈空间随机,因此不可以再使用level3中的方式找到buf的起始位置,但栈帧结构是相同的,可以利用这一点找到buf的大致位置。

      buf的地址

       首先先找到buf的大致位置,确保可以跳转到这里执行需要执行的指令。

       getbufn的汇编代码如下,buf的首地址是ebp-0x208。

       接下来进入gdb调试,将断点打在0x8049247,此处getbufn的栈帧已经建立完毕:

       五次调用getbufn,buf的首地址如下:

       0x55683308

       0x556832c8

       0x55683318

       0x55683308

       0x55683358

       因此可以找到buf首地址的大致范围0x556832c8-0x55683358。

       改写getbufn的返回地址只能跳转到一个固定的地址,如何在跳转到固定地址后能够执行需要执行的指令,需要使用nop指令,利用一种nop sled的方式实现。nop sled就是在实际攻击代码前插入多条nop指令,nop指令只对PC+1,只要可以跳转到nop指令的部分,就可以一直执行到实际的攻击代码处并执行攻击代码。以上已经试探过了buf首地址的大致范围,选取首地址最大的0x55683358处,跳转到这里就可以保证进入缓冲区,然后在写入的数据中先插入nop指令,把攻击代码放到后面就可以了。

需要执行的代码

       能够保证跳转到合适的位置,接下来就可以准备需要执行的代码了。需要完成的工作为修改getbufn的返回值,恢复testn的ebp,返回testn。其中修改返回值和返回testn与level3相同。

       恢复testn的ebp可以通过观察testn与getbufn的栈帧情况进行处理。testn的部分汇编代码如下:

       结合getbufn的汇编代码,可以得出getbufn的栈帧关系如下:

       在getbufn返回前执行的leave指令将ebp的值赋给esp,然后弹出testn的ebp(已被覆盖),然后通过ret指令pop eip跳转执行指定的指令,esp+8。getbuf调用前后,esp的值是不变的。因此要恢复testn的ebp,只需要将esp+0x28赋值给ebp。

       最终需要执行的代码如下:

       编译后反汇编如下:

       因此最终输入的数据如下,共528个字节:

       由于getbufn执行5次,需要5次以上数据的输入,使用0a(\n)分隔。转换为字符后输入,目标完成。

三.实验总结

      通过本次实验,加深了对缓冲区溢出的理解,并学习了解了缓冲区溢出攻击的方法,更加熟悉了函数调用时栈帧建立的过程和栈帧情况。其中最主要的是理解了缓冲区溢出攻击的方式,即通过溢出覆盖返回地址跳转到一段设定的程序进行执行,并可以通过恢复寄存器的方式恢复栈帧,以及应对动态堆栈的nop sleds方法。

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Buflab 实验是针对缓冲区溢出漏洞的一个实验,主要包括以下 5 个实验: 1. Phase 1: Buffer Overflow Basics 这个实验旨在让学生了解基本的缓冲区溢出漏洞,并通过编写一个简单的程序来利用这个漏洞。具体步骤如下: - 理解 buffer overflow 的基本原理和机制; - 阅读并分析提供的代码; - 利用编译器提供的工具来探测和分析漏洞; - 编写一个攻击程序,利用漏洞覆盖指令指针,执行指定的代码。 2. Phase 2: Code Injection 这个实验旨在让学生进一步学习如何利用缓冲区溢出漏洞,将恶意代码注入到受害者程序中。具体步骤如下: - 理解代码注入的原理和机制; - 阅读并分析提供的代码; - 利用编译器提供的工具来探测和分析漏洞; - 编写一个攻击程序,利用漏洞将恶意代码注入到目标程序中。 3. Phase 3: Return-Oriented Programming 这个实验旨在让学生学习如何利用 Return-Oriented Programming (ROP) 技术,绕过缓冲区溢出漏洞的防御机制。具体步骤如下: - 理解 ROP 的原理和机制; - 阅读并分析提供的代码; - 利用编译器提供的工具来探测和分析漏洞; - 编写一个攻击程序,利用 ROP 技术绕过目标程序的防御机制。 4. Phase 4: Heap Overflow 这个实验旨在让学生学习如何利用堆溢出漏洞,从而攻击堆管理程序。具体步骤如下: - 理解堆溢出漏洞的原理和机制; - 阅读并分析提供的代码; - 利用编译器提供的工具来探测和分析漏洞; - 编写一个攻击程序,利用堆溢出漏洞攻击堆管理程序。 5. Phase 5: Format String Vulnerability 这个实验旨在让学生学习如何利用格式化字符串漏洞,从而攻击受害者程序。具体步骤如下: - 理解格式化字符串漏洞的原理和机制; - 阅读并分析提供的代码; - 利用编译器提供的工具来探测和分析漏洞; - 编写一个攻击程序,利用格式化字符串漏洞攻击受害者程序。 以上就是 Buflab 实验每个实验的大致步骤,希望对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值