常见的Linux防护措施

检查防护措施

pwndbg 自带 checksec

1
2
3
4
5
6
7
8
pwndbg> checksec
[*] '/ctf/work/datastore'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

ASLR

概念

ASLR (Address Space Layout Randomization), 地址空间配置随机加载,简称地址随机化 , 是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的的一种技术

有以下三种情况

1
2
3
0 - 表示关闭进程地址空间随机化。
1 - 表示将mmap的基址, stack和vdso页面随机化。
2 - 表示在1的基础上增加栈(heap)的随机化。

可以防范基于 Ret2libc 方式的针对 DEP 的攻击。ASLR 和 DEP 配合使用,能有效阻止攻击者在堆栈上运行恶意代码。

关闭 ASLR

为了方便我们调试,可以在自己的系统上关闭 ASLR 来确认偏移等等

Ubuntu 下:

1
sudo sysctl -w kernel.randomize_va_space=0

绕过 ASLR

程序信息泄露: 目前广泛应用在操作系统的地址随机化多为粗粒度的实现方式,同一模块中的所有代码与数据的相对偏移固定。只需要通过信息泄露漏洞将某个模块中的任一代码指针或者数据指针泄露,即可通过计算得到此模块中任意代码或数据的地址

Canary

Canary

没想到吧,👴直接抄

绕过 Canary

程序信息泄露

NX

NX(Non-eXecute) 位是一种针对 shellcode 执行攻击的保护措施,意在更有效地识别数据区和代码区。通过在内存页的标识中增加” 执行” 位,可以表示该内存页是否执行,若程序代码的 EIP 执行至不可运行的内存页,则 CPU 将直接拒绝执行” 指令” 造成程序崩溃。

在 Linux 中,当装载器把程序装载进内存空间后,将程序的.text 段标记为可执行,而其余的数据段 (.data, .bss等) 以及栈、堆均不可执行。当攻击者在堆栈上部署自己的 shellcode 并触发时,只会直接造成程序的崩溃。

工作原理如图:

20200805154723

👨‍🦳悟辣,就是不给可写可执行段嘛

关闭 / 开启 NX

gcc 编译器默认开启了 NX 选项,如果需要关闭 NX 选项,可以给 gcc 编译器添加 - z execstack 参数。

1
2
3
gcc -o test test.c                  // 默认情况下, 开启NX保护
gcc -z execstack -o test test.c // 禁用NX保护
gcc -z noexecstack -o test test.c // 开启NX保护

在 Windows 下,类似的概念为 DEP(数据执行保护), 在最新版的 Visual Studio 中默认开启了 DEP 编译选项

绕过 NX

代码重用攻击,使用现有代码构造自身所需控制流。

PIE

PIE(Position-Independent Executable, 位置无关可执行文件) 技术与 ASLR 技术类似,ASLR 将程序运行时的堆栈以及共享库的加载地址随机化,而 PIE 技术则在编译时将程序编译为位置无关 , 即程序运行时各个段加载的虚拟地址也是在装载时才确定。

关闭 PIE

gcc 编译时加入参数 -no-pie

1
gcc -no-pie code.c -o code

绕过 PIE

  • 程序信息泄露: 同 ASLR, 通过信息泄露漏洞将某个模块中的任一代码指针或者数据指针泄露,即可通过计算得到此模块中任意代码或数据的地址
  • 部分写入: PIE 存在一个缺陷,那就是 PIE 的随机化只能影响到单个内存页。通常来说,一个内存页大小为 0x1000, 所以最后的 3 位 16 进制数是不会变化的,我们就可以通过程序信息泄露或部分写入来绕过 PIE。

RELRO

RELRO(RELocation Read-Only, 重定位只读), 此技术主要针对 GOT 改写的攻击方式。分为部分 RELRO(Partial RELRO) 与完全 RELRO(Full RELRO) 两种

  • 部分 RELRO: 在程序装入后,将其中一段 (如.dynamic) 标记为只读,防止程序的一些重定位信息被修改
  • 完全 RELRO: 在部分 RELRO 的基础上,在 程序装入时,直接解析完所有符号并填入对应的值,此时所有的 GOT 表项都已初始化,且不装入 link_map_dl_runtime_resolve 的地址 (二者都是程 序动态装载的重要结构和函数)。

设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对 GOT(Global Offset Table)攻击。RELRO 为” Partial RELRO”, 说明我们对 GOT 表具有写权限。

关闭 / 开启 RELRO

gcc 编译参数

1
2
3
4
gcc -o test test.c                  // 默认情况下, 是Partial RELRO
gcc -z norelro -o test test.c // 关闭, 即No RELRO。
gcc -z lazy -o test test.c // 部分开启, 即Partial RELRO
gcc -z now -o test test.c // 全部开启, 即
  • -z now -z norelro, 立即绑定,但不添加 PT_GNU_RELRO 段,.got.plt.got 都可写
  • -z relro, 延时绑定,添加 PT_GNU_RELRO 段,只有.got 只读,.got.plt 依然可写
  • -z now, 立即绑定,添加 PT_GNU_RELRO 段,.got 只读,.got.plt 节取消 (plt 直接调用.got 节地址了)

绕过 RELRO

改写 glibc 中其他函数指针