天天看点

《深入理解计算机系统》CSAPP_AttackLab

操作系统:linux

调试工具:gdb

目录

AttackLab

Phase 1

Phase 2

Phase 3

Phase 4

Phase 5

Reference Linking

Conclusion

我们是攻击者,也就是hack,其实我更喜欢骇客这个翻译,而不是黑客。<code>phase1 ~ phase3</code>的攻击方式都是运行<code>CTARGET</code>使用注入代码技术。

作为一名骇客小白,我们可以通过<code>unix &gt; objdump -d ctarget &gt; ctarget.d</code>这段指令查看汇编代码,但我更喜欢用用gdb里的disas指令展示函数。这样更方便阅读。(这里就不需要打断点了)

第一个攻击任务是:

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函数时,栈会给存留一个返回地址栈帧<code>0x401976</code>,执行getbuf函数结束后,通过这个地址返回主函数test。我们只要把这个地址改为touch1的函数地址<code>0x4017c0</code>即可。

《深入理解计算机系统》CSAPP_AttackLab

如何改呢?我们利用栈溢出的特性,因为getbuf函数开辟了40(0x28)beytes的栈空间,我们多输入8bytes的touch1的函数地址用来覆盖,之前的40bytes随意输入。(栈的一行里有8byte,返回地址也是8bytes)

同时注意到intel使用的是小端法排序,我们无需用0x注明十六进制,CTARGET会自动转为十六进制。

key:

(<code>c0 17 40</code>后续的零可填可不填,会自动补上的)

我们通过<code>unix&gt; ./hex2raw &lt; level1.txt | ./ctarget -q</code>进行攻击。(<code>./ctarget -q</code>,在<code>./ctarget</code>之后加 <code>-q</code>的原因是我们没法连接上CMU的服务器,在本地运行验证结果就行了;文件<code>level1.txt</code>中存放着我们的答案)

先查看代码及对应汇编代码:

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. <code>%rdi</code>中存储touch2函数着第一个参数。 Your injected code should set the register to your cookie, and then use a <code>ret</code> instruction to transfer control to the first instruction in touch2. 你注入的代码应该把寄存器<code>%rdi</code>设置为<code>cookie</code>,然后中使用<code>ret</code>指令转移控制权到touch2中的第一条指令(即touch2的函数地址<code>0x4017ec</code>)。

根据advice及touch2源代码可以知道:(要写汇编代码)

用<code>movq</code>指令,<code>$0x59b997fa</code>移入<code>%rdi</code>。从而<code>val == cookie</code>函数touch2执行成功。(在cookie.txt中存着cookie的值<code>0x59b997fa</code>)。

用<code>push</code>指令压入touch2的函数地址,从而<code>ret</code>时会返回为touch2的函数地址。

那么有:

再将其转化为机器代码:使用<code>linux&gt; gcc -c level2.s</code>及<code>linux&gt; objdump -d level2.o</code>

从而得到部分注入代码:(机器代码已经使用小端法了)

那么现在问题来了,从哪里注入代码?答案应该是从栈顶,在<code>getbuf</code>函数已经开辟栈空间的<code>%rsp</code>处注入。

原因呢?如下图所示,注入的register必须在stack top之前。

《深入理解计算机系统》CSAPP_AttackLab

接下来我们查找<code>getbuf</code>开辟栈空间的<code>%rsp</code>对应地址。

好,最终我们有,key: (注意填充已经开辟的栈空间40bytes以及小端法)

最后手绘一下,注入代码后的部分栈空间:(鼠标画的,字有点抽象)省略了<code>getbuf</code>的<code>return address</code>(注入顺序和图示顺序相反)

《深入理解计算机系统》CSAPP_AttackLab

查看(汇编)代码:

先看看任务:

Your task is to get <code>CTARGET</code> to execute the code for <code>touch3</code> rather than returning to <code>test</code>. You must make it appear to <code>touch3</code> as if you have passed a string representation of your cookie as its argument. 你的任务是让<code>CTARGET</code>执行<code>touch3</code>的代码,而不是返回<code>test</code>。你必须让<code>touch3</code>在<code>test</code>之上(执行)看起来就像你(通过<code>test</code>)传递了一个cookie的字符串表示作为<code>touch3</code>的参数。

