[pwn基础] Linux安全机制

Canary(栈溢出保护)

Canary:(取名自地下煤矿的金丝雀,因为它能够比旷工更早的发现煤气泄漏,有预警的作用),是一种用于对抗栈溢出攻击的技术,即SSP安全机制,有时候也叫做Stack cookies

Canary的值是上的一个随机数,在程序启动时随机生成并且保存在比函数返回地址更低的位置。(看过栈溢出文章的应该都知道,我们栈溢出就是为了覆盖返回地址),由于我们覆盖到返回地址,那么不可避免的会覆盖到这个随机数(Cannary),程序在函数返回前,检查下Cannary是否被覆盖从而判断是否被栈溢出,从而达到保护栈的目的。

开启关闭Cannary

#默认情况下是会开启Canary保护的

#禁用栈保护
gcc -fno-stack-protector cannary.c -o cannary
#开启局部保护(只保护局部变量中含有char数组的函数)
gcc -fstack-protector cannary.c -o cannary
#开启全局保护
gcc -fstack-protector-all cannary.c -o cannary #命令速记
stack-protector(栈保护)
-f(开启)
-fno(关闭)
-all(全部)

Canary的种类

Cannary通常可以分为3中类型,terminator、random、random XOR,具体的实现有StackGuard、StackShield、Propoliced等

Terminator canaries(终结者金丝雀)

大多数缓冲区溢出攻击都基于某些字符串操作,比如说strcpy,这些操作基本上以字符串终止符结束\x00

终止符金丝雀包含 NULL(0x00)、CR(0x0d)、LF(0x0a)和 EOF(0xff)这四个字符,这四个字符应终止大多数字符串操作,从而使溢出尝试无害。

这可以防止使用 strcpy() 和其他方法的攻击,这些方法在复制空字符时返回。

绕过方法:
攻击者可以使用其已知值覆盖金丝雀,并使用特制值覆盖返回地址,从而绕过这种类型的保护,从而导致代码执行。
当使用非字符串函数来复制缓冲区并且缓冲区内容和缓冲区长度都受攻击者控制时,可能会发生这种情况。

Random cannaries(随机金丝雀)

随机金丝雀是在程序执行时随机选择的。

使用此方法,攻击者无法在程序启动之前通过搜索可执行映像来了解金丝雀值。随机值取自 /dev/urandom(如果可用),如果不支持 /dev/urandom,则通过hash一天中的时间来创建。这种随机性足以阻止大多数预测尝试。

绕过方法:
需要配合信息泄漏漏洞
如果应用程序中存在可用于读取金丝雀值的信息泄漏漏洞,则可以绕过这种保护。

Random XOR cannaries(随机异或金丝雀)

随机异或金丝雀是使用全部或部分控制数据(帧指针+返回地址等)进行异或加干扰的随机金丝雀。

这样,一旦金丝雀或控制数据被破坏,金丝雀值就是错误的,它将导致程序立即终止。

绕过方式总结:

泄露内存中的canary,如通过格式化字符串漏洞打印出来
one-by-one爆破,但是一般是多线程的程序,产生新线程后canary不变才行。最高位为00。
劫持_stack_chk_fail函数,canary验证失败会进行该函数,__stack_chk_fail 函数是一个普通的延迟绑定函数,可以通过修改 GOT 表劫持这个函数。
覆盖线程局部存储TLS中的canary,溢出尺寸比较大可以用。同时修改栈上的canary和TLS中的canary.

NX(No-eXecute)

No-eXecute(NX)表示不可执行,其原理是将数据所在的内存页标识为不可执行。

相当于Windows平台上的DEP(数据执行保护),NX的实现由软件和硬件共同完成

  • 硬件层:利用CPU的NX位,对相应页表项中的第63位进行设置

    • NX=1,不可知性
    • NX=0,可执行
  • 软件层面:需要操作系统支持NX以配置页表,涉及相关API
    • Windows:VirtualAllocVirtualProtect
    • Linux:mmapmprotect
