PWN题型之对抗Canary技术

5 篇文章 2 订阅

前言

参考资料:

b站:https://www.bilibili.com/video/BV1Yf4y197Wi?p=5 漏洞利用的方式讲了很多,不过需要掌握基础知识
看雪:https://www.kanxue.com/book-57-857.htm jin讲了原理,讲了如何运行调试,适合新手,不过要收费。


0x1 :什么是Canry保护

想必大家已经做了一些最基本的栈溢出题目,它们利用的就是你输入的大小大于它所能存储的大小,然后来覆盖ebp,进而修改返回地址来达到目的。然而随着这种漏洞的产生,相应的就产生了阻止这种漏洞利用的方式,于是后面就出现Canary保护机制,它的出现导致了栈无法直接溢出了。那么Canary保护到底是怎么进行的呢?
在这里插入图片描述

Canary保护:在当你覆盖ebp之前时会进行一个验证,这个验证的内容是之前传入的,如果这个内容与之前的不一样,那么它就会跳转到一个叫_stack_chk_fail的函数当中,就不会向下溢出覆盖返回地址,导致直接的栈溢出无法实现。下面就以一个实际的程序为例详细谈谈。

下图是开启Canary保护 的一个程序 的反汇编的main函数 的代码,
在这里插入图片描述

首先可以看到把 fs段的0x28 先存放到rax当中 然后保存到[rbp-0x8]里面。rbp-0x8是什么?它不就是的就在rbp上面一个的一个位置吗,如果你的填充了溢出的话是就会导致rbp-8位置的数值发生了改变。

在出栈的时会进行一个判断:它将之前存放到[rbp-0x8]里面的内容存放到rdx当中,与之前的fs段的0x28进行xor异或判断,如果相等的话就执行je跳转到<main + 94>,如果不相等就会执行call指令,就会跳转到_stack_chk_fail函数当中。

关于跳转的过程(xor 和 je指令)再在这详细谈一谈
xor命令:异或,如果a、b两个值相等则为0,不同则为1
je指令:只在 ZF = 1的时候才会跳转,跳转到leave指令当中,才能避免执行call指令


在这里插入图片描述
ZF又是什么?上面是eflag标准寄存器的低16位的结构,每一个标志位都有其专门的含义。ZF是其中的的一个标志位==>0标准位,作用是判断前面那条指令是否为0,若为0则为真即ZF = 1,若不为0则为假即ZF = 0。

结合前面的所以内容可以得到的是:
在这里插入图片描述

另外再给出一种确定ZF标志位的值的方法:

查看执行xor命令以后的标志位寄存器的数值,然后将该数值转换成二进制,ZF标志位在第七位上,然后看该二进制的第七位是0还是1。(在gdb中用 i r 命令查看寄存器里面的内容)。同样也可以得到相同的结果。
这是正常情况下的执行了xor命令后的eflag寄存器的情况,
在这里插入图片描述

在这里插入图片描述
可以发现第七位的结果为1,所以ZF = 1,可执行je指令

这是溢出并修改了Canary值以后的标准寄存器:
在这里插入图片描述
在这里插入图片描述
可以发现ZF = 0,所以就会导致je不跳转,执行call命令。



0x2 :对抗思路

既然是添加了一个判断的数值,那么我们该怎么样来绕过这个保护机制呢?下面讲讲利用思路,对于有些的实现我还是存在不少问题。

(1)直接泄露Canary的值,比如你第一次溢出的时候知道了该Canary的值,然后第二次溢出时你原封不动的填充进去不就可以。或者是利用某些漏洞的任意读的功能(像格式化字符串漏洞就有任意的读的能力)来读取Canary的值然后改写以此来通过它的校验。
在这里插入图片描述

