Lab要求:
理解函数调用引起栈变化的机制,并利用此机制发现漏洞发起攻击
实验机器:
VMware的虚拟机——Ubuntu-20.04
实验材料:
CSAPP的官网selfstudy-handout:
点击以进入CMU提供的下载渠道
一定记得看Writeup!
关键小技巧:
1:直接./ctarget会给CMU服务器报告,就会出错,以-q运行则正常。
2:./flie1.exe | ./file2.exe是指将./file1的结果作为运行file2的参数, 例如./hex2raw < try.txt | ./ctarget -q,就是将十六进制字符转化为raw,再把它作为运行ctarget参数。
3:如果想以文件为输入调试ctarget可以这样:先将构造的16进制字符串转化为raw保存在sd.txt,再进入gdb,用-i来引入sd.txt:
Part 1 Level 1:
从writeup中找到任务:通过改写getbuf栈帧最下面的返回地址,使本该返回test的getbuf,去调用另一个存在于ctarget的未调用函数touch1
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp
4017ac: 48 89 e7 mov %rsp,%rdi
4017af: e8 8c 02 00 00 callq 401a40 <Gets>
4017b4: b8 01 00 00 00 mov $0x1,%eax
4017b9: 48 83 c4 28 add $0x28,%rsp
4017bd: c3 retq
4017be: 90 nop
4017bf: 90 nop
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 callq 400cc0 <puts@plt>
4017d8: bf 01 00 00 00 mov $0x1,%edi
4017dd: e8 ab 04 00 00 callq 401c8d <validate>
4017e2: bf 00 00 00 00 mov $0x0,%edi
4017e7: e8 54 f6 ff ff callq 400e40 <exit@plt>
从sub $0x28,%rsp ,可以看到getbuf预留了40(dec)的位置给即将输入的字符。
由于小端法,0x4017c0被表示为c0 17 40
结果展示:
Part 1 Level 2:
从writeup中找到任务:通过改写getbuf栈帧最下面的返回地址,使本该返回test的getbuf,去调用另一个存在于ctarget的未调用函数touch2,且需要传入自己的cookie。
主要思路:
- 考虑到需要完成特殊的操作,首先需要用objdump把代码转化为十六进制数,构造字符串。
- 应该把代码转义成的数插入输入,再利用ret跳转至该位置运行自己插入的代码,
再然后还是用ret跳转至touch2的位置,正常运行。
具体步骤:
-
先将需要完成的操作转化为十六进制数:
比如 pushq 0x4017ec 就是 68 ec 17 40 00
为什么用pushq?:ret==pop %rip会将当前的%rsp所在的内存读取出来,作为%rip即将要执行的指令,所以要先将这段插入代码之后要去的地址压入栈中。
为什么赋值%rdi?:%rdi是默认用来传输第一参数的寄存器,一般是由调用者负责提前赋好值,我的目的在于覆盖它的赋值,让%rdi等于我的cookie。
-
构建字符串:
构建思路:首先要让我们插进来的代码运行,然后我们的代码会跳转至touch2:
为了让我们插进来的代码得以运行,需要知道gets把我们的字符串放到了哪里,在gets的出口设置断点,
查看get将我们的字符串放在了哪?可以看到我输入的“asdfasdf”应为gets的输出,即字符串地址保存在%rax,用x /s $rax查看%rax中保存的地址所在内存是否包含我的输入——“asdfasdf”,发现在0x5561dc78确实有我的输入。
那么应该在0x5561dc78+0x28,即0x5561dca0处放置0x5561dc78。
结果展示:
Part 1 Level 3:
从writeup中找到任务:通过改写getbuf栈帧最下面的返回地址,使本该返回test的getbuf,去调用另一个存在于ctarget的未调用函数touch3,且需要以字符串传入自己的cookie。
主要思路:
- 考虑到需要完成特殊的操作,首先需要用objdump把代码转化为十六进制数,构造字符串。
- 应该于安全的位置存放cookie的ASCII码。
- 应该把代码转义成的数插入输入,再利用ret跳转至该位置运行自己插入的代码,
再然后还是用ret跳转至touch3的位置,正常运行。
具体步骤:
1. 先将需要完成的操作转化为十六进制数:
为什么用pushq?:ret==pop %rip会将当前的%rsp所在的内存读取出来,作为%rip即将要执行的指令,所以要先将这段插入代码之后要去的地址压入栈中。
为什么赋值%rdi?:%rdi是默认用来传输第一参数的寄存器,一般是由调用者负责提前赋好值,我的目的在于覆盖它的赋值,让%rdi等于我打算存放字符串的地址,即0x5561dca8。
为什么将字符串存放于0x5561dca8如图?:
Writeup 的小建议,大概是说hexmatch与strncmp会覆盖getbuf的栈帧,应当把字符串放置在安全的地方。
可以看到,0x5561dca8至 0x5561dcb8 的部分没有被污染,但0x5561dc78至 0x5561dca8 的数据均已被破坏,所以可以把数据放在0x5561dca8之上。
2. 构建字符串:
把之前反汇编好的十六进制字符填充到相应位置,得:
结果展示:
Part 2 Level 2:
从writeup中找到任务:通过改写getbuf栈帧最下面的返回地址,使本该返回test的getbuf,去调用另一个存在于ctarget的未调用函数touch2,且需要传入自己的cookie。
主要思路:
- 反汇编需要的操作
- 根据反汇编的结果在farm中寻找gadgets(ctrl+F)
- 按照合理的顺序插入数据以及gadgets的地址
具体步骤:
- 需要反汇编的操作为:
- 为什么使用pop?: 直接再farm中找到 mov $0x59b997fa, %rdi 比较困难,所以需要把0x59b997fa存入栈中,再进一步保存至寄存器
- 为什么不用pop %rdi?: 使用的操作需要在farm中找到,而恰好没有pop %rdi (5f in hex)
- 找到相应的gadgets
- 注意到在函数 addval_219 中,有58 90 c3,其中90表示什么也不做,所以正好满足gadgets的要求,
对应着 pop %rax,开始于0x4019ab; - 而函数 addval_273 中,有48 89 c7 c3,对应着mov %rax, %rdi ,开始于0x4019a2;
00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
4019ad: c3 retq
00000000004019a0 <addval_273>:
4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
4019a6: c3 retq
- 构建入侵字符串
具体步骤解释: 注意箭头位置,此刻正在执行gadget_1,所以pop %rax,会把我提前安排好的cookie(0x59b997fa)赋给%rax,
且 %rsp=%rsp-8,然后下一步执行retq (c3 in hex),即pop %rip,这会使程序跳转执行0x4019a2,即gadget_2,
之后类似,不多赘述。