if ${readelf} -l "${1}" 2>/dev/null | grep -q 'GNU_STACK'; then
if ${readelf} -l "${1}" 2>/dev/null | grep 'GNU_STACK' | grep -q 'RWE'; then
echo_message '\033[31mNX disabled\033[m ' 'NX disabled,' ' nx="no"' '"nx":"no",'
else
echo_message '\033[32mNX enabled \033[m ' 'NX enabled,' ' nx="yes"' '"nx":"yes",'
fi
else
echo_message '\033[31mNX disabled\033[m ' 'NX disabled,' ' nx="no"' '"nx":"no",'
fi
#判断是否有GNU_STACK,继续判断是否有RWE权限,否则就是开启了NX保护.

绕过方式:

在Linx中,当装载器将程序载入进内存空间后,将程序的.text节标记为可执行,而其余的数据段.data、.bss、.rodata以及堆栈均为不可执行。

所以:无法用传统的修改GOT表来执行shellcode.

绕过的正确方法:
攻击者可以通过代码重用来执行攻击(ret2libc)。

PIE(ASLR地址随机化)

PIE(Position-Independent Executable) 中文解释为地址无关可执行文件。

也叫做ASLR(地址空间随机化)全称是叫(Address Space Layout Randomization)。

这个搞过逆向的应该都不会陌生,比如搞过Windows逆向或者是iOS/macOS逆向的,我们在逆一个程序的时候。用动态调试器去调试他的时候,他的装载地址或入口点地址,每次重启后都是会变化的,

所以每次都需要在IDA中计算出基地址,然后再加上模块的基址才能在调试器中找到真实的内存地址,在iOS中则每次都要image list -o -f来找装载的地址。

ASLR cat /proc/sys/kernel/randomize_va_space有三种情况:

0:表示全部关闭

1:表示部分开启

2:表示完全开启(在部分开启的基础上增加了heap的随机化)

ASLR Executable PLT Heap Stack Shared Libraries
0 × × × × ×
1 × × ×
2 × ×
2+pie

PIE 位置无关可执行文件,在应用层的编译器上实现,通过将程序编译为位置无关代码PIC,使程序加载到任意位置,就像是一个特殊的共享库。PIE会一定程度上影响性能。

关闭PIE/ALSR(地址随机化)

sudo -s echo 0 > /proc/sys/kernel/randomize_va_space
gcc -fpie -pie -o test test.c // 开启PIE,此时强度为1
gcc -fPIE -pie -o test test.c // 开启PIE,此时为最高强度2
gcc -fpic -o test test.c // 开启PIC,此时强度为1,不会开启PIE
gcc -fPIC -o test test.c // 开启PIC,此时为最高强度2,不会开启PIE #助记
-pie (开启)
-no-pie(关闭)

PIE/ALSR 检查脚本

if ${readelf} -h "${1}" 2>/dev/null | grep -q 'Type:[[:space:]]*EXEC'; then
echo_message '\033[31mNo PIE \033[m ' 'No PIE,' ' pie="no"' '"pie":"no",'
elif ${readelf} -h "${1}" 2>/dev/null | grep -q 'Type:[[:space:]]*DYN'; then
if ${readelf} -d "${1}" 2>/dev/null | grep -q 'DEBUG'; then
echo_message '\033[32mPIE enabled \033[m ' 'PIE enabled,' ' pie="yes"' '"pie":"yes",'
else
echo_message '\033[33mDSO \033[m ' 'DSO,' ' pie="dso"' '"pie":"dso",'
fi
elif ${readelf} -h "${1}" 2>/dev/null | grep -q 'Type:[[:space:]]*REL'; then
echo_message '\033[33mREL \033[m ' 'REL,' ' pie="rel"' '"pie":"rel",'
else
echo_message '\033[33mNot an ELF file\033[m ' 'Not an ELF file,' ' pie="not_elf"' '"pie":"not_elf",'
fi

FORTIFY_SOURCE

FORTIFY_SOURCE(源码增强),这个其实有点类似与Windows中用新版Visual Studio进行开发的时候,当你用一些危险函数比如strcpy、sprintf、strcat,编译器会提示你用xx_s加强版函数。

FORTIFY_SOURCE本质上一种检查和替换机制,对GCC和glibc的一个安全补丁。

