计算机系统基础实训三—AttackLab实验

本文介绍了实验的目的,要求学生掌握栈溢出攻击的基本原理,通过逐步实现不同阶段的缓冲区溢出攻击,包括非ROP和ROP方式,利用GDB和汇编知识来理解和利用Linux环境下的AttackLab程序。实验旨在提升学生的逆向工程和调试能力。
摘要由CSDN通过智能技术生成

实验目的与要求

1. 强化机器级表示、汇编语言、调试器和逆向工程等方面基础知识,并结合栈帧工作原理实现简单的栈溢出攻击,掌握其基本攻击基本方式和原理,进一步为编程过程中应对栈溢出攻击打下一定的基础。

2. 理解缓冲区的工作原理和字符填充过程及其特点。对于无边界检测的语言及其工作方式所造成的缓冲区漏洞加深理解。

3. 通过字符串填充的方式,完成5个阶段的缓冲区攻击。分别基于基本返回地址填充、攻击代码填充、ROP等实现这5个难度递增的阶段的缓冲区溢出攻击。

实验原理与内容

“AttackLab”是一个Linux下的可执行C程序,包含了5个阶段(phase1~phase5)的不同内容。程序运行过程中,要求学生能够根据缓冲区的工作方式和程序的反汇编代码来确定攻击字符串长度和字符串中的关键内容。每次成功实现缓冲区溢出攻击时都会有提示相应内容,如果攻击失败则单纯的提示segmentation fault相关信息。

要求攻击字符串的执行不许绕开代码中的validate函数,缓冲区溢出之后对应ret的返回地址可以是以下类型:

1.函数touch1、touch2、touch3的首地址;

2.自行注入的攻击的首地址;

3.在后两个阶段中(ROP攻击),与farm.c的对应的可利用的gadget的起始地址,farm.c对应的机器码已经包含在可执行文件中。可以使用的gadget首地址需处于start_farm和end_farm之间的部分。

注意:前三个阶段使用ctarget作为攻击目标文件,后两个阶段中使用rtarget作为攻击目标文件。

每个阶段考察一个缓冲区溢出方式,难度逐级递增:

n阶段1:使用非ROP方式对ctarget进行攻击,调用touch1,且成功输出Touch1!: You called touch1。若不完全满足题目要求,则会提示“Misfire”和FAIL相关字段。

n阶段2:使用非ROP方式对ctarget进行攻击,调用touch2,且成功输出Touch2!: You called touch2。攻击过程中需要改写cookie变量的值。若不完全满足题目要求,则会提示“Misfire” 和FAIL相关字段。

n阶段3:使用非ROP方式对ctarget进行攻击,调用touch3,且成功输出Touch3!: You called touch3。攻击过程中需要使hexmatch的返回值能够正确引导validate函数。若不完全满足题目要求,则会提示“Misfire” 和FAIL相关字段。

n阶段4:使用ROP方式对rtarget进行攻击,调用touch2,且成功输出Touch2!: You called touch2。若不完全满足题目要求,则会提示“Misfire” 和FAIL相关字段。

n阶段5:使用ROP方式对rtarget进行攻击,调用touch3,且成功输出Touch3!: You called touch3。若不完全满足题目要求,则会提示“Misfire” 和FAIL相关字段。

ctarget和rtarget都从standard input读入数据,可以以重定向文件的形式进行输入。实验利用getbuf函数中的缓冲区。getbuf函数的结构如下:

unsigned getbuf()

{

char buf[BUFFER_SIZE];

Gets(buf);

return 1;

}

函数中的Gets函数与标准库中的gets函数类似,它从standard input中读取字符(以\n或者EOF结尾)并将它们添加字符串结尾符\0后存入缓冲区中。学生需要根据ctarget和rtarget文件及其反汇编代码来确定缓冲区位置及大小,并想办法构建出攻击字符串。

实验设备与软件环境

1.Linux操作系统—64位Ubuntu 18.04

