DEV Community

Satoru Takeuchi
Satoru Takeuchi

Posted on

A brief description of ASLR and KASLR

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

Discussion (0)