请选择 进入手机版 | 继续访问电脑版

CSAPP:Attack lab的解题过程

[复制链接]
蝶蝶已蝶已蝶蝶 发表于 2021-1-1 10:33:09 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
Attack lab

  1. void test(){    int val;    val = getbuf();    printf("No exploit. Getbuf returned 0x%x\n", val);}
复制代码
正常执行的话是调用getbuf,然后从屏幕中输入字符串,如果正常退出的话,则会执行
  1. printf("No exploit. Getbuf returned 0x%x\n", val)`;
复制代码
而这个lab就是不让它正常退出。
这个lab总共有5个phase,前三个phase是一种方法,后俩是另一种。
level1

level1要求输入字符串使步调跳转到函数touch1
touch1里面没啥东西,所以我们要做的只是使步调跳转。
要使步调跳转到touch1,我们需要将test栈帧顶部存放的地址改写成touch1的起始地址
  1. void touch1() {    vlevel = 1;    printf("Touch!: You called touch1()\n");    validate(1);    exit(0);}
复制代码
通过反汇编工具得到touch1的地址如下

可以看到touch1的地址为0x4017c0
接下来需要做的是填充getbuf分配的空间,查察getbuf的反汇编如下:

可以看到%rsp减了40,故我们需要填满这40个字节的缓冲区,然后在接下来的8个字节中,填入touch1的地址0x4017c0。
这里我选择用字节00填充。
综上所述,输入的字节序列为:

最后再验证答案正确性



level2

level2要求输入字符串使步调跳转到函数touch2
  1. void touch2(unsigned val){    vlevel = 2;    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);}
复制代码
首先看看函数touch2要干嘛,它判定参数val是否即是cookie,要即是才算过关。
所以我们不但要使步调跳转到touch2,还得包管传给touch2的参数val必须与cookie相等。
此中cookie是十六进制表现的无符号整数,值为0x59b997fa,
level2与level1的差别之一在于,level2在跳转之前需要将0x59b997fa传给touch2,这一步可以由汇编指令
movq 0x59b997fa, %rdi完成。
那么如何使CPU执行这条指令呢?
我们可以将指令写入getbuf分配的空间中,并将test栈帧中的return address改写成getbuf的栈顶(注入字节序列的起始地址),这样一来,当getbuf执行ret的时候,CPU会跳转到getbuf的栈顶,执行我们写入的指令。
而getbuf的栈顶可以通过反汇编得到:

这里显示的是执行完第一条sub指令后的状态,这时候的%rsp即为我们注入字符串的首地址=0x5561dc78
为了使步调执行touch2,需要将touch2的地址push到栈中,然后执行ret指令
使用反汇编得到touch2的地址为:0x4017ec

故我们的指令为
  1. movq    $0x59b997fa, %rdipushq   0x4017ecret
复制代码
故栈的目的状态如下:

最后需要做的就是将汇编指令转化成字节序列

故最后得到的字节序列为:

最后再验证答案正确性

level3

level3要求输入字符串使步调跳转到函数touch3
  1. /* Compare string to hex represention of unsigned value */int hexmatch(unsigned val, char *sval) {    char cbuf[110];    /* Make position of check string unpredictable */    char *s = cbuf + random() % 100;    sprintf(s, "%.8x", val);    return strncmp(sval, s, 9) == 0;}void touch3(char *sval) {    vlevel = 3; /* Part of validation protocol */    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要干嘛,它调用了函数hexmatch,要使它返回true才算过关。
那么看看hexmatch函数要干嘛,它首先分配了一个110字节的缓冲区,然后在这个缓冲区里面随机取一个地址作为指针s的值,sprintf将参数val存放在s所指的缓冲区内,最后比力参数sval所指的字符串与s所指的字符串是否相等。
所以为了使hexmatch返回true,我们需要使转化成字符串后的cookie,与字符串sval相等。
此中cookie是十六进制表现的无符号整数,值为0x59b997fa,
使用man ascii指令查询cookie的ascii码表现为:
35 39 62 39 39 37 66 61 00
现在我们知道了sval字符串的内容,还要办理一个问题,就是该把它放在那里?
当调用hexmatch和strncmp时,他们会把数据压入到栈中,有大概会覆盖getbuf栈帧的数据,所以不要放到getbuf的栈帧里面,要放到test的栈帧里。那么就放在test栈帧里返回地址的上面就好了。
使用反汇编工具,查察getbuf函数的汇编代码如下图:

现在的状态是刚进入函数,还没执行第一条语句,所以rsp指向的是test栈帧的顶部,里面存放的是本该回到的地址,
rsp指向0x5561dca0,由于地址占8个字节,所以我们的sval字符串应该放在0x5561dca0+8=0x5561dca8,这个位置。

总的来说,栈的初始状态很简朴,如果没有buffer overflow,那么执行完getbuf函数,CPU将回到test函数中紧跟着getbuf()的语句继承执行;这里我们的目的就是使CPU执行完getbuf后,跳转到touch3函数,为了到达这个目的,我们需要把原本应该回到的地址改成我们注入字符串的首地址,如此一来,getbuf函数ret后并不会回到test,而是跳转到我们getbuf的栈顶,开始执行我们写入的指令,这些指令的任务就是使CPU去执行函数touch3。

(1)为了使步调跳转到touch3,需要把touch3的起始地址push到栈中
使用disas touch3得到函数touch3的反汇编代码:``

可知touch3的起始地址为0x4018fa。
(2)在执行touch3之前,需要把之前得到的与cookie相等的字符串sval的首地址作为参数1通报给touch3,
而这个首地址我们在之前已经算出来是0x5561dca8
(3)最后只剩下注入字符串的首地址未知了,我们直接从栈顶注入字符串,所以首地址就是getbuf的栈顶,即执行完第一条汇编指令后%rsp所指的地址。如下图:

这时候的%rsp即为我们注入字符串的首地址=0x5561dc78
联合(1)(2)我们可以得出存放在getbuf栈帧中的注入代码为
  1. movq    $0x5561dca8, %rdipushq   0x4018faret
复制代码
再联合(3),得到
目的状态详细为

固然,这段指令需要转化成字节表现,
详细做法如下

那么得到最终的输入字节序列就出来了

最后再验证答案正确性

phase 4

phase4其实就是用另一种方法实现phase2的工作。
要将cookie作为参数传入touch2,可以用以下指令完成
  1. popq %raxmovq %rax, %rdi
复制代码
接下来从栈的状态图一步步来分析,这些指令毕竟都做了什么

在getbuf ret之前,%rsp指向popq %rax ret指令所在的地址

当getbuf ret后,popq %rax ret指令所在的地址被pop到%rip,指示CPU去执行语句popq %rax,而且将%rsp加8,使其指向栈中的前一个值。

popq %rax这句话相当于
movq (%rsp) %rax
addq $8 %rsp

ret后, movq %rax, %rdi 所在的地址被pop到%rip,指示CPU去执行语句 movq %rax, %rdi ,而且将%rsp加8,使其指向栈中的前一个值。

这时我们把前一个值改写成cookie,那么%rsp指向的地址内存放的就是cookie的值。
即(%rsp)==cookie,这样一来,语句popq %rax将cookie的值赋给%rax,并将%rsp加8指向 movq %rax, %rdi 所在的地址。
movq %rax, %rdi 把%rax赋给%rdi,即将要调用的函数touch2的第一个参数,再通过ret指令跳转到touch2。
接下来要做的就是找到这两个gadget的地址了。
popq %rax对应的 字节是 58,
ret 是 c3
movq %rax, %rdi是48 89 c7
所以我们的任务就是将farm.c变成字节序列,然后从中找到58 c3以及48 89 c7 c3这两个序列,记下他们的地址。

搜索58发现么有58 c3,只有58 90 c3,90其实代表的是指令no operation,就是啥都不做,相当于跳过的意思。
所以这里用58 90 c3效果是一样的。记下地址为0x5c

同理记下地址0x51

由此得到输入字节序列为
  1. 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 005c 00 00 00 00 00 00 00fa 97 b9 59 00 00 00 0051 00 00 00 00 00 00 00ec 17 40 00 00 00 00 00
复制代码
level 5

在这一阶段中,我们需要做的就是把字符串的起始地址传送到%rdi,然后调用touch3函数。
因为每次栈的位置是随机的,所以无法直接用地址来索引字符串的起始地址,只能用栈顶地址 + 偏移量来索引字符串的起始地址。从farm中我们可以获取到这样一个gadget :lea (%rdi,%rsi,1),%rax,这样就可以把字符串的首地址传送到%rax。
解题思路:
  1. (1)首先获取到%rsp的地址,而且传送到%rdi (2)其二获取到字符串的偏移量值,而且传送到%rsi (3)lea (%rdi,%rsi,1),%rax, 将字符串的首地址传送到%rax, 再传送到%rdi (4)调用touch3函数
复制代码
(1) 第一步,获取到%rsp的地址

movq %rsp, %rax的指令字节为:48 89 e0, 所以这一步的gadget地址为:0x1c5
(2) 第二步,将%rax的内容传送到%rdi

movq %rax, %rdi的指令字节为:48 89 c7,所以这一步的gadget地址为:0x1a
(3) 第三步,将偏移量的内容弹出到%rax

popq %rax的指令字节为:58, 此中90为nop指令, 所以这一步的gadget地址为:0x5c
(4) 第四步,将%eax的内容传送到%edx

movl %eax, %edx的指令字节为:89 c2, 所以这一步的gadget地址为:0x79
(5) 第五步,将%edx的内容传送到%ecx

movl %edx, %ecx的指令字节为:89 d1,所以这一步的gadget地址为:0x164
(6) 第六步,将%ecx的内容传送到%esi

movl %ecx, %esi的指令字节为:89 ce, 所以这一步gadget地址为:0xcf
(7) 第七步,将栈顶 + 偏移量得到字符串的首地址传送到%rax

这一步的gadget地址为:0x6a
(8) 将字符串首地址%rax传送到%rdi

movq %rax, %rdi的指令字节为:48 89 c7,所以这一步的gadget地址为:0x51
综上,我们可以得到字符串首地址和返回地址之隔断了9条指令,所以偏移量为72个字节,也就是0x48,可以的到如下字符串的输入:



来源:https://blog.csdn.net/xxxxxzz123/article/details/111910589
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

发布主题

专注素材教程免费分享
全国免费热线电话

18768367769

周一至周日9:00-23:00

反馈建议

27428564@qq.com 在线QQ咨询

扫描二维码关注我们

Powered by Discuz! X3.4© 2001-2013 Comsenz Inc.( 蜀ICP备2021001884号-1 )