aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2018-11-23 03:21:34 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2018-11-23 18:27:19 +0100
commit48bc55f87e4926dc3d08366fb16d024ad61bb942 (patch)
tree6520bbbed978e3198bbe6bb2f97e485cc45edc09
parentHijack `su` instead. (diff)
downloadkernel-assisted-superuser-48bc55f87e4926dc3d08366fb16d024ad61bb942.tar.xz
kernel-assisted-superuser-48bc55f87e4926dc3d08366fb16d024ad61bb942.zip
Write below stack pointer instead of mmapping
-rw-r--r--superuser.c133
1 files changed, 49 insertions, 84 deletions
diff --git a/superuser.c b/superuser.c
index 9152923..895c5e6 100644
--- a/superuser.c
+++ b/superuser.c
@@ -17,6 +17,7 @@
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/mman.h>
+#include <linux/ptrace.h>
static bool is_su(const char __user *filename)
{
@@ -26,110 +27,74 @@ static bool is_su(const char __user *filename)
return likely(!copy_from_user(ufn, filename, sizeof(ufn))) && unlikely(!memcmp(ufn, su_path, sizeof(ufn)));
}
-static int new_sh_user_path(char __user **filename)
+static char __user *sh_user_path(void)
{
static const char sh_path[] = "/system/bin/sh";
- unsigned long addr;
-
- addr = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0);
- if (IS_ERR_VALUE(addr))
- return (int)addr;
- if (copy_to_user((void __user *)addr, sh_path, sizeof(sh_path)))
- return -EFAULT;
- *filename = (char __user *)addr;
- return 0;
-}
+ /* To avoid having to mmap a page in userspace, just write below the stack pointer. */
+ char __user *p = (void __user *)current_user_stack_pointer() - sizeof(sh_path);
+
+ return copy_to_user(p, sh_path, sizeof(sh_path)) ? NULL : p;
-static void free_sh_user_path(char __user *filename)
-{
- struct mm_struct *mm = current->mm;
- down_write(&mm->mmap_sem);
- do_munmap(mm, (unsigned long)filename, PAGE_SIZE);
- up_write(&mm->mmap_sem);
}
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)) {
- char __user *new_filename;
- int ret = new_sh_user_path(&new_filename);
-
- if (!ret) {
- ret = old_newfstatat(dfd, new_filename, statbuf, flag);
- free_sh_user_path(new_filename);
- return ret;
- }
-
- }
- return old_newfstatat(dfd, filename, statbuf, 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)) {
- char __user *new_filename;
- int ret = new_sh_user_path(&new_filename);
-
- if (!ret) {
- ret = old_faccessat(dfd, new_filename, mode);
- free_sh_user_path(new_filename);
- return ret;
- }
- }
- return old_faccessat(dfd, filename, 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)
{
- if (is_su(filename)) {
- char __user *new_filename;
- int ret = new_sh_user_path(&new_filename);
-
- if (!ret) {
- static const char now_root[] = "You are now root.\n";
- struct file *stderr;
- struct cred *cred;
-
- /* 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));
-
- stderr = fget(2);
- if (stderr) {
- kernel_write(stderr, now_root, sizeof(now_root) - 1, 0);
- fput(stderr);
- }
-
- ret = old_execve(new_filename, argv, envp);
- free_sh_user_path(new_filename);
- return ret;
- }
+ static const char now_root[] = "You are now root.\n";
+ struct file *stderr;
+ struct cred *cred;
+
+ if (!is_su(filename))
+ return old_execve(filename, argv, envp);
+
+ /* 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));
+
+ stderr = fget(2);
+ if (stderr) {
+ kernel_write(stderr, now_root, sizeof(now_root) - 1, 0);
+ fput(stderr);
}
- return old_execve(filename, argv, envp);
+
+ return old_execve(sh_user_path(), argv, envp);
}
extern const unsigned long sys_call_table[];