Satoru Takeuchi
Posted on September 5, 2019
Preface
This article explains two features in Linux, Address Space Location Randomization(ASLR) and Kernel ASLR (KASLR). You can read this article easily if you have some knowledge of memory address
and pointer.
ASLR
Hackers have plenty of ways to attack computer systems, for example executing unauthorized code and accessing unauthorized code. Many of these kinds of attacks require the address of the target code and the target data.
Without ASLR, the program's code and data are loaded on the fixed address on execution(*1). So, if you have these program's execution files, attackers can get the target addresses easily.
*1: As an exception, dynamic shared libraries are loaded to arbitrary addresses.
ASLR is one of the features to mitigate these kinds of attacks. If the programs enable ASLR, their code and data are loaded at random place. So it's difficult for attackers to get these addresses(*2).
*2: There are many ways to bypass ASLR and KASLR. However, I omit to explain how to do it here.
ASLR is not implemented by hardware, but by software (Linux kernel). When ASLR-enabled programs are loaded in virtual memory address space, Linux locates their code/data segments at random.
If code/data is accessed not directly (for example 0x10000000
), but indirectly (for example base address + 0x100000000
), it's easy to access to these. It's because Linux kernel just changes base address
. Heap and stack, they are allocated at the loading time and be accessed with base addressed by nature, are such kinds of data.
On the other hand, how about code/data that are accessed directly on its execution? These are called position-dependent code/data
and Linux doesn't locate these at random addresses.
Here is an example.
$ sleep 10000 &
[1] 7951
$ sleep 10000 &
[2] 7952
$ cat /proc/7951/maps # sleep(pid=7951)'s memory map
00400000-00407000 r-xp 00000000 00:16 207201 /bin/sleep # code
00606000-00607000 r--p 00006000 00:16 207201 /bin/sleep # read-only data
00607000-00608000 rw-p 00007000 00:16 207201 /bin/sleep # readable and writable data
009aa000-009cb000 rw-p 00000000 00:00 0 [heap] # heap
...
7ffc60378000-7ffc60399000 rw-p 00000000 00:00 0 [stack] # stack
$ cat /proc/7952/maps # sleep(pid=7952)'s memory map
00400000-00407000 r-xp 00000000 00:16 207201 /bin/sleep
00606000-00607000 r--p 00006000 00:16 207201 /bin/sleep
00607000-00608000 rw-p 00007000 00:16 207201 /bin/sleep
00cf1000-00d12000 rw-p 00000000 00:00 0 [heap]
...
7ffd1a406000-7ffd1a427000 rw-p 00000000 00:00 0 [stack]
...
Here the addresses of stack and heap are randomized, but the addresses of code and data are not randomized.
To randomize code/data segments, programs should be compiled as position-independent. Here position-independent code is generally called PIC and programs that all code/data are position-independent are called position-independent executable(PIE).
To build PIE by gcc
, -fpic
option is used. The programs built with this option, all data/code are located at random addresses.
The following example is snooping the address map information of two ssh, that is a PIE.
$ cat /proc/7981/maps
5626d3d9c000-5626d3e45000 r-xp 00000000 00:16 867533 /usr/bin/ssh
5626d4045000-5626d4048000 r--p 000a9000 00:16 867533 /usr/bin/ssh
5626d4048000-5626d4049000 rw-p 000ac000 00:16 867533 /usr/bin/ssh
5626d4049000-5626d404c000 rw-p 00000000 00:00 0
5626d5505000-5626d5544000 rw-p 00000000 00:00 0 [heap]
...
7ffe2bd73000-7ffe2bd94000 rw-p 00000000 00:00 0 [stack]
...
$ cat /proc/7985/maps
5616589f2000-561658a9b000 r-xp 00000000 00:16 867533 /usr/bin/ssh
561658c9b000-561658c9e000 r--p 000a9000 00:16 867533 /usr/bin/ssh
561658c9e000-561658c9f000 rw-p 000ac000 00:16 867533 /usr/bin/ssh
561658c9f000-561658ca2000 rw-p 00000000 00:00 0
561658d81000-561658dc0000 rw-p 00000000 00:00 0 [heap]
...
7ffc75060000-7ffc75081000 rw-p 00000000 00:00 0 [stack]
...
You can see all memory segments are located at random.
PIE hadn't used in the past because it's slower than position-dependent executable because of extra works needed to addressing memories. However, recent major Linux distributions tend to make all or most their binaries relocatable. One of the reason is that the robustness of security is more important than the subtle improvement of performance.
For example, Ubuntu's binaries are PIE by default.
You can use file
command to confirm whether a program is PIE or not. Here is an example.
$ file /usr/bin/ssh
/usr/bin/ssh: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=ecf7433a7d26461fc1bc7\
a6b6a4eba868e685839, stripped # `...share object` means PIE
$ file /bin/bash
/bin/bash: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=04eca96c5bf3e9a300952a29ef3\218f00487d37b, stripped # `... executable` means position-dependent executable.
Linux enables ASLR by default. To disable ASLR, set 0
to kernel.randomize_va_space
sysctl parameter. Here 2
means enabling this feature and it's the default configuration. I omit to explain the meaning of 1
about this parameter. This parameter influences all the binaries in the system.
KASLR
KASLR can be considered as the kernel version of ASLR. Its concept is the same as ASLR. With enabling KASLR, the Linux kernel locates its code/data at the random addresses at every boot time. Of course, the Linux kernel should be built as PIE.
The effect of KASLR can be confirmed by an address of a symbol in the kernel. All symbols and their addresses are written in so-called System.map
file. On Ubuntu, it's located under /boot
directory and their names are System.map-<kernel version>
. In addition, the runtime symbol information can be get by /proc/kallsyms
. So if the address of a symbol in /proc/kallsyms
is different from the address in System.map
, KASLR is enabled with the kernel which your system running on.
Here is an example.
$ sudo grep "T do_page_fault" /boot/System.map-$(uname -r)
ffffffff8106b780 T do_page_fault
$ sudo grep "T do_page_fault" /proc/kallsyms
ffffffffb626b780 T do_page_fault # It's different from the address in System.map
Usually System.map
can only be read by root and /proc/kalsyms
's address information is filled with zero, non-root users can't get the real address of kernel symbols.
KASLR is introduced in the Linux kernel v3.14. To enable KASLR, it's necessary to build Linux kernel with enabling CONFIG_RANDOMIZE_BASE
configuration option.
Although KASLR had been disabled by default in the upstream the Linux kernel, it's enabled by default from the Linux kernel v4.12.
If you want to disable KASLR explicitly, you should set nokaslr
kernel boot parameter.
There is a pitfall in the old Linux kernel, v4.7 or older. These kernels can't co-exist KASLR with hibernation feature. If CONFIG_HIBERNATION
is enabled, KASLR is disabled. On the other hand, if you boot this kernel with setting kaslr
parameter, KASLR is enabled and hibernation is disabled.
To make matters worse, KASLR doesn't print any message on the kernel log by default. Here is the corresponding code of the Linux kernel v4.4(arch/x86/boot/compressed/aslr.c).
...
unsigned char *choose_kernel_location(struct boot_params *boot_params, unsigned char *input, unsigned long input_size, unsigned char *output, unsigned long output_size) { unsigned long choice = (unsigned long)output; unsigned long random;
#ifdef CONFIG_HIBERNATION
if (!cmdline_find_option_bool("kaslr")) {
debug_putstr("KASLR disabled by default...\n");
goto out;
}
#else
if (cmdline_find_option_bool("nokaslr")) {
debug_putstr("KASLR disabled by cmdline...\n");
goto out;
}
#endif
...
You would feel "v4.7 or older is too old, probably I don't use it". However, the kernel version of Ubuntu 16.04 is v4.4 and this distribution is still used in many places. If you use so-called long-time-support distributions for a long time, the situation is the same. If you expect that KASLR is enabled, I recommend you to confirm your kernel version. In addition, if your kernel is enough old, please confirm whether KASLR is working by the kernel log with setting the kernel log level to show the above mentioned debug message.
Conclusion
You got the following information from this article.
- There are the features called ASLR and KASLR in Linux.
- These features are for mitigating some kind of attack.
- ASLR is for user program and KASLR is for the kernel.
- The old Linux kernels would disable KASLR silently
Posted on September 5, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.