理解[K]ASLR

Last Updated: 2023-03-06 08:04:10 Monday

-- TOC --

ASLR概念

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

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必须固定地址)。

Linux下ASLR配置

$ sudo sysctl kernel.randomize_va_space
kernel.randomize_va_space = 2

sysctl命令

测试

#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时不一样。

--enable-default-pie

据说现在有些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 |

查看executable是否为pie

$ 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

Linux KASLR

本文链接:https://cs.pynote.net/se/202302131/

-- EOF --

-- MORE --