diff options
author | Andreas Kling <kling@serenityos.org> | 2020-01-18 23:31:29 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-01-18 23:40:12 +0100 |
commit | 862b3ccb4e54113fc4139a3b4d2af3a8f40fe122 (patch) | |
tree | 4f2cdde028bf55dbacba3bd824ab77998cf95edb /Tests/Kernel | |
parent | 7ea264a660a184fadd75f74379320c0e4d1bda21 (diff) | |
download | serenity-862b3ccb4e54113fc4139a3b4d2af3a8f40fe122.zip |
Kernel: Enforce W^X between sys$mmap() and sys$execve()
It's now an error to sys$mmap() a file as writable if it's currently
mapped executable by anyone else.
It's also an error to sys$execve() a file that's currently mapped
writable by anyone else.
This fixes a race condition vulnerability where one program could make
modifications to an executable while another process was in the kernel,
in the middle of exec'ing the same executable.
Test: Kernel/elf-execve-mmap-race.cpp
Diffstat (limited to 'Tests/Kernel')
-rw-r--r-- | Tests/Kernel/elf-execve-mmap-race.cpp | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/Tests/Kernel/elf-execve-mmap-race.cpp b/Tests/Kernel/elf-execve-mmap-race.cpp new file mode 100644 index 0000000000..2928a30f1e --- /dev/null +++ b/Tests/Kernel/elf-execve-mmap-race.cpp @@ -0,0 +1,128 @@ +#include <LibELF/exec_elf.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +volatile bool hax = false; + +int main() +{ + char buffer[16384]; + + auto& header = *(Elf32_Ehdr*)buffer; + header.e_ident[EI_MAG0] = ELFMAG0; + header.e_ident[EI_MAG1] = ELFMAG1; + header.e_ident[EI_MAG2] = ELFMAG2; + header.e_ident[EI_MAG3] = ELFMAG3; + header.e_ident[EI_CLASS] = ELFCLASS32; + header.e_ident[EI_DATA] = ELFDATA2LSB; + header.e_ident[EI_VERSION] = EV_CURRENT; + header.e_ident[EI_OSABI] = ELFOSABI_SYSV; + header.e_ident[EI_ABIVERSION] = 0; + header.e_type = ET_EXEC; + header.e_version = EV_CURRENT; + header.e_ehsize = sizeof(Elf32_Ehdr); + header.e_machine = EM_386; + header.e_shentsize = sizeof(Elf32_Shdr); + + header.e_phnum = 1; + header.e_phoff = 52; + header.e_phentsize = sizeof(Elf32_Phdr); + + auto* ph = (Elf32_Phdr*)(&buffer[header.e_phoff]); + ph[0].p_vaddr = 0x20000000; + ph[0].p_type = PT_LOAD; + ph[0].p_filesz = sizeof(buffer); + ph[0].p_memsz = sizeof(buffer); + ph[0].p_flags = PF_R | PF_W; + ph[0].p_align = PAGE_SIZE; + + header.e_shnum = 3; + header.e_shoff = 1024; + + u32 secret_address = 0x00184658; + + auto* sh = (Elf32_Shdr*)(&buffer[header.e_shoff]); + sh[0].sh_type = SHT_SYMTAB; + sh[0].sh_offset = 2048; + sh[0].sh_entsize = sizeof(Elf32_Sym); + sh[0].sh_size = 1 * sizeof(Elf32_Sym); + + sh[1].sh_type = SHT_STRTAB; + sh[1].sh_offset = secret_address - 0x01001000; + sh[1].sh_entsize = 0; + sh[1].sh_size = 1024; + + sh[2].sh_type = SHT_STRTAB; + sh[2].sh_offset = 4096; + sh[2].sh_entsize = 0; + sh[2].sh_size = 1024; + header.e_shstrndx = 2; + + auto* sym = (Elf32_Sym*)(&buffer[2048]); + sym[0].st_value = 0; + sym[0].st_name = 0; + + header.e_entry = 0; + + int fd = open("x", O_RDWR | O_CREAT, 0777); + if (fd < 0) { + perror("open"); + return 1; + } + + int nwritten = write(fd, buffer, sizeof(buffer)); + if (nwritten < 0) { + perror("write"); + return 1; + } + + sync(); + + auto* mapped = (u8*)mmap(nullptr, sizeof(buffer), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0); + if (mapped == MAP_FAILED) { + perror("mmap"); + return 1; + } + + auto* writable_program_headers = (Elf32_Phdr*)(&mapped[header.e_phoff]); + + pthread_attr_t attrs; + pthread_attr_init(&attrs); + sched_param high_prio { 99 }; + pthread_attr_setschedparam(&attrs, &high_prio); + + pthread_t t; + pthread_create( + &t, &attrs, [](void* ctx) -> void* { + auto& ph = *(volatile Elf32_Phdr*)ctx; + for (;;) { + if (!hax) + ph.p_offset = 0x60000000; + else + ph.p_offset = 0; + hax = !hax; + usleep(1); + } + return nullptr; + }, + &writable_program_headers[0]); + + for (;;) { + + if (!fork()) { + try_again: + printf("exec\n"); + if (execl("/home/anon/x", "x", nullptr) < 0) { + } + goto try_again; + } + + printf("waitpid\n"); + waitpid(-1, nullptr, 0); + } + return 0; +} |