Liunx下关于绕过cancry保护总结

Liunx下关于绕过cancry保护总结

最近一段时间接触了很多关于canary方面的pwn,看了网上的一些资料和例子,总结了一些方法分享一下。

0x00 Linux Canary介绍

首先了解一下Linux的Canary保护机制。Canary是Linux众多安全保护机制中的一种,主要用于防护栈溢出攻击。我们知道,在32位系统上,对于栈溢出漏洞,攻击者通常是通过溢出栈缓冲区,覆盖栈上保存的函数返回地址来达到劫持程序执行流的目的:


针对此种攻击情况,如果在函数返回之前,我们能够判断ret地址是否被改写,若被改写则终止程序的执行,便可以有效地应对攻击。canary如何做到防止栈溢出的发生呢?它的主要思想是在刚进入函数时,在栈上放置一个标志,在函数结束时,判断该标志是否被改变,如果被改变,则表示有攻击行为发生。如下:


canary的位置是随机的,不一定在上图所示的位置。

0x01 Linux Canary实现

Linux程序的Canary保护是通过gcc编译选项来控制的,gcc与canary相关的参数及其意义分别为:
-fstack-protector:启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码
-fstack-protector-all:启用堆栈保护,为所有函数插入保护代码。
-fno-stack-protector:禁用堆栈保护,为默认选项。
我们通过一个简单的例子来了解一下。示例代码如下

#include <stdio.h>
#include <string.h>

void foo (char *src)
{
    char dest[48] = {0};
    strcpy (dest, src);
}

int main()
{
    foo("Hello, world!");
    return 0;
}

将该段代码保存为test.c,然后使用如下命令进行编译:

gcc –o test test.c

然后通过如下命令对test进行反编译:

objdump -d ./test

我们得到foo()的汇编代码如下:

080483eb <foo>:
80483eb:    55                       push   %ebp
80483ec:    89 e5                    mov    %esp,%ebp
80483ee:    57                       push   %edi
80483ef:    83 ec 34                 sub    $0x34,%esp
80483f2:    8d 55 c8                 lea    -0x38(%ebp),%edx
80483f5:    b8 00 00 00 00           mov    $0x0,%eax
80483fa:    b9 0c 00 00 00           mov    $0xc,%ecx
80483ff:    89 d7                    mov    %edx,%edi
8048401:    f3 ab                    rep stos %eax,%es:(%edi)
8048403:    83 ec 08                 sub    $0x8,%esp
8048406:    ff 75 08                 pushl  0x8(%ebp)
8048409:    8d 45 c8                 lea    -0x38(%ebp),%eax
804840c:    50                       push   %eax
804840d:    e8 ae fe ff ff           call   80482c0 <strcpy@plt>
8048412:    83 c4 10                 add    $0x10,%esp
8048415:    90                       nop
8048416:    8b 7d fc                 mov    -0x4(%ebp),%edi
8048419:    c9                       leave  
804841a:    c3                       ret

然后我们使用”-fstack-protector”编译选项重新编译编译:

gcc -o test2 -fstack-protector ./test.c

然后通过相同的命令对test2进行反编译,得到foo()的汇编代码如下:

0804844b <foo>:
804844b:    55                       push   %ebp
804844c:    89 e5                    mov    %esp,%ebp
804844e:    57                       push   %edi
804844f:    83 ec 54                 sub    $0x54,%esp
8048452:    8b 45 08                 mov    0x8(%ebp),%eax
8048455:    89 45 b4                 mov    %eax,-0x4c(%ebp)
8048458:    65 a1 14 00 00 00        mov    %gs:0x14,%eax
804845e:    89 45 f4                 mov    %eax,-0xc(%ebp)
8048461:    31 c0                    xor    %eax,%eax
8048463:    8d 55 c4                 lea    -0x3c(%ebp),%edx
8048466:    b8 00 00 00 00           mov    $0x0,%eax
804846b:    b9 0c 00 00 00           mov    $0xc,%ecx
8048470:    89 d7                    mov    %edx,%edi
8048472:    f3 ab                    rep stos %eax,%es:(%edi)
8048474:    83 ec 08                 sub    $0x8,%esp
8048477:    ff 75 b4                 pushl  -0x4c(%ebp)
804847a:    8d 45 c4                 lea    -0x3c(%ebp),%eax
804847d:    50                       push   %eax
804847e:    e8 9d fe ff ff           call   8048320 <strcpy@plt>
8048483:    83 c4 10                 add    $0x10,%esp
8048486:    90                       nop
8048487:    8b 45 f4                 mov    -0xc(%ebp),%eax
804848a:    65 33 05 14 00 00 00     xor    %gs:0x14,%eax
8048491:    74 05                    je     8048498 <foo+0x4d>
8048493:    e8 78 fe ff ff           call   8048310 <__stack_chk_fail@plt>
8048498:    8b 7d fc                 mov    -0x4(%ebp),%edi
804849b:    c9                       leave  
804849c:    c3                       ret

可以看到,在加了canary保护之后函数开始时,会取gs:0x14处的值,并放在%ebp-0xc的地方(mov %gs:0x14,%eax, mov %eax,-0xc(%ebp)),在程序结束时,会将该值取出,并与gs:0x14的值进行抑或(mov -0xc(%ebp),%eax,xor %gs:0x14,%eax),如果抑或的结果为0,说明canary未被修改,程序会正常结束,反之如果抑或结果不为0,说明canary已经被非法修改,存在攻击行为,此时程序流程会走到__stack_chk_fail,从而终止程序。

0x02 Canary保护绕过方法

从Canary的工作机制,可以总结出绕过Canary保护的方法有:

1.泄露canary。由于Canary保护仅仅是检查canary是否被改写,而不会检查其他栈内容,因此如果攻击者能够泄露出canary的值,便可以在构造攻击负载时填充正确的canary,从而绕过canary检查,达到实施攻击的目的。NJCTF2017的messager就是通过这个方法利用。

2.劫持stack_chk_fail。当canary被改写时,程序执行流会走到stack_chk_fail函数,如果攻击者可以劫持该函数,便能够改变程序的执行逻辑,执行攻击者构造的代码。我们知道,Linux采用的是延迟绑定技术(PLT),如果我们能够修改全局偏移表(GOT)中存储的__stack_chk_fail函数地址,便可以在触发canary检查失败时,跳转到指定的地址继续执行。

3.利用canary在发生栈溢出是的警告信息,需要对__stack_chk_fail函数的执行流程有一定了解,在错误提示中,会将你发生栈溢出的程序名调用输出,其位置位于argv[0],我们可以将argv[0]的地址改写为我们想要获取的内容的地址,使它随着错误提示一起输出。

文章目录
  1. 1. Liunx下关于绕过cancry保护总结
    1. 1.1. 0x00 Linux Canary介绍
    2. 1.2. 0x01 Linux Canary实现
    3. 1.3. 0x02 Canary保护绕过方法
|