三 Attack Lab

上完CMU CSAPP的8 LECTURE之后,就可以做了。
csapp 课程观看地址:https://search.bilibili.com/all?keyword=csapp&from_source=banner_search
lab 3 下载地址: http://csapp.cs.cmu.edu/3e/target1.tar

CENTOS 环境采坑指南

因为网站的README 要求GCC 至少是4.8以上的版本
我的CENTOS 默认给了4.4 所以需要升,之后跑的时候,发现GCLIB 版本也不够。我通过下面2个网站解决了环境问题。
https://blog.csdn.net/changcsw/article/details/79761620
https://www.vpser.net/manage/centos-6-upgrade-gcc.html

准备工作

x86-64 架构的寄存器有一些使用习惯,比如:

  • 用来传参数的寄存器:%rdi, %rsi, %rdx, %rcx, %r8, %r9
  • 保存返回值的寄存器:%rax
  • 被调用者保存状态:%rbx, %r12, %r13, %r14, %rbp, %rsp
  • 调用者保存状态:%rdi, %rsi, %rdx, %rcx, %r8, %r9, %rax, %r10, %r11
  • 栈指针:%rsp
  • 指令指针:%rip

函数调用前需要把某些以后仍旧需要用到的值保存起来。

而对于 x86-64 的栈来说,栈顶的地址最小,栈底的地址最大,寄存器 %rsp 保存着指向栈顶的指针。栈支持两个操作:

  • push %reg%rsp 的值减去 8,把寄存器 %reg 中的值放到 (%rsp)
  • pop %reg:把寄存器 (%rsp) 中的值放到 %reg 中,%rsp 的值加上 8

接下来需要了解的事情是,每个函数都有自己的栈帧(stack frame),可以把它理解为每个函数的工作空间,保存着:

  • 本地变量
  • 调用者和被调用者保存的寄存器里的值
  • 其他一些函数调用可选的值

如下图所示


image.png

x86-64 的函数调用过程,需要做的设置有:

  • 调用者:
    • 为要保存的寄存器值及可选参数分配足够大控件的栈帧
    • 把所有调用者需要保存的寄存器存储在帧中
    • 把所有需要保存的可选参数按照逆序存入帧中
    • call foo: 会先把 %rip 保存到栈中,然后跳转到 label foo
  • 被调用者
    • 把任何被调用者需要保存的寄存器值压栈减少 %rsp 的值以便为新的帧腾出空间

x86-64 的函数返回过程:

  • 被调用者
    • 增加 %rsp 的计数,逆序弹出所有的被调用者保存的寄存器,执行 ret: pop %rip

有了上面的基础知识,我们大概就能明白,利用缓冲区溢出,实际上是通过重写返回值地址,来执行另一个代码片段,就是所谓代码注入了。比较关键的点在于

  • 熟悉 x86-64 约定俗成的用法
  • 使用 objdump -d 来了解相关的偏移量
  • 使用 gdb 来确定栈地址

这之后,我们需要把需要注入的代码转换位字节码,这样机器才能执行,这里可以使用 gccobjdump 来完成这个工作

假设 foo.s 是我们想要注入的代码

vim foo.s

利用 gcc 生成对应的字节码 foo.o

gcc -c foo.s

通过 objdump 来查看其内容,可以看到对应的字节码

objdump -d foo.o | less

然后需要把十六进制代码转换成字符串这样我们可以写在程序里

./hex2raw -i inputfile -o outputfile

另一种攻击是使用 return-oriented programming 来执任意代码,这种方法在 stack 不可以执行或者位置随机的时候很有用。

这种方法主要是利用 gadgets 和 string 来组成注入的代码。具体来说是使用 popmov 指令加上某些常数来执行特定的操作。也就是说,利用程序已有的代码,重新组合成我们需要的东西,这样就绕开了系统的防御机制。

举个例子,一个代码片段如下:


