canary简介:
canary是一种用来防护栈溢出的保护机制。其原理是在一个函数的入口处,先从fs/gs寄存器中取出一个4字节(eax)或者8字节(rax)的值存到栈上,当函数结束时会检查这个栈上的值是否和存进去的值一致,若一致则正常退出,如果是栈溢出或者其他原因导致canary的值发生变化,那么程序将执行___stack_chk_fail函数,继而终止程序。
所以如果有一道题开启了canary保护,但不知道canary的值,就不能够进行ROP来劫持程序流程,那么这道题就无法解决了。
Canary绕过方式一般canary有两种利用方式
1.爆破canary
2.如果存在字符串格式化漏洞可以输出canary并利用溢出覆盖canary从而达到绕过
这里我们采用第二种利用方法:
printf 属于可变参数函数,函数调用者可任意指定参数和数量,这也是漏洞产生的原因。
printf()函数的一般形式为:
printf(“format”,输出列表)
format参考列表:
参考https://blog.csdn.net/py_1995/article/details/84031480
例题源码
程序提供了shell
检查下程序保护
开启了NX以及Canary保护
载入IDA查看流程
发现有格式化字符串漏洞,但是对照源码发现程序结束以后多出一步比较,所以判断此处的v6就是canary保护生成的随机参数,我们也可以在汇编代码处看出
因为v6的地址是esp+3Ch 而这里 mov edx,[esp+3ch]就可以知道edx里面存在的就应该是v6的值了
看到这里利用edx中获取的v6的值与large gs:14h进行 xor判断,如果不相等则 call ___stack_chk_fail 退出程序。由此可以判断出v6就是canary保护生成的随机参数。
找出canary值的思路:
根据canary特性知道canary也会进行压栈所以canary的值应该会在栈的附近,我们就可以canary赋值给edx之后进行下断,通过edx的值来得到canary的值。
把程序载入gdb进行调试
在printf处下断
运行程序,观察我们输入的字符在栈堆中的位置
尝试打印该地址内存
可以看到输出AAAA的偏移为5,printf打印出的应该是偏移后的下一位地址
在IDA中查看xor的地址进行下断
继续运行程序查看edx的值
我们查看之前printf的内存地址
发现edx 0x9b0cff00的偏移为15,所以可以构造当前的canary的值
成功的泄露出当前的canary的值,但是要注意的是每次程序初始化canary的值都是不一样的,所以我们因该写一个exp
from pwn import *
p = process(’./binary_200’)
leak_canary = ‘%15$x’
p.sendline(leak_canary)
canary = int(p.recv(),16)
print hex(canary)
泄露出了canary的值,我们可以通过gets溢出到canary处覆盖canary的值从而达到绕过canary的目的
计算第一次gets与canary的偏移
因为canary的赋值是从v6而来所以计算v5与v6的偏移即可
得到偏移为40
我们的最终目的是为了获取shell,在IDA里面可以看见程序提供shell
我们还需溢出第二个gets到ret的位置并且覆盖ret地址为当前sh地址
偏移为当前canary地址到ret地址
得到v6的地址为
因为这里的canary已经被覆盖
而v6距离ebp为0x4
所以此时只需要计算v6到ebp的偏移即可
构造的exp
运行脚本得到shell本题结束。
题目链接:https://bamboofox.cs.nctu.edu.tw/courses/3/challenges/61