再康康建议:

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 “<code>0x</code>.” 你将会用到cookie的字符表示。这个字符表示是连续的十六进制数字且没有前缀<code>0x</code>。 Recall that a string is represented in C as a sequence of bytes followed by a byte with value 0. Type “<code>man ascii</code>” on any Linux machine to see the byte representations of the characters you need. 在c语言中,字符串表示的结尾是1byte的0(也就是<code>'\0'</code>)。你可以在任何Linux机器中,使用<code>man ascii</code>指令查看字符对应的ascii码。 Your injected code should set register %rdi to the address of this string. 你注入的代码应该把<code>%rdi</code>设置为字符串地址。 When functions <code>hexmatch</code> and <code>strncmp</code> are called, they push data onto the stack, overwriting portions of memory that held the buffer used by <code>getbuf</code>. As a result, you will need to be careful where you place the string representation of your cookie. 当函数<code>hexmatch</code>和<code>strncmp</code>被调用时,它们会push数据到栈上,覆盖部分调用<code>getbuf</code>时存储的内容。因此,你应该仔细确定在哪里放入你的cookie。

由此我们可以知道,和touch2类似,也是在使用test过程中先执行getbuf,在getbuf过程调用touch3,同时touch3会调用hexmatch,hexmatch会调用strncmp。我们通过汇编代码可以知道它们分别开辟的栈空间是多少。

考虑到hexmatch开辟的110bytes栈空间,会覆盖部分原有的getbuf开辟的40bytes空间。所以我们不能直接输入cookie的字符表示(也就是cookie的ASCII表示)。

不能输入立即数,那我们可以用输入地址的方式,在此之前将cookie的ASCII表示先放入该地址当中。

那么,该放入哪个地址呢?我们这里选择的是放入test的栈顶地址当中,原因有两个。一是因为test的栈顶地址可以临时使用,当test开辟栈空间后,它立刻调用了getbuf函数,其栈空间实际上没有存放任何数据。当调用结束后,才开始在栈空间处存放数据。二是因为方便输入,test开辟的8bytes空间,恰好用来直接输入cookie的ASCII表示。

由此得出,先输入cookie的ASCII表示,然后引用test的栈顶地址,再调用touch3,最后返回即可。接下来,我们按部就班的来。

先用<code>man ascii</code>指令查询字符的ASCII表示:

​ 从左边的16进制表示可以得到cookie的ASCII表示:

接下来查找test的栈顶地址:

​ 那么test的栈顶地址为:<code>0x5561dca8</code>.

引用test的栈顶地址(作为地址存放在%rdi中,作为输入),再调用touch3(<code>0x4018fa</code>,之前的汇编已经给出),最后返回。得出如下汇编代码:

反汇编得出机器代码:

最终结合cookie的ASCII表示以及要注入的位置(getbuf栈顶地址:<code>0x5561dc78</code>)可以得出key:

(要将<code>ff 34 25</code>改为<code>68</code>才可通过,和标准答案编译的不一样,但不知道为啥,也许是我用<code>ubuntu</code>问题)

注意,任何字符表示的结尾都要加上<code>'\0'</code>,也就是16进制的<code>00</code>。也许会有人问,test只开辟了8bytes的栈空间,这不会溢出到返回地址,然后覆盖返回地址内容吗?当然会,但是经过查阅,发现<code>ctarget</code>的所有指令的地址<code>400c48 ~ 402d7c</code>都只占据6byetes。从而test返回时的地址最后2bytes是无效位,覆盖了也对返回地址没影响。

验证一下:

最后手绘一下,注入代码后的部分栈空间:(鼠标画的,字有点抽象)省略了函数的返回地址R.A.

(省略了<code>getbuf, hexmatch</code>的<code>return address</code>;hexmatch栈空间实际已经覆盖了getbuf栈空间部分内容,但为了能看清楚,没有展示出来)

