// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2015-2018 Jason A. Donenfeld . All Rights Reserved. */ /* Hello. If this is enabled in your kernel for some reason, whoever is * distributing your kernel to you is a complete moron, and you shouldn't * use their kernel anymore. But it's not my fault! People: don't enable * this driver! (Note that the existence of this file does not imply the * driver is actually in use. Look in your .config to see whether this is * enabled.) -Jason */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include static bool is_su(const char __user *filename) { static const char su_path[] = "/system/bin/su"; char ufn[sizeof(su_path)]; return likely(!copy_from_user(ufn, filename, sizeof(ufn))) && unlikely(!memcmp(ufn, su_path, sizeof(ufn))); } static void __user *userspace_stack_buffer(const void *d, size_t len) { /* To avoid having to mmap a page in userspace, just write below the stack pointer. */ char __user *p = (void __user *)current_user_stack_pointer() - len; return copy_to_user(p, d, len) ? NULL : p; } static char __user *sh_user_path(void) { static const char sh_path[] = "/system/bin/sh"; return userspace_stack_buffer(sh_path, sizeof(sh_path)); } static long(*old_newfstatat)(int dfd, const char __user *filename, struct stat *statbuf, int flag); static long new_newfstatat(int dfd, const char __user *filename, struct stat __user *statbuf, int flag) { if (!is_su(filename)) return old_newfstatat(dfd, filename, statbuf, flag); return old_newfstatat(dfd, sh_user_path(), statbuf, flag); } static long(*old_faccessat)(int dfd, const char __user *filename, int mode); static long new_faccessat(int dfd, const char __user *filename, int mode) { if (!is_su(filename)) return old_faccessat(dfd, filename, mode); return old_faccessat(dfd, sh_user_path(), mode); } extern int selinux_enforcing; static long (*old_execve)(const char __user *filename, const char __user *const __user *argv, const char __user *const __user *envp); static long new_execve(const char __user *filename, const char __user *const __user *argv, const char __user *const __user *envp) { static const char now_root[] = "You are now root.\n"; struct cred *cred; if (!is_su(filename)) return old_execve(filename, argv, envp); if (!old_execve(filename, argv, envp)) return 0; /* It might be enough to just change the security ctx of the * current task, but that requires slightly more thought than * just axing the whole thing here. */ selinux_enforcing = 0; /* Rather than the usual commit_creds(prepare_kernel_cred(NULL)) idiom, * we manually zero out the fields in our existing one, so that we * don't have to futz with the task's key ring for disk access. */ cred = (struct cred *)__task_cred(current); memset(&cred->uid, 0, sizeof(cred->uid)); memset(&cred->gid, 0, sizeof(cred->gid)); memset(&cred->suid, 0, sizeof(cred->suid)); memset(&cred->euid, 0, sizeof(cred->euid)); memset(&cred->egid, 0, sizeof(cred->egid)); memset(&cred->fsuid, 0, sizeof(cred->fsuid)); memset(&cred->fsgid, 0, sizeof(cred->fsgid)); memset(&cred->cap_inheritable, 0xff, sizeof(cred->cap_inheritable)); memset(&cred->cap_permitted, 0xff, sizeof(cred->cap_permitted)); memset(&cred->cap_effective, 0xff, sizeof(cred->cap_effective)); memset(&cred->cap_bset, 0xff, sizeof(cred->cap_bset)); memset(&cred->cap_ambient, 0xff, sizeof(cred->cap_ambient)); sys_write(2, userspace_stack_buffer(now_root, sizeof(now_root)), sizeof(now_root) - 1); return old_execve(sh_user_path(), argv, envp); } extern const unsigned long sys_call_table[]; static void read_syscall(void **ptr, unsigned int syscall) { *ptr = READ_ONCE(*((void **)sys_call_table + syscall)); } static void replace_syscall(unsigned int syscall, void *ptr) { WRITE_ONCE(*((void **)sys_call_table + syscall), ptr); } #define read_and_replace_syscall(name) do { \ read_syscall((void **)&old_ ## name, __NR_ ## name); \ replace_syscall(__NR_ ## name, &new_ ## name); \ } while (0) static int superuser_init(void) { pr_err("WARNING WARNING WARNING WARNING WARNING\n"); pr_err("This kernel has kernel-assisted superuser and contains a\n"); pr_err("trivial way to get root. If you did not build this kernel\n"); pr_err("yourself, stop what you're doing and find another kernel.\n"); pr_err("This one is not safe to use.\n"); pr_err("WARNING WARNING WARNING WARNING WARNING\n"); read_and_replace_syscall(newfstatat); read_and_replace_syscall(faccessat); read_and_replace_syscall(execve); return 0; } module_init(superuser_init); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Kernel-assisted superuser for Android"); MODULE_AUTHOR("Jason A. Donenfeld ");