void foo(char *input){

    char buf[32];

    ...

    strcpy (buf, input;

    return;

}

假设我们这里想要把一个值 0xBBBBBBBB 弹出到 %rbx 中并且移动它到 %rax 中,我们找到下面两个 gadgets:

  • address1: mov %rbx, %rax; ret
  • address2: pop %rbx; ret

所以在这里我们其实不需要关心如何在 buffer 中运行我们的代码,而只需要知道 buffer 的 size,从而改写返回地址,即可以利用程序中原有的代码进行我们的操作。

在这个例子中,因为 address2 中的代码是把栈顶的值弹出到 %rbx 中,所以执行的时候,就会把 0xBBBBBBBB 放到 %rbx 中,现在程序就指向 address1 了,然后就会继续执行 address1,也就达到我们的目的,把 0xBBBBBBBB放到了 %rax 中。

那么问题来了,我们如何能找到想要的 gadget 呢?在这个实验中,提供了一个 farm.c,可以从这里找到我们需要的 gadgets。

gcc -c farm.c

objdump -d farm.o | less

一些建议:

  • 注意寻找 c3 结尾的代码,因为这可以作为每个 gadget 的最后一句(也就是正常返回)
  • 画出栈的图
  • 注意字节的顺序 (little endian)

level 1

image.png

根据建议第一步先生成汇编,我的想法是先去看看TEST,和GETBUF这2个函数。
发现GETBUF里,0x28的RSP移动。大概是40个字符。
先做个实验,看下输入40个字符,会不会SEGMENT FAULT。
随后去把地址的地方修改到TOUCH即可。


image.png

验证了自己的猜想。
下一步就是攻击啦。在汇编文件里找到TOUCH1的地址
00000000004017c0
构建HEX 2 RAW 的结果集。 输入40个0, 后跟上地址。


image.png

level 2

第一关 是直接跳转到那个地址就可以。 第二关难度高一些,因为TOUCH 2,需要传一个参数进去,根据题目意思,要把自己的COOKIE传进去。


image.png

那么我就需要在BUFFER区写一段代码,根据传参寄存器定义,第一个参数应该放进%rdi
大概指令是movl 0x59b997fa(我的cookie值),$rdi 随后跳转。
所以原先RET 存地址的位置,应该放上我写的MOVL 代码的地址,后接PUSH一个地址到栈顶应该放到TOUCH2 那里,最后RET。


image.png
image.png

image.png

image.png
image.png

Level 3

image.png

第三关 COOKIE 要用STRING的形式传进去

第一步构建COOKIE的STRING 的二进制表示。


image.png

第二步有一个要注意的地方是,调用 hexmatch 和 strncmp 时会把数据存入栈中,也就是会覆盖一部分 getbuf 的缓冲区,所以要看看到底需要把传入的字符串放到哪里。

首先我试了放在BUFFER的最远端,


image.png
image.png

那我想既然这个栈帧不行,那我就放到上一个栈帧去试试。


image.png

image.png

Level 4

从前面我们可以知道,有缓冲区加上缓冲区的代码可以执行使得程序非常容易被攻击,但是在 rtarget 中使用了两个技术来防止这种攻击:

  • 每次栈的位置是随机的,于是我们没有办法确定需要跳转的地址
  • 即使我们能够找到规律注入代码,但是栈是不可执行的,一旦执行,则会遇到段错误

那么现在怎么办呢?可以利用已有的可执行的代码,来完成我们的操作,称为 retrun-oriented programming(ROP),策略就是找到现存代码中的若干条指令,这些指令后面跟着指令 ret,如下图所示

image

每次 return 相当于从一个 gadget 跳转到另一个 gadget 中,然后通过这样不断跳转来完成我们想要的操作。举个具体的例子,假设程序中有一个像下面这样的函数:


void setval_210(unsigned *p){

    *p = 3347663060U;

}

这么看起来没啥用,但是看看对应的汇编代码,可能就是另一个感觉:

image

这里 48 89 c7 就编码了 movq %rax, %rdi 指令(参加后面的表格),后面跟着一个 c3(也就是返回),于是这段代码就包含一个 gadget,起始地址是 0x400f18,我们就可以利用这个来做一些事情了。

image.png

用BUFFER溢出,替换掉返回地址之后,接着把数据存到栈上。
之后找到POP RDI , RET

溢出的写法为 FARM 地址 -》 cookie -> touch 2 地址


image.png

还是先去FARM 里找下这个组合吧


image.png

所以要跳转的FARM地址是40141b

在sol2的基础上修改


image.png
image.png

Level 5

思路 首先要把RSP的地址给放到一个寄存器里,
在FARM 中找到


image.png

image.png

然后怎么对RAX的地址做位移

image.png

感觉没法直接使用
试试看 ,拆出来 有没有花头
https://defuse.ca/online-x86-assembler.htm#disassembly2
image.png

成功造出了RSP+16*3+7 的空间
那么我只要在这个位置上放好我的COOKIE STRING。
下一步就是找到 -> RDI的操作
image.png

image.png

综上,
栈顶
401a06
4019d8
4019a2
touch3 地址(4018fa) (到这里一共花了32个)(55-32=23)
23+8个00(因为RSP 是先RET一次后被存入RAX)
(rsp+55)cookie string


image.png

你可能感兴趣的:(三 Attack Lab)