苦学kernel(二)——kernel如何pwn

kernel pwn介绍

kernel pwn其实就是自己写一个程序在kernel的用户态下跑,这时候我们是完全可以自己写个system("/bin/sh")来getshell的,这一点与用户态的pwn有所不同,我们虽然可以轻松拿到shell,但是我们并没有读取flag文件的权限,所以这时候我们要把自己的权限提升,所以kernel pwn的主要目的就是提权,从而通过自己起的shell读取flag

提权

内核提权是指普通用户可以获取到root用户的权限,访问原先受限的资源(即flag),提权的方法大致分为两类
改变自身:通过改变自身进程的权限,使得自己获得root权限
改变别人:通过影响高权限进程的执行,使其完成我们想要的功能

改变自己

内核会通过进程的task_struct结构体中的cred指针来索引cred结构体,然后根据cred的内容来判断一个进程拥有的权限,如果cred结构体成员中的uid-fsgid都为0,那么一般就会认为进程具有root权限

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 */
  ...
}

这时候我们就有两种思路来提权
1、直接修改cred的uid-fsgid
2、伪造一个满足要求且合法的cred结构体,然后将task_struct结构体的cred指针指向我们伪造的cred结构体

直接修改cred

寻找cred的地址

直接定位

cred结构体的最前面记录的各种id信息,对于一个普通的进程而言,uid-fsgid都是执行进程的用户的身份,因此我们可以通过扫描内存来定位cred
在实际的执行过程中,我们可能会发现很多满足要求的cred,这主要是因为cred结构体可能会被拷贝、释放。

间接定位
struct

进程的task_struct结构体中会有指向cred的指针
1、定位当前进程task_struct结构体的地址
2、根据cred指针相对于task_struct结构体的偏移计算出cred指针储存的地址
3、获取cred具体的地址

coom

coom是用来标记可执行文件的名字,位于进程的task_struct结构体中,我们可以发现comm其实在cred的正下方,所以我们也可以先定位comm,然后定位cred的地址

    /* Process credentials: */

    /* Tracer's credentials at attach: */
    const struct cred __rcu     *ptracer_cred;

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

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

#ifdef CONFIG_KEYS
    /* Cached requested key. */
    struct key          *cached_requested_key;
#endif

    /*
     * executable name, excluding path.
     *
     * - normally initialized setup_new_exec()
     * - access it with [gs]et_task_comm()
     * - lock it with task_lock()
     */
    char                comm[TASK_COMM_LEN];

然而,在进程名字并不特殊的情况下,内核中可能会有多个同样的字符串,这会影响搜索的正确性和效率,所以,我们可以使用pprctl设置进程的comm为一个特殊的字符串,然后再开始定位comm

修改

直接修改

在这种方法下我们需要知道cred的地址,然后使用任意地址写或者rop执行代码来修改cred

间接修改

在这种方法下我们并不需要知道cred的地址
我们知道cred本质就是一个堆,那么创建cred的时候必然是需要进行堆分配的,那么我们就可以使用uaf来修改cred了
1、申请一个与cred结构体大小一样的堆块
2、释放该堆块
3、fork出新进程,恰好使用刚刚释放的堆块
4、通过修改cred从而提权

修改cred指针

定位

很明显其实就是上面所讲述的间接定位的两种方法

修改

直接修改

1、修改cred指针为内核镜像中已有的init_cred的地址,这种方法适合我们能够直接修改cred指针以及知道init_cred地址的情况。
2、伪造一个cred,然后修改cred指针指向该地址,这种方法比较麻烦,一般不使用。

间接修改

我们可以使用commit_creds(prepare_kernel_cred(0))进行提权,该函数会自动生成一个合法的cred,并且定位当前线程的task_struct的位置,然后修改它的cred为新的cred,这种方法比较适合用于控制程序执行流后使用。

posted @ 2023-04-25 11:25  予柒  阅读(505)  评论(0)    收藏  举报
返回顶端
Live2D /*修改地一:waifu.css*/
/*修改地二:waifu.css*/
/*修改地三:live2d.js*/ /*修改地四:waifu-tips.js*/