2. gdb调试器和objdump反汇编指令

3. 笔记本

实验过程与结果(可贴图)

ctarget的函数调用链:main() -> test() -> getbuf() -> Gets()

我的cookie值为:0x49e7ed15

现将ctarget生成汇编代码,开始做题:

阶段1

首先查看getbuf的汇编代码:objdump -d ./ctarget

实验过程与结果(可贴图)

ctarget的函数调用链:main() -> test() -> getbuf() -> Gets()

我的cookie值为:0x49e7ed15

现将ctarget生成汇编代码,开始做题:

阶段1

首先查看getbuf的汇编代码:objdump -d ./ctarget

从中可以看出BUFFER_SIZE的大小是56 (16进制 0x38),retq指令作用等价于:可以通过objdump -d命令查看ctarget的汇编代码,从而设计自己的注入字符串。

反汇编函数test

反汇编touch1

touch1函数的起始地址为0x4018e5。getbuf在栈中分配了56个字节的内存来存储输入数据。在执行ret指令后,从%rsp+56处获得返回地址,因此我们需要来利用缓冲区溢出覆盖掉其返回地址,就可以将返回地址修改为touch1的起始地址。

构造合适的字节序列(16进制表示),写到文件phase1.txt 中。

我的机器使用 小端法 。e5 18 40 00 00 00 00 00 表示的地址就是 0x4018e5

执行以下指令进行测试

cat phase1.txt | ./hex2raw | ./ctarget -q

阶段2

在第二个实验中需要在注入字符串中加入一小段的代码

Touch2的起始地址为0x401913,而“cmp    %edi,0x203bdb(%rip) ”说明touch2中的val则存在$edi中。也就是我们需要先将edi中的值设置为cookie,然后再跳转到touch2执行。

建立phase2.s文档,输入注入代码内容为:

利用gcc -c phase2.s和objdump -d phase2.o命令得到机器代码:

可以得到三条指令序列如下:

48 c7 c7 15 ed e7 49 68 13 19 40 00 c3

接下来就是寻找%rsp的地址,利用gdb进行调试

地址为0x5561fe38,如上所示,我们获取到了%rsp的地址,结合上文所讲,可以构造出如下字符串,在栈的开始位置为注入代码的指令序列,然后填充满至56个字节,在接下来的8个字节,也就是原来的返回地址,填充成注入代码的起始地址,也就是%rsp的地址,可以得到如下字符串:

阶段3

反汇编代码

参数是一个指针变量(即为内存地址),使用gdb调式得到这个地址量

函数test输入参数所在的内存地址为0x5561fe30touch3起始地址为0x401a2a,模仿第二关的操作,新建phase3.s,先写出攻击代码的汇编

可以得到三条指令序列如下:

48 c7 c7 78 fe 61 55 68 2a 1a 40 00 c3

在hexmatch代码中我们可以看出需要让touch3参数val等于我的cookie字符的字符串表示。

我的cookie:0x49e7ed15

字符串表示为:34 39 65 37 65 64 31 35

找到%rsp的地址

得%rsp地址为0x5561fe38

得到字符串如下:

验证:

阶段4

与第二关相同,需要我们修改返回地址,调用touch2函数

其中两行攻击的代码是第二关的首地址:401913,还有Cookie值:49e7ed15

利用老师给的四张图

结合网上的教程,test 函数调用 getbuf 返回时,由于无法再像第一部分那用在栈中注入攻击代码。所以需要跳转到合适的 gadget 指令。

popq %rax #将cookie弹入 %rax 中

movq %rax,%rdi #将 %rax 的值复制到 %rdi 中,即 touch2 函数中的第一个参数。

有上述图可知:

popq %rax → 58

movq %rax,%rdi → 48 89 c7

将rtarget进行反汇编

得到有两个答案

阶段5(没做出来)

我直接翻译每一个可通过表格的代码