(2)直接泄露fs : 0x28内的数值,Canary的校验不是最开始是由fs : 0x28决定的吗。这里同样可以利用某漏洞任意读的功能,那么我们就可以读取fs : 0x28位置里的内容。如果我们存在任意读,并且已经算出来libc的基址时是可以计算出这个位置的地址的,fs : 0x28的地址和 libc里面的基地址的偏移是一样的,是你每次电脑重启之后都是一样的。
在这里插入图片描述

(3)直接改掉fs : 0x28里面的数值,这样Canary的值想是什么就是什么了。

(4)劫持_stack_chk_fail函数,失败不是会跳转到_stack_chk_fail函数里面吗?这样那么就利用这个函数里面的漏洞直接泄露flag的地址。(具体的我在下面会进行讲解),

(5)stack smashing技术泄露内存地址(详细内容请看下文)




0x3 :格式化字符串漏洞

等我写完这个漏洞的利用后就附上链接,




0x4 :smashing技术的利用

基础知识 :

首先我们需要讲讲这个_stack_chk_fail这个函数,这个函数的源代码就不讲了,就谈谈这个函数的功能,它会将参数argv[0]的内容打印输出,而在里面存放的内容正好是这个程序的名字。如下图,只要溢出覆盖跳转了就会打印下面带*的那一行
在这里插入图片描述
那argv到底是什么?大家都知道main函数吧,我们是不是就是直接int main(),实际上main函数是有参数的,这个参数可以认为是main函数的形式参数,c语言规定main函数的参数只能有两个,一般写为argc和argv。argc参数表示了命令行中参数的格数。注意:文件名也算一个参数。这就导致了argv[0]的第0个参数里的内容是这个文件的名字。

打印argv[0]处的内容,那如果我们能够修改这个地方的值,改成读取的flag的地址,那么我们是不是就可以直接看到flag的内容了,而我们需要的是知道这个参数的位置在 输入时 距离栈顶的长度,这样你输入时,一直向下覆盖,然后覆盖成其他地址就可以打印出其他内存地址的信息了。



流程图 :

在这里插入图片描述

前提条件:

(1)存在溢出条件。

(2)存在flag.txt文件(应该是要打开的)。

说明: 其实这个并不只是可以泄露flag文件里面的内容,你也可以泄露其他地址的内容,然后与其他漏洞结合起来。




实例分析 :

题目来源: jarvisoj里面的Smashes

程序分析:64位程序,开启了Canary保护、NX保护、fortify保护。打开IDA,查看一下源代码
在这里插入图片描述
代码分析:存在一个gets函数输入v3的值,但是由于存在Canary保护,显然不可以直接溢出了。接着看,可以发现printf函数将v3的内容打印出来了,接着用f12可以看到存在CTF这样flag的字样,就是我们需要读取的内容,不过本地和服务器上面的具体内容显然是不一样的。

接下来就是找到argv距离esp的距离了,打开gdb,下断点到getc函数处。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
也就是说你从rsp到你要修改的地方距离536个字节,中间的536填充随机数。接下来就是找到flag的文件的地址了,修改成ida里面的那个flag地址,但是发现并未泄露出flag,后面才知道程序执行过程中修改了flag。但是这里需要注意的是flag文件它有可能会被封,然后打开gdb查看一下
在这里插入图片描述
果然存在,所以只要我们覆盖的地址改成0x400d2就可以读取flag了。
exp:

from pwn import *

#r = process('./smashes')
r = remote("pwn.jarvisoj.com",9877)

flag_addr=0x400d20                                                                                              
payload='a' * 536 + p64(flag_addr)

r.recvuntil("What's your name? ")
r.sendline(payload)

r.recvuntil("Please overwrite the flag: ")
r.sendline("aaaa")
r.interactive()

这里有一个坑我找了半天(具体是为什么我暂时还不知道),这个flag地址是0x400d20 而不是0x400d20 ,地址是21的话会少一个P,然后我还以为一直是对的。
在这里插入图片描述




0x4 :劫持_stack_chk_fail函数




总结

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值