谈谈计算机安全领域的高频词汇 Canary
在研究 OS 攻击检测工具的时候,在社区、Github 等上面会高频出现一个词:Canary,这引发了我的好奇心,为什么在 Cyber Security 领域中 Canary 是一个高频词汇,它是不是计算机安全领域里面的 “Hello World”?这篇文章我就来研究一下这个问题。
历史渊源
煤炭开采行业有一个传统 —— 矿工下矿工作时经常会携带一只金丝雀在身边,这个传统始于 1911 年直到1986年才结束。
为什么会有这样一个反直觉的传统呢?这是因为矿工在煤矿工作时,出于安全需要,他们需要能够及时检测一氧化碳等有毒气体,由于这些有毒气体无色无味,当人体感觉到它们的时再采取措施可能为时已晚。John Scott Haldane 在研究一氧化碳等无色无味有毒气体的时候得出一个结论,建议使用对这些有毒气体更敏感的金丝雀来检测和发现这些有毒气体。所以在煤矿工作时,如果矿工发现携带的金丝雀出现异常甚至死亡的时候,就说明有有毒气体,需要立刻撤离。
为什么 Haldane 选择的是金丝雀呢?这是由于金丝雀独特的生理构造,金丝雀在飞行的时候需要大量的氧气,而为了能够在空气稀薄的高空中飞行,它们的身体结构允许他们在吸气时获得一剂氧气,在呼气时通过在额外的囊储存空气从而获得另一剂氧气。由于这样的生理构造,导致它吸入的空气是其他生物的两倍,所以导致它对一氧化碳等无色无味的气体更加敏感。
当然除了安全问题之外,矿工他们在面对地下枯燥的工作时,对着鸟儿吹口哨、哄它们,把它们当作宠物,也给了他们情感上的陪伴和寄托。
计算机安全领域的 “Canary”
Canary 简介
网络安全领域的 “Canary” 一词被认为起源于上世纪90年代初,Sun Microsystems 最早使用 “Canary”来检测对其公司网络的未经授权的访问。
在网络安全领域,狭义上的 Canary 通常是指嵌入在网站或应用程序中的一段文本或代码。如果 Canary 金丝雀被更改或删除,则表明系统已被破坏,这有助于在安全漏洞造成任何损害之前提醒管理员。有几种不同类型的 Canary 金丝雀可以用于网络安全,一些常见的类型包括:
- 文本金丝雀(Text Canary):这些只是嵌入在网站或应用程序中的文本片段。如果文本被更改或删除,则表明系统已被破坏。
- 代码金丝雀(Code Canary):这些是嵌入在网站或应用程序中的代码片段。如果代码被更改或删除,则表明系统已被破坏。
- 哈希金丝雀(Hash Canary):这些是存储在网站或应用程序中的数据的加密哈希。如果哈希值改变了,则表明数据被修改。
检测 Buffer Overflow 的 Canary
函数栈帧的内存布局如下图所示:
简单建立下面函数的栈帧:
int foo() {
int a; /* integer */
int *b; /* pointer to integer */
char c[10]; /* character arrays */
char d[3];
b = &a; /* initialize b to point to location of a */
strcpy(c,get_c()); /* get c from somewhere, write it to c */
*b = 5; /* the data at the point in memory b indicates is set to 5 */
strcpy(d,get_d());
return *b; /* read from b and pass it to the caller */
}
+ high +----+------------+
| | | |
| | +------------+
| | d | |
| | +------------+
| | | |
| +-----------------+
| | | |
| | +------------+
| | | |
| | +------------+
| | | |
| | +------------+
| | | |
| | +------------+
| | c | |
| | +------------+
| | | |
| | +------------+
| | | |
| | +------------+
| | | |
| | +------------+
| | | |
| | +------------+
| | | |
| +-----------------+
| | | |
| | +------------+
| | | |
| | b +------------+
| | | |
| | +------------+
| | | |
| +-----------------+
| | | |
| | +------------+
| | | |
| | a +------------+
| | | |
| | +------------+
| | | |
| +-----------------+
| | | |
| | | |
| | | |
| |CTLI| |
| | | |
| | | |
| | | |
| +-----------------+
| | | |
| | | |
| |RETA| |
| | | |
| | | |
v low +----+------------+
在这种假设的情况下,如果向数组 c 写入超过10个字节,或者向字符数组 d 写入超过13个字节,那么多余的字节将溢出到整数指针 b,然后溢出到整数 a,然后溢出到控制信息,最后溢出到返回地址。通过覆盖 b,指针可以引用内存中的任何位置,从而导致从任意地址读取。通过覆盖 RETA,可以让函数执行其他代码(当它试图返回时),既可以是现有的函数,也可以是在溢出期间写入堆栈的代码。
简而言之,对 c 和 d 的处理不当,比如上面的无界 strcpy()
调用,可能允许攻击者通过直接影响分配给 c 和 d 的值来控制程序。缓冲区溢出保护的目标是以尽可能少的干扰方式检测这个问题。
缓冲区溢出保护是通过对编译器的更改来实现的。因此,保护可以改变堆栈帧上数据的结构。上面函数的变量被更安全地重新排列:数组 c 和 d 首先在堆栈帧中分配,堆栈帧将整数 a 和整数指针 b 放在它们之前的内存中。所以堆栈帧变成:
+----+-------------+
| | |
| +-------------+
| | |
| b +-------------+
| | |
| +-------------+
| | |
+------------------+
| | |
| +-------------+
| | |
| a +-------------+
| | |
| +-------------+
| | |
+------------------+
| | |
| +-------------+
| d | |
| +-------------+
| | |
+------------------+
| | |
| +-------------+
gcc re-arrange | | |
data structure | +-------------+
| | |
+--------------> | +-------------+
| | |
| +-------------+
| | |
| c +-------------+
| | |
| +-------------+
| | |
| +-------------+
| | |
| +-------------+
| | |
| +-------------+
| | |
+------------------+
| | |
| | |
| | |
|CTLI| |
| | |
| | |
| | |
+------------------+
| | |
| | |
|RETA| |
| | |
| | |
+----+-------------+
由于不可能在不破坏生成的代码的情况下移动 CTLI 或 RETA,因此采用了另一种策略。一个额外的信息,称为“Canary”(CNRY),被放置在堆栈帧中的缓冲区之后。当缓冲区溢出时,金丝雀值被改变。因此,为了有效地攻击程序,攻击者必须留下明确的攻击迹象。编译器修改之后的堆栈帧为:
+----+-------------+
| | |
| +-------------+
| | |
| b +-------------+
| | |
| +-------------+
| | |
+------------------+
| | |
| +-------------+
| | |
| a +-------------+
| | |
| +-------------+
| | |
+------------------+
| | |
| +-------------+
| d | |
| +-------------+
| | |
+------------------+
| | |
| +-------------+
gcc add canary | | |
| +-------------+
| | |
+----------------> | +-------------+
| | |
| +-------------+
| | |
| c +-------------+
| | |
| +-------------+
| | |
| +-------------+
| | |
| +-------------+
| | |
| +-------------+
| | |
+------------------+
|CNRY| |
+------------------+
| | |
|CTLI| |
| | |
| | |
| | |
+------------------+
| | |
| | |
|RETA| |
| | |
| | |
+----+-------------+
Canary 保护 Buffer Overflow 的原理就是在每个函数的末尾都有一条指令,该指令从 RETA 指示的内存地址继续执行。在执行此指令之前,对 CNRY 进行检查以确保它没有被更改。如果 CNRY 的值没有通过测试,则立即结束程序执行。本质上,故意的攻击和无意的编程错误都会导致程序中止。
广义“Canary”
目前在计算机安全领域,Canary 一般用来表示一段用于检测恶意活动的代码或工具,这些代码或工具通常隐藏在应用程序中,如果被攻击者修改它会发生变化,这可以帮助应用程序所有者和管理员检测他们的系统或应用是否已被破坏。
当然,广义的 “Canary” 还有另外一种用法,也就是很多安全领域的项目、社区等会自然而然的想到用 “canary” 作为项目或社区的名称,例如 Red Canary、Thinkst Canary、CANARIE、Canarytokens。