目前支持memcpy, memmove, memset, strcpy, strncpy, strcat, strncat,sprintf, vsprintf, snprintf, vsnprintf, gets等。

开启关闭FORTIFY_SOURCE

#默认Ubuntu16.04下是关闭的,测试发现Ubuntu18.04是开启的
gcc -D_FORTIFY_SOURCE=1 仅仅只在编译时进行检查(尤其是#include <string.h>这种文件头)
gcc -D_FORTIFY_SOURCE=2 程序执行时也会进行检查(如果检查到缓冲区溢出,就会终止程序) #助记
FORTIFY_SOURCE(代码增强)
-D 1(开启缓冲区溢出攻击检查)
-D 2(开启缓冲区溢出以及格式化字符串攻击检查) ,通过数组大小来判断替换`strcpy、memcpy、memset`等函数名,来防止缓冲区溢出。

RELRO

RELRO全称:Relocation Read-Only(重定位表只读),之前文章有介绍过动态链接(GOT表、PLT表)需要延迟绑定。

在启用延时绑定时,符号的解析只发生在第一次使用的时候,该过程是通过PLT表进行的,解析完成后,相应的GOT表条目才会修改为正确的函数地址。因此,在延迟绑定的情况下,.got.plt必须是可写的。攻击者就可以通过篡改地址劫持程序。

RELRO就是为了解决延迟绑定的安全问题的,之前可以看到我们很轻松的用调试器修改了GOT表,达到了劫持效果,RELRO会把这些延迟绑定表设置成只读,防止来修改。

RELRO一般有两种形式:

Partial RELRO(部分RELRO):

Ubuntu 16.04开始默认的GCC设置,几乎所有的二进制文件都至少有部分RELRO。这样仅仅只能防止全局变量上的缓冲区溢出从而覆盖到GOT。

(.dynamic、.got)初始化时候标记为只读。

Full RELRO(完全RELRO):

使整个GOT只读,从而无法被覆盖,但是这样会大大增加程序的启动时间,因为需要在启动之前解析所有的符号。

也就是延迟绑定被禁止了,所有的导入符号在开始时候被解析,.got.plt段会被完全初始化为目标函数的最终地址,并且被mprotect标记为只读,但是.got.plt会被合并到.got,也就看不到这些段了,对性能会造成影响。

开启关闭RELRO

#关闭RELRO
-z norelro 禁用relro
#开启RELRO
-z lazy 开启Partial RELRO(部分RELRO)
-z now FULL PARTIAL(完全开启RELRO) #助记
-z (RELRO)
norelro (关闭RELRO)
lazy (开启部分RELRO)
now (完全开启RELRO)

PWN菜鸡交流小分队

最后感谢大家的阅读,本菜鸡也是刚学,文章中如有错误请及时指出。

大家也可以来群里骂我哈哈哈,群里有PWN、RE、WEB大佬,欢迎交流

参考文章 :

http://www.manongzj.com/blog/27-gvafmooopa.html (PWN保护机制详解)

https://www.cnblogs.com/ttxs69/p/pwn_canary.html (PWN之Canary学习)

https://lzeroyuee.cn/2021/02/23/Linux-Pwn-安全机制/ (Linux Pwn - 安全机制)

https://www.redhat.com/en/blog/security-technologies-stack-smashing-protection-stackguard (安全技术:堆栈粉碎保护 (StackGuard))

https://clibre.io/blog/por-secciones/hardening/item/413-proteccion-de-ejecutables-fortify-source (可执行文件保护:FORTIFY_SOURCE)

《CTF权威指南(PWN篇)》

[pwn基础] Linux安全机制的更多相关文章

  1. 【深入浅出Linux网络编程】 “基础 -- 事件触发机制”

    回顾一下“"开篇 -- 知其然,知其所以然"”中的两段代码,第一段虽然只使用1个线程但却也只能处理一个socket,第二段虽然能处理成百上千个socket但却需要创建同等数量的线程 ...

  2. 20155301 滕树晨linux基础——linux进程间通信(IPC)机制总结

    20155301 滕树晨linux基础--linux进程间通信(IPC)机制总结 共享内存 共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在 ...

  3. Linux Namespaces机制

    转自:http://www.cnblogs.com/lisperl/archive/2012/05/03/2480316.html Linux Namespaces机制提供一种资源隔离方案.PID,I ...

  4. linux进程同步机制_转

    转自:Linux进程同步机制 具体应用可参考:线程同步       IPC之信号量 为了能够有效的控制多个进程之间的沟通过程,保证沟通过程的有序和和谐,OS必须提供一 定的同步机制保证进程之间不会自说 ...

  5. Linux保护机制和绕过方式

    Linux保护机制和绕过方式 CANNARY(栈保护) ​ 栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行.用C ...

  6. [pwn基础]动态链接原理

    目录 [pwn基础]动态链接原理 动态链接概念 动态链接调用so例子 GOT(全局偏移表) got表劫持小实验 PLT(延迟绑定) PLT概念 延迟绑定(PLT表) 实战学习 [pwn基础]动态链接原 ...

  7. [pwn基础]Pwntools学习

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

  8. Linux模块机制浅析

    Linux模块机制浅析   Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! ...

  9. android & Linux uevent机制

    Linux uevent机制 Uevent是内核通知android有状态变化的一种方法,比如USB线插入.拔出,电池电量变化等等.其本质是内核发送(可以通过socket)一个字符串,应用层(andro ...

随机推荐

  1. 百度离线人脸识别sdk的使用

    1.1下载sdk运行 百度离线人脸识别sdk的使用 1.2配置环境 添加至项目,可以拖动复制或者以类库形式添加face-resource此文件夹 放到根目录上一层 激活文件与所有dll引用放到根目录嫌 ...

  2. C++的"开始" Hello World! 你好世界!

    # C++的"开始" Hello World! 你好世界! ```C++ // 第一个程序 //代表注释这一行 #include <iostream> //c++专属头 ...

  3. 如何规避容器内做Java堆dump导致容器崩溃的问题

    写在前边 最近公司生产环境的容器云上出了个性能问题,为了做性能分析,使用 JDK 自带的 jmap 收集堆dump,出现了内存溢出导致了容器崩溃. 本篇文章将带你探究,如何规避容器内做堆 dump 导 ...

  4. Django中间件、csrf跨站请求、csrf装饰器、基于django中间件学习编程思想

    django中间件 中间件介绍 什么是中间件? 官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子.它是一个轻量.低级别的插件系统,用于在全局范围内改变Django的输入和输出. ...

  5. QT-图标设置

    记录一下怎么在QT里添加图片,包括exe图标和里面使用的其他图片. 注意这个图片是指ico后缀的图片,去网上搜就有了,一大堆. 随便找的一个在线转换,http://www.bitbug.net/ 新建 ...

  6. 【高并发】不得不说的线程池与ThreadPoolExecutor类浅析

    大家好,我是冰河~~ 今天,我们一起来简单聊聊线程池中的ThreadPoolExecutor类,好了,不多说了,开始进入今天的正题. 一.抛砖引玉 既然Java中支持以多线程的方式来执行相应的任务,但 ...

  7. (十一)React Ant Design Pro + .Net5 WebApi:后端环境搭建-IdentityServer4(三)持久化

    一.前言 IdentityServer配合EFCore持久化,框架已经为我们准备了两个上下文: ConfigurationDbContext:配置数据(资源.客户端.身份等) PersistedGra ...

  8. 『现学现忘』Git基础 — 13、Git的基础操作

    目录 1.Git最基础的使用方式 (1)初始化本地版本库 (2)查看文件的状态 (3)把文件添加到暂存区 (4)把暂存区的内容提交到本地版本库 2.总结本文用到的Git命令 1.Git最基础的使用方式 ...

  9. Docker Compose 的介绍、安装与使用

    什么是 Docker Compose? Compose 是 Docker 官方的开源项目,负责实现Docker容器集群的快速编排,开源代码在 https://github.com/docker/com ...

  10. SD卡之二:SD总线访问模式

    SD 卡是以命令.回应.数据流进行通讯. 1.命令:命令的长度是48位,命令以'0'开始,第2位为'1'表示主机发往SD卡的命令,最后以CRC和结束位'1'结尾. 2.回应:回应的长度是48位或者13 ...