同样将 cookie 写到栈中,并将其地址传入 %rdi 寄存器。栈的位置是随机的,我们这时候若要将cookie放在栈中,则没有办法通过绝对的地址访问到cookie,因此可以采取偏移量的计算方法,用相对地址访问。

计算相对地址需要使用%rsp寄存器保存的地址,因此想办法用gadget进行获取

由于要进行地址计算,而attacklab中给出的字节形式表格的指令只有mov,pop,ret和nop等指令,并没有lea用于计算

查看寻找gadget的gadget farm,有唯一加法函数:

通过这个函数来实现加法,因为lea (%rdi,%rsi,1) %rax就是%rax = %rdi + %rsi。所以,只要能够让%rdi和%rsi其中一个保存%rsp,另一个保存从stack中pop出来的偏移值,就可以表示cookie存放的地址,然后把这个地址mov到%rdi就大功告成了。

从%rax并不能直接mov到%rsi,而只能通过%eax->%edx->%ecx->%esi来完成这个。所以,兵分两路:

   1.把%rsp存放到%rdi中

  2.把偏移值(需要确定指令数后才能确定)存放到%rsi中

  然后,再用lea那条指令把这两个结果的和存放到%rax中,再movq到%rdi中就完成了。

  值得注意的是,上面两路完成任务的寄存器不能互换,因为从%eax到%esi这条路线上面的mov都是4个byte的操作,如果对%rsp的值采用这条路线,%rsp的值会被截断掉,最后的结果就错了。但是偏移值不会,因为4个bytes足够表示了。

movq   %rsp,%rax

movq   %rax,%rdi

popq  %rax        

偏移值=48               

movl  %eax,%edx

movl  %edx,%ecx

movl  %ecx,%esi

lea   (%rdi,%rsi,1),%rax

movq   %rax,%rdi

Touch3

Cookie

第一步,获取到%rsp的地址,movq%rsp,%rax的指令字节为: 48 89 e0,所以这一步的gadget地址为: 401b86+3=401b89

第二步,将%rax的内容传送到%rdimovq %rax,%rdi的指令字节为: 48 89c7所以这一步的gadget地址为: 401afc+1=401afd

第三步,将偏移量的内容弹出到%raxpopq %rax的指令字节为: 58,其中c3为返回指令所以这一步的gadget地址为: 401ae7+3=401aea

第四步:从第一条指令结束一直到cookie之前还有9条指令9*8=72=0x48,所以偏移量为0x48

第五步,将%eax的内容传送到%edxmovl %eax,%edx的指令字节为:89 c2所以这一步的gadget地址为:401b7f+2=401b81

第六步,将%edx的内容传送到%ecx,movl%edx,%ecx的指令字节为:89 d1,所以这一步的gadget地址为: 401ba2+2=401ba4

第七步,将%ecx的内容传送到%esi,movl%ecx,%esi的指令字节为: 89 ce, 所以这一步gadget地址为:401b0d+2=401b0f

第八步,将栈顶+偏移量得到字符串的首地址传送到%rax这一步的gadget地址为:401b08

第九步,将字符串首地址%rax传送到%rdi,movg %rax,%rdi的指令字节为:48 89 c7,所以这一步的gadget地址为:401afc+1=401afd

第十步:touch3的首地址:401a2a

第十一步:cookie转换为字符串:34 39 65 37 65 64 31 35

因此我个人的答案为:

但还是错了,我也不知道为什么。难过.......

实验总结

通过这两个验室,我显著提高了使用GDB的熟练程度,并对程序执行有了更深入的了解。实验室的结构化和循序渐进的设计有助于以启发性的方式学习。老师提供的富有洞察力和清晰的图片提示对减少混淆非常有帮助。展望未来,提供更多真实世界的案例、调试练习的错误场景、程序执行的可视化、编码练习将进一步增强学习体验。我也会继续通过积极的练习来磨练我自己的GDB调试技能。

  • 42
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值