《深入理解计算机系统》CSAPP_AttackLab

日期:21.11.7 ~ 21.11.8

从Phase4起,用ROP(Return-Oriented Programming),以因为单单使用注入代码(inject code)技术无法解决的栈随机化、二进制文件被限制访问的问题。

Phase4的任务和Phase2一样:将%rdi设置为cookie,调用touch2。

ROP要用到<code>gatgets</code>这个工具。

gadget:每一段<code>gadget</code>包含一系列指令字节,而且以<code>ret</code>结尾,跳转到下一个<code>gadget</code>,就这样连续的执行一系列的指令代码,对程序造成攻击。

实验老师已经很贴切的将我们需要用到的<code>gatget</code>放进<code>fram.c</code>文件里了。我们将其反汇编才可运用,终端指令如下:(注意是<code>-Og</code>,<code>O</code>要大写,不然就会采用 stack frame pointer,而<code>rtarget</code>里是没有用该指针的,不加的话指令编码会很复杂)

提示里有句很重要的话:

When a gadget uses a <code>popq</code> instruction, it will pop data from the stack.

说明使用gadget中的popq指令可以将栈的元素弹出,能够弹出的位置当然只能是栈顶了。

再结合我们之前在Phase2注入cookie使用的是movq指令,可以猜测,这里应该汇编代码应该是弹出cookie再将其移入<code>%rdi</code>,即:(gadget中的指令都是以<code>ret</code>结尾,所以弹出之后应该放在<code>%rax</code>里)

再参照表格:(这个图片在Phase 5处)

得出上述汇编代码十六进制表示:

再于<code>farm.d</code>文件中查找对应<code>gadget</code>指令(直接ctrl+f查找<code>48 89 c7</code>)。我们是要把cookie加进去,所以我们选择addvel这一组如下:

参考链接1 参考链接2(链接是关于<code>endbr64</code>的解释)

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

可以得出对应movq、popq对应的起始地址:<code>0x4019a0 + 2 = 0x4019a2</code>,<code>0x4019a7 + 4 = 0x4019ab</code>。

灵魂画手又来啦。直接把gatget的指令的注入,pop之后会有一个数据,这个数据就是cookie。

《深入理解计算机系统》CSAPP_AttackLab

key:

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任务,也是在<code>tset</code>返回前执行<code>touch3</code>,但还要记得插入个<code>cookie</code>的ascii代码

由于思路堵塞,这里直接参考这位博主的思路:

因为开启了栈随机化,所以不能直接把代码插入到绝对地址,必须找一个基准,我们就只能找%rsp。 因为touch3会开辟一个很大的buffsize,若把数据插到touch3下面的栈空间,有关内存之后基本就会被重写,所以要存在touch3的更高地址处。所以要在%rsp上加一个bias才可以,即字符串地址是%rsp + bias。 没有直接的加法指令,那就找两个寄存器互相加,找到一个放在下面 具体的操作可以是这样: 把<code>%rsp</code>里的栈指针地址放到<code>%rdi</code> 拿到bias的值放到<code>%rsi</code> 利用<code>lea x, y</code>,把栈指针地址<code>%rdi</code>和bias(<code>%rsi</code>)加起来放到<code>%rax</code>,再传到<code>%rdi</code> 调用<code>touch3</code>

关于寄存器的转化,需要自己查表配出来。

我们先画(写出= =)出要执行的栈帧。

因为<code>bias</code>的取值要根据插入的ROP指令数量来决定,<code>bias = 8byte * 9 = 72 = 0x48</code>。(注意,直接用0x48覆盖弹出的%rax的值也算一条ROP指令)

具体的查找指令对应十六进制代码的过程就不详述了,可以参考<code>Phase 4</code>,直接放出答案。

系统入门编程

leyN的CS学习之旅

myk的CS学习之旅

attack的要求蛮高,经常要找参考答案。

英文真的很重要(所以最近要用心、用脑备考六级了)

想到在补充

如有谬误,敬请指正。

上一篇: form表单控件