学习资料:
- https://ctf-wiki.github.io/ctf-wiki/pwn/linux/mitigation/Canary/
- https://blog.csdn.net/Virtual_Func/article/details/48789947
- http://blog.fourthbit.com/2014/12/23/traffic-analysis-of-an-ssl-slash-tls-session
学习内容:
- Linux-Pwn,安全保护机制
由于 stack overflow 而引发的攻击非常普遍也非常古老,相应地一种叫做 canary 的 mitigation 技术很早就出现在 glibc 里,直到现在也作为系统安全的第一道防线存在。
- PostScript:glibc:The GNU C Library, commonly known as glibc, is the GNU Project’s implementation of the C standard library.
原理
canary 不管是实现还是设计思想都比较简单高效,就是插入一个值,在 stack overflow 发生的 高危区域的尾部,当函数返回之时检测 canary 的值是否经过了改变,以此来判断 stack/buffer overflow 是否发生。
PostScript:gcc 中使用:
|
|
实现原理
开启 Canary 保护的 stack 结构大概如下:
当程序启用 Canary 编译后,在函数序言部分会取 fs 寄存器 0x28 处的值,存放在栈中 %ebp-0x8
的位置。 这个操作即为向栈中插入 Canary 值,代码如下:
|
|
在函数返回之前,会将该值取出,并与 fs:0x28 的值进行异或。如果抑或的结果为 0,说明 canary 未被修改,函数会正常返回,这个操作即为检测是否发生栈溢出。
|
|
这意味可以通过劫持 __stack_chk_fail
的 got 值劫持流程或者利用 __stack_chk_fail
泄漏内容 (stack smash)。
进一步来说,对于 Linux 来说,fs
寄存器实际上指向的是当前函数栈的 TLS
结构中的 stack_guard
,该值由函数 security_init
进行初始化。初始化的值由 glibc
计算,在进入函数的时候就写入了 Kernel
中。
PostScript:
GOT (Global Offset Table):是一个存储在数据区的地址表。当被执行程序试图寻找编译时未知的全局变量时,程序就会寻找这个表。
延迟绑定:即函数第一次被用到时才进行绑定。通过延迟绑定大大加快了程序的启动速度。而 ELF 则使用了PLT (Procedure Linkage Table) 的技术来实现延迟绑定。
TSL (Transport Layer Security):前身是现在已经弃用的 SSL (Secure Sockets Layer),指的是通过计算机网络提供通信安全性的加密协议。详细的概论信息可参见:SSL/TLS Session。一般的协议结构如下图所示:
可见
TLS/SSL
协议是介于应用层与传输层之间的协议。下图解释了建立一个SSL Record
的过程:1 2 3 4 5 6 7 8 9
graph TB; D1{Data} subgraph 1.Fragment Data; D2["|......|"]; end subgraph 2.Compress Data <通常不会有压缩操作>; D3["|......|MAC|<br>Add Message Authentication Code"]; end subgraph 3.Encrypt data; D4["|..........|<br>cipher text"]; end subgraph 4.Add header; D5["|header|...........|<br>TLS record header."]; end D1-->D2; D2-->D3; D3-->D4; D4-->D5
较高层则完成
Handshake
,Change Cipher Spec
,Alert
,Application Data
这样四项任务。其中握手的过程可以用以下的方式来表示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
TLS Handshake +-----+ +-----+ | | | | | | ClientHello | | | o----------------------------> | | | | | | CLIENT | | ServerHello | | SERVER | | [Certificate] | | | | [ServerKeyExchange] | | | | [CertificateRequest] | | | | ServerHelloDone | | | | <----------------------------o | | | | | | | [Certificate] | | | | ClientKeyExchange | | | | [CertificateVerify] | | | | ** ChangeCipherSpec ** | | | | Finished | | | o----------------------------> | | | | | | | | ** ChangeCipherSpec ** | | | | Finished | | | | <----------------------------o | | | | | +-----+ +-----+
Experiment:
尝试以下的 C 语言程序:
|
|
使用以下的命令编译该程序生成 ELF
可执行文件:
|
|
使用 gdb
的 -ex
特性查看生成的可执行文件中 func
函数的汇编代码(也可以使用 objdump -d
):
|
|
可以看到它在 *func+8
的位置在函数栈中加入了 canary
。在 *func+30
与 *func+39
的位置会检测 canary
是否发生了改变,并在发生错误时延迟绑定(在 plt
段)了 gibc
函数 <__stack_chk_fail>
。
绕过技术
泄漏栈中的 Canary
Canary 设计为以字节 \x00
结尾,本意是为了保证 Canary 可以截断字符串。
泄露栈中的 Canary 的思路是覆盖 Canary 的低字节,来打印出剩余的 Canary 部分。 这种利用方式需要存在合适的输出函数,并且可能需要第一溢出泄露 Canary,之后再次溢出控制执行流程。
Experiment:
存在漏洞的示例源代码如下:
|
|