Last Updated: 2023-03-06 08:04:10 Monday
-- TOC --
Address Space Layout Randomization (ASLR) is a security technique used in operating systems, first implemented in 2001. The current versions of all major operating systems (iOS, Android, Windows, macOS, and Linux) feature ASLR protection.
Address Space Layout Randomization (ASLR) is primarily used to protect against buffer overflow attacks. In a buffer overflow, attackers feed a function as much junk data as it can handle, followed by a malicious payload. The payload will overwrite data the program intends to access. Instructions to jump to another point in code are a common payload. The famous JailbreakMe method of jailbreaking iOS 4, for example, used a buffer overflow attack, prompting Apple to add ASLR to iOS 4.3.
Buffer overflows require an attacker to know where each part of the program is located in memory. Figuring this out is usually a difficult process of trial and error. After determining that, they must craft a payload and find a suitable place to inject it. If the attacker does not know where their target code is located, it can be difficult or impossible to exploit it.
ASLR works alongside virtual memory management to randomize the locations of different parts of the program in memory. Every time the program is run, components (including the stack, heap, and libraries) are moved to a different address in virtual memory. Attackers can no longer learn where their target is through trial and error, because the address will be different every time. Generally, applications need to be compiled with ASLR support, but this is becoming the default, and is even required on Android 5.0 and later.
PIE,Position Independent Executable。
2000年早期及以前,PIC用于动态库。对于可执行程序来讲,仍然是使用绝对地址链接,它可以使用动态库,但程序本身的各个segment地址仍然是固定的。随着ASLR的出现,可执行程序运行时各个segment的虚拟地址也能够随机分布,这样就让攻击者难以预测程序运行地址,让缓存溢出攻击变得更困难。
Linux使能ASLR(=2)后,会检查可执行程序是否为PIE的可执行程序。gcc通过-fpie -pie
产生PIE可执行程序(详解gcc编译选项)。如果是PIE程序,所有segment都是随机地址,如果不是PIE程序,只有部分segment可以随机地址(.data .text .bss必须固定地址)。
$ sudo sysctl kernel.randomize_va_space
kernel.randomize_va_space = 2
2
表示完全随机化,默认值。1
表示仅对共享库,栈,堆,mmap和vdso地址进行随机化处理。0
关闭ASLR功能。#include <stdio.h>
#include <stdlib.h>
int bss;
int data = 1;
int main() {
int stack = 2;
int *heap = malloc(0);
printf("text: %p\n", main);
printf("data: %p\n", &data);
printf("bss : %p\n", &bss);
printf("heap: %p\n", heap);
printf("stack:%p\n", &stack);
free(heap);
return 0;
}
用-fpie -pie
选项编译后,多次运行:
$ sudo sysctl kernel.randomize_va_space
kernel.randomize_va_space = 2
$ ./a.out
text: 0x55cf556d9159
data: 0x55cf556dc03c
bss : 0x55cf556dc044
heap: 0x55cf56ba82a0
stack:0x7ffc39e14c74
$ ./a.out
text: 0x55c323931159
data: 0x55c32393403c
bss : 0x55c323934044
heap: 0x55c3242b82a0
stack:0x7ffd934b66b4
$ ./a.out
text: 0x5570e1376159
data: 0x5570e137903c
bss : 0x5570e1379044
heap: 0x5570e26192a0
stack:0x7fffe8472044
每次运行时,这几个segment的地址都不同。观察发现data和bss的地址差每次都一样,其实,它俩本来就在一起,是一个segment:
$ readelf -l a.out
Elf file type is DYN (Position-Independent Executable file)
Entry point 0x1070
There are 13 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002d8 0x00000000000002d8 R 0x8
INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000006b8 0x00000000000006b8 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x0000000000000229 0x0000000000000229 R E 0x1000
LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000
0x00000000000000dc 0x00000000000000dc R 0x1000
LOAD 0x0000000000002de0 0x0000000000003de0 0x0000000000003de0
0x0000000000000260 0x0000000000000268 RW 0x1000
DYNAMIC 0x0000000000002df8 0x0000000000003df8 0x0000000000003df8
0x00000000000001e0 0x00000000000001e0 RW 0x8
NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000040 0x0000000000000040 R 0x8
NOTE 0x0000000000000378 0x0000000000000378 0x0000000000000378
0x0000000000000044 0x0000000000000044 R 0x4
GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000040 0x0000000000000040 R 0x8
GNU_EH_FRAME 0x0000000000002038 0x0000000000002038 0x0000000000002038
0x0000000000000024 0x0000000000000024 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000002de0 0x0000000000003de0 0x0000000000003de0
0x0000000000000220 0x0000000000000220 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .data.rel.ro .dynamic .got .got.plt .data .bss
06 .dynamic
07 .note.gnu.property
08 .note.gnu.build-id .note.ABI-tag
09 .note.gnu.property
10 .eh_frame_hdr
11
12 .init_array .fini_array .data.rel.ro .dynamic .got
注意这一行:05 .init_array .fini_array .data.rel.ro .dynamic .got .got.plt .data .bss
。
测试非PIE时的情况:
$ gcc test.c
$ ./a.out
text: 0x401146
data: 0x404034
bss : 0x40403c
heap: 0x11432a0
stack:0x7ffd13fd15c4
$ ./a.out
text: 0x401146
data: 0x404034
bss : 0x40403c
heap: 0x22dc2a0
stack:0x7ffdc6b52884
$ ./a.out
text: 0x401146
data: 0x404034
bss : 0x40403c
heap: 0x6812a0
stack:0x7ffed25ad1d4
text和data(bss)不在发生变化。
测试PIE但ASLR=0时的情况:
$ sudo sysctl kernel.randomize_va_space=0
kernel.randomize_va_space = 0
$ gcc -fpie -pie test.c
$ ./a.out
text: 0x555555555159
data: 0x55555555803c
bss : 0x555555558044
heap: 0x5555555592a0
stack:0x7fffffffdf44
$ ./a.out
text: 0x555555555159
data: 0x55555555803c
bss : 0x555555558044
heap: 0x5555555592a0
stack:0x7fffffffdf44
$ ./a.out
text: 0x555555555159
data: 0x55555555803c
bss : 0x555555558044
heap: 0x5555555592a0
stack:0x7fffffffdf44
segment地址不再变化,但这些固定的地址,与非PIE但ASLR=2时不一样。
据说现在有些Linux中的gcc,在编译的时候,就带上了--enable-default-pie
参数。
Your distro configured gcc with --enable-default-pie, so it's making position-independent executables by default, (allowing for ASLR of the executable as well as libraries). Most distros are doing that, these days.
检查gcc编译参数:
$ gcc -v |& grep <keyword>
gcc -v向stderr输出,
|&
的效果是2>&1 |
。
$ file a.out
a.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=2c841f46e281f9b8782f3ef70f5383dd46fd46f3, for GNU/Linux 3.2.0, not stripped
本文链接:https://cs.pynote.net/se/202302131/
-- EOF --
-- MORE --