From 7c4334e03a630cee9c28206c1369fafafccf3b4f Mon Sep 17 00:00:00 2001 From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:33:08 +0800 Subject: [PATCH] kernel: Add the ability to manually elevate privileges for programs using `prctl` by specifying UID or PID. --- kernel/Makefile | 1 + kernel/core_hook.c | 102 +++++++++++++++++++++++++++++++++++++++++++++ kernel/ksu.h | 2 + kernel/manual_su.c | 45 ++++++++++++++++++++ kernel/manual_su.h | 22 ++++++++++ 5 files changed, 172 insertions(+) create mode 100644 kernel/manual_su.c create mode 100644 kernel/manual_su.h diff --git a/kernel/Makefile b/kernel/Makefile index 10bc1e04..aa13642c 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -8,6 +8,7 @@ kernelsu-objs += ksud.o kernelsu-objs += embed_ksud.o kernelsu-objs += kernel_compat.o kernelsu-objs += throne_comm.o +kernelsu-objs += manual_su.o ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y) kernelsu-objs += ksu_trace.o diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 45b85144..4aee8595 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -52,6 +53,7 @@ #include "throne_comm.h" #include "kernel_compat.h" #include "dynamic_manager.h" +#include "manual_su.h" #ifdef CONFIG_KPM #include "kpm/kpm.h" @@ -283,6 +285,86 @@ void escape_to_root(void) setup_selinux(profile->selinux_domain); } +void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid) +{ + struct cred *newcreds; + struct task_struct *target_task; + + pr_info("cmd_su: escape_to_root_for_cmd_su called for UID: %d, PID: %d\n", target_uid, target_pid); + + // Find target task by PID + rcu_read_lock(); + target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID); + if (!target_task) { + rcu_read_unlock(); + pr_err("cmd_su: target task not found for PID: %d\n", target_pid); + return; + } + get_task_struct(target_task); + rcu_read_unlock(); + + if (task_uid(target_task).val == 0) { + pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid); + put_task_struct(target_task); + return; + } + + newcreds = prepare_kernel_cred(target_task); + if (newcreds == NULL) { + pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid); + put_task_struct(target_task); + return; + } + + struct root_profile *profile = ksu_get_root_profile(target_uid); + + newcreds->uid.val = profile->uid; + newcreds->suid.val = profile->uid; + newcreds->euid.val = profile->uid; + newcreds->fsuid.val = profile->uid; + + newcreds->gid.val = profile->gid; + newcreds->fsgid.val = profile->gid; + newcreds->sgid.val = profile->gid; + newcreds->egid.val = profile->gid; + newcreds->securebits = 0; + + u64 cap_for_cmd_su = profile->capabilities.effective | CAP_DAC_READ_SEARCH | CAP_SETUID | CAP_SETGID; + memcpy(&newcreds->cap_effective, &cap_for_cmd_su, sizeof(newcreds->cap_effective)); + memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted)); + memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset)); + + setup_groups(profile, newcreds); + task_lock(target_task); + + const struct cred *old_creds = get_task_cred(target_task); + + rcu_assign_pointer(target_task->real_cred, newcreds); + rcu_assign_pointer(target_task->cred, get_cred(newcreds)); + task_unlock(target_task); + + if (target_task->sighand) { + spin_lock_irq(&target_task->sighand->siglock); + disable_seccomp(target_task); + spin_unlock_irq(&target_task->sighand->siglock); + } + + setup_selinux(profile->selinux_domain); + put_cred(old_creds); + wake_up_process(target_task); + + if (target_task->signal->tty) { + struct inode *inode = target_task->signal->tty->driver_data; + if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) { + __ksu_handle_devpts(inode); + } + } + + put_task_struct(target_task); + + pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid); +} + int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry) { if (!current->mm) { @@ -363,6 +445,11 @@ static bool is_system_bin_su() return (current->mm->exe_file && !strcmp(current->mm->exe_file->f_path.dentry->d_name.name, "su")); } +int handle_cmd_su_escalation(uid_t uid, pid_t pid, const char __user *pwd) +{ + return ksu_manual_su_escalate(uid, pid, pwd); +} + static void init_uid_scanner(void) { ksu_uid_init(); @@ -398,6 +485,21 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, bool from_root = 0 == current_uid().val; bool from_manager = is_manager(); + if (arg2 == CMD_SU_ESCALATION_REQUEST) { + uid_t target_uid = (uid_t)arg3; + pid_t target_pid = (pid_t)arg4; + const char __user *user_password = (const char __user *)arg5; + + int ret = handle_cmd_su_escalation(target_uid, target_pid, user_password); + + if (ret == 0) { + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("cmd_su_escalation: prctl reply error\n"); + } + } + return 0; + } + if (!from_root && !from_manager && !(is_allow_su() && is_system_bin_su())) { // only root or manager can access this interface diff --git a/kernel/ksu.h b/kernel/ksu.h index aa92013a..ec6d4568 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -24,6 +24,8 @@ #define CMD_IS_SU_ENABLED 14 #define CMD_ENABLE_SU 15 +#define CMD_SU_ESCALATION_REQUEST 50 + #define CMD_GET_FULL_VERSION 0xC0FFEE1A #define CMD_ENABLE_KPM 100 diff --git a/kernel/manual_su.c b/kernel/manual_su.c new file mode 100644 index 00000000..f4ef2ae2 --- /dev/null +++ b/kernel/manual_su.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include +#include "kernel_compat.h" +#include "manual_su.h" +#include "ksu.h" +#include "allowlist.h" +#include "manager.h" + +static const char *ksu_su_password = "zakozako"; +extern void escape_to_root_for_cmd_su(uid_t, pid_t); + +int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid, + const char __user *user_password) +{ + if (ksu_is_current_verified()) + goto allowed; + + if (current_uid().val == 0 || is_manager() || ksu_is_allow_uid(current_uid().val)) + goto allowed; + + if (!user_password) { + pr_warn("manual_su: password required\n"); + return -EACCES; + } + char buf[64]; + if (strncpy_from_user(buf, user_password, sizeof(buf) - 1) < 0) + return -EFAULT; + buf[sizeof(buf) - 1] = '\0'; + + if (strcmp(buf, ksu_su_password) != 0) { + pr_warn("manual_su: wrong password\n"); + return -EACCES; + } + + ksu_mark_current_verified(); + +allowed: + escape_to_root_for_cmd_su(target_uid, target_pid); + return 0; +} \ No newline at end of file diff --git a/kernel/manual_su.h b/kernel/manual_su.h new file mode 100644 index 00000000..5a756fca --- /dev/null +++ b/kernel/manual_su.h @@ -0,0 +1,22 @@ +#ifndef __KSU_MANUAL_SU_H +#define __KSU_MANUAL_SU_H + +#include +#include + +#define KSU_SU_VERIFIED_BIT (1UL << 0) + +static inline bool ksu_is_current_verified(void) +{ + return ((unsigned long)(current->security) & KSU_SU_VERIFIED_BIT) != 0; +} + +static inline void ksu_mark_current_verified(void) +{ + current->security = (void *)((unsigned long)(current->security) | KSU_SU_VERIFIED_BIT); +} + +int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid, + const char __user *user_password); + +#endif \ No newline at end of file