title: Linux内核态赋予进程root权限
id: 6d75b26b-8a99-4d19-9d12-510427feca12
date: 2024-09-20 20:46:06
auther: liccsu
cover: null
excerpt: title Linux内核态赋予进程root权限 date 2023/07/10 tags Linux kernel root categories 笔记 一、Linux中进程的用户管理 在Linux中,每个进程都拥有真实用户ID (==uid==)和真实组ID (==gid==),有效用户
permalink: /archives/ie7FYgJ7
categories:

  • bi-ji
    tags:
  • linux


title: Linux内核态赋予进程root权限
date: 2023/07/10
tags:

  • Linux
  • kernel
  • root
    categories:
  • 笔记

一、Linux中进程的用户管理

在Linux中,每个进程都拥有真实用户ID (==uid==)和真实组ID (==gid==),有效用户ID (==euid==)和有效组ID (==egid==),保存的设置用户ID (==suid==)和保存的设置组ID (==sgid==),另外地,还有文件系统用户ID (==fsuid==)和文件系统组ID (==fsgid==)。

(1) 真实用户、组 (uid/gid):指的是进程的实际所有者和所属组,一般就是执行此进程的用户。

(2) 有效用户、组 (euid/egid):用来判断进程执行各种操作的权限。

(3) 保存的设置用户、组 (suid/sgid):是有效用户、组的副本,主要用来还原有效用户、组的值。

(4) 文件系统用户、组 (fsuid/fsgid):仅用来进行文件权限检查。

二、如何获取进程号?

(1) 通过pidof命令获取:

image-20230823033635993.png

(2) 通过pgrep命令获取:

image-20230823033705071.png

(3) 可以通过遍历/proc来获取:

/proc下存在若干以数字命名的目录,这些数字就是PID,目录下有关于该进程的一些信息,其中/proc/$pid/cmdline文件里的内容便是进程名。

pid_t get_pid_t(const char *cmd) {
    DIR *dir;
    FILE *file;
    struct dirent *ent;
    char filename[256], cmdline[256];
    pid_t pid = -1;
    dir = opendir("/proc");

    if (dir != NULL) {
        while ((ent = readdir(dir))) {
            if (ent->d_type == DT_DIR && STR_IS_INT(ent->d_name)) {
                snprintf(filename, sizeof(filename), "/proc/%s/cmdline", ent->d_name);
                file = fopen(filename, "r");
                if (file) {
                    fgets(cmdline, sizeof(cmdline), file);
                    fclose(file);
                    if (strcmp(cmd, cmdline) == 0) {
                        pid = atoi(ent->d_name);
                        break;
                    }
                }
            }
        }
        closedir(dir);
    }

    return pid;
}

image-20230823033747157.png

三、通过进程号获取struct pid

先看一下struct pid的定义:

struct pid
{
    atomic_t count; // pid的引用计数
    unsigned int level; // pid的层级
    /* lists of tasks that use this pid */
    struct hlist_head tasks[PIDTYPE_MAX];
    /* wait queue for pidfd notifications */
    wait_queue_head_t wait_pidfd;
    struct rcu_head rcu; // RCU头, 用于RCU同步机制
    struct upid numbers[1]; // 用于储存pid号的结构体数组
};

其中struct upid定义如下:

struct upid
{
    /* Try to keep pid_chain in the same cacheline as nr for find_vpid */
    int nr;                        /* the pid value */
    struct pid_namespace *ns;      /* namespace */
    struct hlist_node pid_chain;   /* hash chain */
};

然后在$(Linux-4.19)/kernel/pid.c中可以发现这样一个函数:

struct pid *find_get_pid(pid_t nr)
{
    struct pid *pid;

    rcu_read_lock();
    pid = get_pid(find_vpid(nr));
    rcu_read_unlock();

    return pid;
}

其中参数nr便是上面获取到的进程号

其中find_vpidget_pid定义如下:

struct pid *find_vpid(int nr)
{
    return find_pid_ns(nr, task_active_pid_ns(current));
}

static inline struct pid *get_pid(struct pid *pid)
{
    if (pid)
        atomic_inc(&pid->count);
    return pid;
}

其中atomic_inc(&pid->count)表示对pid->count进行原子级加一操作,所谓“原子”,就是最小的不可分解的最小执行单位,使用锁定总线的单指令进行不可分解的原子操作,以避免操作被中断或其他事件打断。

四、通过struct pid获取struct task_struct

struct task_struct *get_pid_task(struct pid *pid, enum pid_type type)
{
    struct task_struct *result;
    rcu_read_lock();
    result = pid_task(pid, type);
    if (result)
        get_task_struct(result);
    rcu_read_unlock();
    return result;
}

其中struct task_struct定义在$(Linux-4.19)/include/Linux/sched.h,太长了就只贴出本篇要用到的两个成员变量:

struct task_struct
{
    ...
    /* Objective and real subjective task credentials (COW): */
    const struct cred __rcu     *real_cred;

    /* Effective (overridable) subjective task credentials (COW): */
    const struct cred __rcu     *cred;
    ...
}

看注释可以知道,其中real_cred指向客体和真实主体证书,cred指向有效主体证书,其结构体定义如下:

struct cred {
    atomic_t    usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
    atomic_t    subscribers;    /* number of processes subscribed */
    void        *put_addr;
    unsigned    magic;
#define CRED_MAGIC  0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endif
    kuid_t      uid;        /* real UID of the task */
    kgid_t      gid;        /* real GID of the task */
    kuid_t      suid;       /* saved UID of the task */
    kgid_t      sgid;       /* saved GID of the task */
    kuid_t      euid;       /* effective UID of the task */
    kgid_t      egid;       /* effective GID of the task */
    kuid_t      fsuid;      /* UID for VFS ops */
    kgid_t      fsgid;      /* GID for VFS ops */
    unsigned    securebits; /* SUID-less security management */
    kernel_cap_t    cap_inheritable; /* caps our children can inherit */
    kernel_cap_t    cap_permitted;  /* caps we're permitted */
    kernel_cap_t    cap_effective;  /* caps we can actually use */
    kernel_cap_t    cap_bset;   /* capability bounding set */
    kernel_cap_t    cap_ambient;    /* Ambient capability set */
#ifdef CONFIG_KEYS
    unsigned char   jit_keyring;    /* default keyring to attach requested
                     * keys to */
    struct key __rcu *session_keyring; /* keyring inherited over fork */
    struct key  *process_keyring; /* keyring private to this process */
    struct key  *thread_keyring; /* keyring private to this thread */
    struct key  *request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITY
    void        *security;  /* subjective LSM security */
#endif
    struct user_struct *user;   /* real user ID subscription */
    struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
    struct group_info *group_info;  /* supplementary groups for euid/fsgid */
    /* RCU deletion */
    union {
        int non_rcu;            /* Can we skip RCU deletion? */
        struct rcu_head rcu;        /* RCU deletion hook */
    };
};

五、更改权限

将得到的struct cred中的以下几个成员变量赋值0

    kuid_t      uid;        /* real UID of the task */
    kgid_t      gid;        /* real GID of the task */
    kuid_t      suid;       /* saved UID of the task */
    kgid_t      sgid;       /* saved GID of the task */
    kuid_t      euid;       /* effective UID of the task */
    kgid_t      egid;       /* effective GID of the task */
    kuid_t      fsuid;      /* UID for VFS ops */
    kgid_t      fsgid;      /* GID for VFS ops */

至此,就已经将一个进程提权至root了。

文章作者: Billy
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 billy blog
技能 博客
喜欢就支持一下吧
打赏
微信 微信