首页
社区
课程
招聘
[原创]Linux内核modprobe_path覆盖利用技术
发表于: 3天前 979

[原创]Linux内核modprobe_path覆盖利用技术

3天前
979

今年的 ccb&ciscn 初赛有一道 Linux 内核的 Pwn 题,让我这个内核菜鸡被直接“硬控”了。现在初赛都上内核了。。。

赛后搞到了 WP,于是正好趁着元旦假期有时间好好复现一下,分析了一下这道题的利用技术的原理,于是就有了这篇文章。

modprobe_path覆盖利用技术是一种 Linux 内核漏洞利用方法,它可以让攻击者以 root 权限运行任意 shell 脚本。

modprobe_path覆盖利用技术的关键是覆盖掉modprobe_path内核全局变量,它的默认值为/sbin/modprobe程序的路径,modprobe是一个用于向 Linux 内核添加可加载内核模块,或从内核中移除可加载内核模块的工具。本质上它是一个用户态程序,当我们在 Linux 内核中安装或卸载新模块时会被执行。

我们可以通过运行以下命令检查:

modprobe_path变量中的程序,会在我们执行一个系统无法识别文件类型的非法格式文件(无效魔数)时被调用。简单说一般我们执行 shell 文件时文件签名是#!/bin/bash,这是合法的文件格式,系统可以识别。而我们执行一个文件签名为\xff\xff\xff\xff的文件时内核会进行一系列操作,最终触发并以 root 权限执行modprobe_path变量中的程序。

因此如果我们将modprobe_path变量覆盖修改为想要执行的程序,然后再执行一个非法格式文件就可以以 root 权限执行我们想要执行的目标程序。

但是如果内核开启了CONFIG_STATIC_USERMODEHELPER内核配置选项,那么会将内核执行用户态 helper 的路径从运行时可变的全局变量改为编译期固定的常量,这样我们就无法对modprobe_path进行覆盖修改。

总结一下modprobe_path覆盖利用技术需要满足以下条件:

基本上,在大多数的 kernel pwn 题目中,条件 2 和条件 3 都是直接满足的,因此最关键的问题在于是否具备任意地址写的能力以及内核是否开启了CONFIG_STATIC_USERMODEHELPER选项。

询问了一下 GPT 给了一个检查内核是否开启CONFIG_STATIC_USERMODEHELPER选项的方法:

如果修改成功则CONFIG_STATIC_USERMODEHELPER选项未开启,不成功则开启。

接下来我们通过分析 Linux 内核的源代码,详细了解覆盖modprobe_path利用技术的原理。

当我们在 Linux Shell 中执行命令时(例如bash中执行ls),本质上会由 Shell 程序内部调用execve系统调用来加载并执行对应的程序。

execve系统调用会设置可执行文件路径、命令行参数数组以及环境变量数组等信息,然后调用do_execve,它才是真正负责执行程序加载的核心函数。

do_execve函数会对传入的可执行文件路径、参数数组和环境变量数组进行封装与处理,然后将执行流程统一转交给 do_execveat_common,由后者完成后续的程序加载与执行逻辑。

do_execveat_common负责构建并填充 linux_binprm,完成命令行参数和环境变量的准备工作。

linux_binprm是 Linux 内核中 描述一个即将被execve执行的可执行文件及其执行上下文的核心数据结构,是execve执行流程中的“载体”,保存了程序加载所需的全部信息。

do_execveat_common最终会调用bprm_execve,进入真正的二进制加载流程。bprm_execveexecve执行路径中最关键的通用入口函数。

bprm_execve函数会先对被加载程序的信息准备凭证与安全检查,最后调用exec_binprm函数加载可执行文件。

exec_binprm函数负责在执行过程中选择并执行合适的二进制格式处理器,通过循环解析解释器链,最终完成可执行文件的确定并触发执行成功事件通知。

其中最重要的逻辑就是解析选择合适的二进制格式处理器部分,因此我们接下来需要跟进search_binary_handler函数分析。

search_binary_handler函数会在formats链表中寻找合适的文件加载器。formats是一个包含所有已注册的可执行格式文件解析器。

常见的解析器:

每种解析器包含一个关键字段load_binary,其中包含着对应格式的加载函数。

检查文件魔数,匹配解析器:

例如 ELF 格式:

如果成功匹配到解析器,内核将调用该格式对应格式的加载函数完成可执行文件的加载(如 ELF 文件或 shell 文件)。

如果没有匹配到解析器,且文件的前 4 字节不可打印(非 ASCII),内核会进入一个兜底处理流程。在在该流程中,内核会根据可执行文件头部的魔数值,动态请求加载对应的 binfmt 内核模块。具体做法是从文件头的第 3、4 字节读取一个 16 位数值,并将其格式化为模块名 binfmt-xxxx,随后调用request_module触发模块加载:

request_module其实是一个宏,真正执行的是__request_module函数:

__request_module函数最终会调用call_modprobe函数。

call_modprobe是内核模块自动加载路径中真正执行用户态程序的函数:

这部分代码的关键点:

通过call_usermodehelper_setup封装好execve需要的所有信息,然后调用call_usermodehelper_exec函数以 root 权限执行modprobe_path的值。

默认的modprobe_path值是:

CONFIG_MODPROBE_PATH来自内核配置,默认为/sbin/modprobe

所以如果我们修改了modprobe_path的值,并执行一个非法格式文件后就会执行modprobe_path的值。

前面已经说过启用CONFIG_STATIC_USERMODEHELPER选项就会导致modprobe_path覆盖利用技术失效,这里结合代码分析一下原因。

call_usermodehelper_setup中,如果CONFIG_STATIC_USERMODEHELPER配置选项启用,那么就会执行以下代码:

此时内核会强制使用静态、编译时写死的路径,而忽略modprobe_path,从而使得modprobe_path覆盖技术失效。

下载题目:8aaK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3&6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Q4x3V1j5I4j5Y4V1J5f1V1q4Q4x3X3c8U0f1U0c8%4y4W2c8m8x3g2y4a6j5h3V1J5N6s2y4m8i4K6y4r3M7s2N6V1i4K6y4p5j5%4j5#2k6l9`.`.

题目提供了三个文件:

首先分析启动脚本:

保护机制:

只读数据保护关闭会导致.rodata段可写,这样的话即使modprobe_path是常量,也能被覆盖。这样就直接绕过了CONFIG_STATIC_USERMODEHELPER的防护。

然后解压rootfs.cpio分析文件系统

init启动脚本中发现内核加载了babydev.ko内核模块,并在后台运行eatFlag程序。

ida 逆向分析eatFlag程序发现它会在程序启动时读取/flag的内容并存放在堆内存中,然后删除该文件。所以我们只能从eatflag程序的堆内存中读取到 flag。

图片描述

flag指针是固定地址,很明显程序是没有开启 PIE 的,所以可以从flag指针读取 flag 的值。

图片描述

接着 ida 逆向分析babydev.ko模块,查看函数表的函数,其中__pfx_开头的是内核在符号层面生成的前缀符号。

图片描述

接下来首先分析init_module函数,它是内核模块加载入口。

init_module函数初始化了一个名为noc的字符设备,创建了/dev/noc供用户态交互,并分配了一个约 64KB 的全局内核堆缓冲区 global_buf

图片描述

发现内核地址泄露漏洞:

图片描述

由于dest变量在站的布局之后就是v11变量。所以copy_to_user复制 40 字节的数据到用户态会将v11的值也给复制到用户态。

我们可以通过访问这个ioctl接口获取到泄露的数据。

图片描述

dev_open在设备首次打开时分配一块内核堆内存,记录当前进程的 PID 和进程名,并将该堆指针保存到file->private_data,同时通过全局标志限制设备只能被单次打开。

图片描述

函数中的v4变量是当前文件偏移,v6变量是实际写入长度。

v4 + a3 > 0x10000时,驱动试图通过v6 = -*a4截断写入长度,但由于v6为无符号整型,该赋值触发了 signed 到 unsigned 的隐式转换,导致 v6 变成一个极大的正数。

随后该长度被直接用于copy_from_user,且写入地址由 global_buf + data_start + f_pos计算,在缺乏完整边界检查的情况下,最终形成可控偏移、可控长度的内核堆越界写漏洞。

图片描述

dev_read根据file->f_posglobal_buf的有效数据区向用户态拷贝数据,并在读完后推进文件偏移,逻辑等价于一个简单的内存文件读取实现。

图片描述

该函数实现了设备的lseek操作(偏移定位),根据a3参数的值选择模式。如果a3为 1 则新位置为当前文件指针加上a2偏移参数。如果a3为 2 则新位置为文件末尾加上偏移。如果a3参数为 3,则新位置等于偏移。

通过这个函数我们可以控制文件偏移配合dev_write函数进行越界写利用。

图片描述

在关闭设备文件时原子递减打开计数并释放filp->private_data

图片描述

cleanup_module在模块卸载时释放init_module中申请的内核资源。

图片描述

modprobe_path覆写:

exp放入文件系统并重打包。

然后修改启动脚本加载的文件系统为exp.cpio

执行exp然后成功拿到 flag:

图片描述

最后附上一个打远程的脚本:

由于这个补丁被合并到上游内核,导致覆盖modprobe_path这个利用方法在新版本内核中作废了。

Linux 内核利用技术:覆盖modprobe_path - Midas 的博客
复兴modprobe_path技术:克服search_binary_handler()补丁 - Theori BLOG

❯ cat /proc/sys/kernel/modprobe
/sbin/modprobe
❯ cat /proc/sys/kernel/modprobe
/sbin/modprobe
#修改modprobe
echo /tmp/test > /proc/sys/kernel/modprobe
#检查是否修改成功
cat /proc/sys/kernel/modprobe
#修改modprobe
echo /tmp/test > /proc/sys/kernel/modprobe
#检查是否修改成功
cat /proc/sys/kernel/modprobe
SYSCALL_DEFINE3(execve,
    const char __user *, filename,            // 参数1:可执行文件路径
    const char __user *const __user *, argv,  // 参数2:命令行参数数组
    const char __user *const __user *, envp)  // 参数3:环境变量数组
{
    return do_execve(getname(filename), argv, envp); //getname从用户态读取filename
}
SYSCALL_DEFINE3(execve,
    const char __user *, filename,            // 参数1:可执行文件路径
    const char __user *const __user *, argv,  // 参数2:命令行参数数组
    const char __user *const __user *, envp)  // 参数3:环境变量数组
{
    return do_execve(getname(filename), argv, envp); //getname从用户态读取filename
}
static int do_execve(struct filename *filename,
    const char __user *const __user *__argv,
    const char __user *const __user *__envp)
{
    struct user_arg_ptr argv = { .ptr.native = __argv };
    struct user_arg_ptr envp = { .ptr.native = __envp };
    // AT_FDCWD为当前工作目录
    return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
static int do_execve(struct filename *filename,
    const char __user *const __user *__argv,
    const char __user *const __user *__envp)
{
    struct user_arg_ptr argv = { .ptr.native = __argv };
    struct user_arg_ptr envp = { .ptr.native = __envp };
    // AT_FDCWD为当前工作目录
    return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
static int do_execveat_common(int fd, struct filename *filename,
                  struct user_arg_ptr argv,
                  struct user_arg_ptr envp,
                  int flags)
{
    struct linux_binprm *bprm; //描述
    int retval;
 
    if (IS_ERR(filename))
        return PTR_ERR(filename);
 
    if ((current->flags & PF_NPROC_EXCEEDED) &&  //防止进程数超限仍不断 exec
        is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) {
        retval = -EAGAIN;
        goto out_ret;
    }
 
    current->flags &= ~PF_NPROC_EXCEEDED;
 
    bprm = alloc_bprm(fd, filename, flags);
    if (IS_ERR(bprm)) {
        retval = PTR_ERR(bprm);
        goto out_ret;
    }
 
    retval = count(argv, MAX_ARG_STRINGS);
    if (retval == 0)
        pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
                 current->comm, bprm->filename);
    if (retval < 0)
        goto out_free;
    bprm->argc = retval;
 
    retval = count(envp, MAX_ARG_STRINGS);
    if (retval < 0)
        goto out_free;
    bprm->envc = retval;
 
    retval = bprm_stack_limits(bprm);
    if (retval < 0)
        goto out_free;
 
    retval = copy_string_kernel(bprm->filename, bprm);
    if (retval < 0)
        goto out_free;
    bprm->exec = bprm->p;
 
    retval = copy_strings(bprm->envc, envp, bprm);
    if (retval < 0)
        goto out_free;
 
    retval = copy_strings(bprm->argc, argv, bprm);
    if (retval < 0)
        goto out_free;
 
    if (bprm->argc == 0) {
        retval = copy_string_kernel("", bprm);
        if (retval < 0)
            goto out_free;
        bprm->argc = 1;
    }
 
    retval = bprm_execve(bprm); //进入真正的执行阶段
out_free:
    free_bprm(bprm);
 
out_ret:
    putname(filename);
    return retval;
}
static int do_execveat_common(int fd, struct filename *filename,
                  struct user_arg_ptr argv,
                  struct user_arg_ptr envp,
                  int flags)
{
    struct linux_binprm *bprm; //描述
    int retval;
 
    if (IS_ERR(filename))
        return PTR_ERR(filename);
 
    if ((current->flags & PF_NPROC_EXCEEDED) &&  //防止进程数超限仍不断 exec
        is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) {
        retval = -EAGAIN;
        goto out_ret;
    }
 
    current->flags &= ~PF_NPROC_EXCEEDED;
 
    bprm = alloc_bprm(fd, filename, flags);
    if (IS_ERR(bprm)) {
        retval = PTR_ERR(bprm);
        goto out_ret;
    }
 
    retval = count(argv, MAX_ARG_STRINGS);
    if (retval == 0)
        pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
                 current->comm, bprm->filename);
    if (retval < 0)
        goto out_free;
    bprm->argc = retval;
 
    retval = count(envp, MAX_ARG_STRINGS);
    if (retval < 0)
        goto out_free;
    bprm->envc = retval;
 
    retval = bprm_stack_limits(bprm);
    if (retval < 0)
        goto out_free;
 
    retval = copy_string_kernel(bprm->filename, bprm);
    if (retval < 0)
        goto out_free;
    bprm->exec = bprm->p;
 
    retval = copy_strings(bprm->envc, envp, bprm);
    if (retval < 0)
        goto out_free;
 
    retval = copy_strings(bprm->argc, argv, bprm);
    if (retval < 0)
        goto out_free;
 
    if (bprm->argc == 0) {
        retval = copy_string_kernel("", bprm);
        if (retval < 0)
            goto out_free;
        bprm->argc = 1;
    }
 
    retval = bprm_execve(bprm); //进入真正的执行阶段
out_free:
    free_bprm(bprm);
 
out_ret:
    putname(filename);
    return retval;
}
static int bprm_execve(struct linux_binprm *bprm)
{
    int retval;
 
    retval = prepare_bprm_creds(bprm);  //准备执行凭据
    if (retval)
        return retval;
 
    check_unsafe_exec(bprm);
    current->in_execve = 1;  //执行状态标记,表示当前进程正处于 execve 中
    sched_mm_cid_before_execve(current); //检查是否允许执行,是否运行提权
 
    sched_exec();
 
    retval = security_bprm_creds_for_exec(bprm);
    if (retval)
        goto out;
 
    retval = exec_binprm(bprm);  //进入二进制加载核心
    if (retval < 0)
        goto out;
 
    sched_mm_cid_after_execve(current);
 
    current->in_execve = 0;
    rseq_execve(current);
    user_events_execve(current);
    acct_update_integrals(current);
    task_numa_free(current, false);
    return retval;
 
out:
    if (bprm->point_of_no_return && !fatal_signal_pending(current))
        force_fatal_sig(SIGSEGV);
 
    sched_mm_cid_after_execve(current);
    current->in_execve = 0;
 
    return retval;
}
static int bprm_execve(struct linux_binprm *bprm)
{
    int retval;
 
    retval = prepare_bprm_creds(bprm);  //准备执行凭据
    if (retval)
        return retval;
 
    check_unsafe_exec(bprm);
    current->in_execve = 1;  //执行状态标记,表示当前进程正处于 execve 中
    sched_mm_cid_before_execve(current); //检查是否允许执行,是否运行提权
 
    sched_exec();
 
    retval = security_bprm_creds_for_exec(bprm);
    if (retval)
        goto out;
 
    retval = exec_binprm(bprm);  //进入二进制加载核心
    if (retval < 0)
        goto out;
 
    sched_mm_cid_after_execve(current);
 
    current->in_execve = 0;
    rseq_execve(current);
    user_events_execve(current);
    acct_update_integrals(current);
    task_numa_free(current, false);
    return retval;
 
out:
    if (bprm->point_of_no_return && !fatal_signal_pending(current))
        force_fatal_sig(SIGSEGV);
 
    sched_mm_cid_after_execve(current);
    current->in_execve = 0;
 
    return retval;
}
static int exec_binprm(struct linux_binprm *bprm)
{
    pid_t old_pid, old_vpid;
    int ret, depth;
 
    old_pid = current->pid;  //保存执行前的 PID 信息
    rcu_read_lock();
    old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
    rcu_read_unlock();
 
    //解析主循环
    for (depth = 0;; depth++) {
        struct file *exec;
        if (depth > 5)
            return -ELOOP;
 
        ret = search_binary_handler(bprm);  //根据文件内容选择合适的处理器
        if (ret < 0)
            return ret;
        if (!bprm->interpreter)
            break;
 
        exec = bprm->file; // 解释器替换,下一轮执行的主角从原文件变成解释器本身
        bprm->file = bprm->interpreter;
        bprm->interpreter = NULL;
 
        allow_write_access(exec);
        if (unlikely(bprm->have_execfd)) {
            if (bprm->executable) {
                fput(exec);
                return -ENOEXEC;
            }
            bprm->executable = exec;
        } else
            fput(exec);
    }
 
    //执行成功后的系统通知
    audit_bprm(bprm);
    trace_sched_process_exec(current, old_pid, bprm);
    ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
    proc_exec_connector(current);
    return 0;
}
static int exec_binprm(struct linux_binprm *bprm)
{
    pid_t old_pid, old_vpid;
    int ret, depth;
 
    old_pid = current->pid;  //保存执行前的 PID 信息
    rcu_read_lock();
    old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
    rcu_read_unlock();
 
    //解析主循环
    for (depth = 0;; depth++) {
        struct file *exec;
        if (depth > 5)
            return -ELOOP;
 
        ret = search_binary_handler(bprm);  //根据文件内容选择合适的处理器
        if (ret < 0)
            return ret;
        if (!bprm->interpreter)
            break;
 
        exec = bprm->file; // 解释器替换,下一轮执行的主角从原文件变成解释器本身
        bprm->file = bprm->interpreter;
        bprm->interpreter = NULL;
 
        allow_write_access(exec);
        if (unlikely(bprm->have_execfd)) {
            if (bprm->executable) {
                fput(exec);
                return -ENOEXEC;
            }
            bprm->executable = exec;
        } else
            fput(exec);
    }
 
    //执行成功后的系统通知
    audit_bprm(bprm);
    trace_sched_process_exec(current, old_pid, bprm);
    ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
    proc_exec_connector(current);
    return 0;
}
static int search_binary_handler(struct linux_binprm *bprm)
{
    bool need_retry = IS_ENABLED(CONFIG_MODULES);
    struct linux_binfmt *fmt;
    int retval;
 
    retval = prepare_binprm(bprm);  //读取文件前 128 字节
    if (retval < 0)
        return retval;
 
    retval = security_bprm_check(bprm);  //安全检查
    if (retval)
        return retval;
 
    retval = -ENOENT;
 retry:
    read_lock(&binfmt_lock);
    // 解析器匹配循环
    list_for_each_entry(fmt, &formats, lh) {
        if (!try_module_get(fmt->module))
            continue;
        read_unlock(&binfmt_lock);
 
        retval = fmt->load_binary(bprm);  //检查文件魔数,判断文件格式
 
        read_lock(&binfmt_lock);
        put_binfmt(fmt);
        if (bprm->point_of_no_return || (retval != -ENOEXEC)) {
            read_unlock(&binfmt_lock);
            return retval;
        }
    }
    read_unlock(&binfmt_lock);
 
    //没有找到解析器
    if (need_retry) {
        if (printable(bprm->buf[0]) && printable(bprm->buf[1]) &&
            printable(bprm->buf[2]) && printable(bprm->buf[3]))
            return retval;
         
        if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0)
            return retval;
        need_retry = false;
        goto retry;
    }
 
    return retval;
}
static int search_binary_handler(struct linux_binprm *bprm)
{
    bool need_retry = IS_ENABLED(CONFIG_MODULES);
    struct linux_binfmt *fmt;
    int retval;
 
    retval = prepare_binprm(bprm);  //读取文件前 128 字节
    if (retval < 0)
        return retval;
 
    retval = security_bprm_check(bprm);  //安全检查
    if (retval)
        return retval;
 
    retval = -ENOENT;
 retry:
    read_lock(&binfmt_lock);
    // 解析器匹配循环
    list_for_each_entry(fmt, &formats, lh) {
        if (!try_module_get(fmt->module))
            continue;
        read_unlock(&binfmt_lock);
 
        retval = fmt->load_binary(bprm);  //检查文件魔数,判断文件格式
 
        read_lock(&binfmt_lock);
        put_binfmt(fmt);
        if (bprm->point_of_no_return || (retval != -ENOEXEC)) {
            read_unlock(&binfmt_lock);
            return retval;
        }
    }
    read_unlock(&binfmt_lock);
 
    //没有找到解析器
    if (need_retry) {
        if (printable(bprm->buf[0]) && printable(bprm->buf[1]) &&
            printable(bprm->buf[2]) && printable(bprm->buf[3]))
            return retval;
         
        if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0)
            return retval;
        need_retry = false;
        goto retry;
    }
 
    return retval;
}
retval = fmt->load_binary(bprm);  //检查文件魔数,判断文件格式
retval = fmt->load_binary(bprm);  //检查文件魔数,判断文件格式
//检查是否为 ELF 魔数 \x7FELF
if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
    goto out;
//检查是否为 ELF 魔数 \x7FELF
if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
    goto out;
if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0)
    return retval;
if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0)
    return retval;
#define request_module(mod...) __request_module(true, mod)
#define request_module(mod...) __request_module(true, mod)
int __request_module(bool wait, const char *fmt, ...)
{
    va_list args;
    char module_name[MODULE_NAME_LEN];
    int ret, dup_ret;
 
    WARN_ON_ONCE(wait && current_is_async());
 
    if (!modprobe_path[0])
        return -ENOENT;
 
    va_start(args, fmt);
    ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
    va_end(args);
    if (ret >= MODULE_NAME_LEN)
        return -ENAMETOOLONG;
 
    ret = security_kernel_module_request(module_name);
    if (ret)
        return ret;
 
    ret = down_timeout(&kmod_concurrent_max, MAX_KMOD_ALL_BUSY_TIMEOUT * HZ);
    if (ret) {
        pr_warn_ratelimited("request_module: modprobe %s cannot be processed, kmod busy with %d threads for more than %d seconds now",
                    module_name, MAX_KMOD_CONCURRENT, MAX_KMOD_ALL_BUSY_TIMEOUT);
        return ret;
    }
 
    trace_module_request(module_name, wait, _RET_IP_);
 
    if (kmod_dup_request_exists_wait(module_name, wait, &dup_ret)) {
        ret = dup_ret;
        goto out;
    }
 
    ret = call_modprobe(module_name, wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
 
out:
    up(&kmod_concurrent_max);
 
    return ret;
}
EXPORT_SYMBOL(__request_module);
int __request_module(bool wait, const char *fmt, ...)
{
    va_list args;
    char module_name[MODULE_NAME_LEN];
    int ret, dup_ret;
 
    WARN_ON_ONCE(wait && current_is_async());
 
    if (!modprobe_path[0])
        return -ENOENT;
 
    va_start(args, fmt);
    ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
    va_end(args);
    if (ret >= MODULE_NAME_LEN)
        return -ENAMETOOLONG;
 
    ret = security_kernel_module_request(module_name);
    if (ret)
        return ret;
 
    ret = down_timeout(&kmod_concurrent_max, MAX_KMOD_ALL_BUSY_TIMEOUT * HZ);
    if (ret) {
        pr_warn_ratelimited("request_module: modprobe %s cannot be processed, kmod busy with %d threads for more than %d seconds now",
                    module_name, MAX_KMOD_CONCURRENT, MAX_KMOD_ALL_BUSY_TIMEOUT);
        return ret;
    }
 
    trace_module_request(module_name, wait, _RET_IP_);
 
    if (kmod_dup_request_exists_wait(module_name, wait, &dup_ret)) {
        ret = dup_ret;
        goto out;
    }
 
    ret = call_modprobe(module_name, wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
 
out:
    up(&kmod_concurrent_max);
 
    return ret;
}
EXPORT_SYMBOL(__request_module);
static int call_modprobe(char *orig_module_name, int wait)
{
    struct subprocess_info *info;
    static char *envp[] = {
        "HOME=/",
        "TERM=linux",
        "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
        NULL
    };
    char *module_name;
    int ret;
 
    char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL);
    if (!argv)
        goto out;
 
    module_name = kstrdup(orig_module_name, GFP_KERNEL);
    if (!module_name)
        goto free_argv;
 
    argv[0] = modprobe_path;
    argv[1] = "-q";
    argv[2] = "--";
    argv[3] = module_name;
    argv[4] = NULL;
 
    info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
                     NULL, free_modprobe_argv, NULL);
    if (!info)
        goto free_module_name;
 
    ret = call_usermodehelper_exec(info, wait | UMH_KILLABLE);
    kmod_dup_request_announce(orig_module_name, ret);
    return ret;
 
free_module_name:
    kfree(module_name);
free_argv:
    kfree(argv);
out:
    kmod_dup_request_announce(orig_module_name, -ENOMEM);
    return -ENOMEM;
}
static int call_modprobe(char *orig_module_name, int wait)
{
    struct subprocess_info *info;
    static char *envp[] = {
        "HOME=/",
        "TERM=linux",
        "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
        NULL
    };
    char *module_name;
    int ret;
 
    char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL);
    if (!argv)
        goto out;
 
    module_name = kstrdup(orig_module_name, GFP_KERNEL);
    if (!module_name)
        goto free_argv;
 
    argv[0] = modprobe_path;
    argv[1] = "-q";
    argv[2] = "--";
    argv[3] = module_name;
    argv[4] = NULL;
 
    info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
                     NULL, free_modprobe_argv, NULL);
    if (!info)
        goto free_module_name;
 
    ret = call_usermodehelper_exec(info, wait | UMH_KILLABLE);
    kmod_dup_request_announce(orig_module_name, ret);
    return ret;
 
free_module_name:
    kfree(module_name);
free_argv:
    kfree(argv);
out:
    kmod_dup_request_announce(orig_module_name, -ENOMEM);
    return -ENOMEM;
}
argv[0] = modprobe_path;
argv[1] = "-q";
argv[2] = "--";
argv[3] = module_name;
argv[4] = NULL;
 
info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
                 NULL, free_modprobe_argv, NULL);
if (!info)
    goto free_module_name;
 
ret = call_usermodehelper_exec(info, wait | UMH_KILLABLE);
argv[0] = modprobe_path;
argv[1] = "-q";
argv[2] = "--";
argv[3] = module_name;
argv[4] = NULL;
 
info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
                 NULL, free_modprobe_argv, NULL);
if (!info)
    goto free_module_name;
 
ret = call_usermodehelper_exec(info, wait | UMH_KILLABLE);
char modprobe_path[KMOD_PATH_LEN] = CONFIG_MODPROBE_PATH;
char modprobe_path[KMOD_PATH_LEN] = CONFIG_MODPROBE_PATH;
#ifdef CONFIG_STATIC_USERMODEHELPER
    sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH;
#ifdef CONFIG_STATIC_USERMODEHELPER
    sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH;
CONFIG_STATIC_USERMODEHELPER_PATH="/sbin/usermode-helper"
CONFIG_STATIC_USERMODEHELPER_PATH="/sbin/usermode-helper"
qemu-system-x86_64 \
    -m 128M \
    -kernel ./bzImage \
    -initrd  ./rootfs.cpio \
    -append "root=/dev/ram rdinit=/init console=ttyS0 oops=panic panic=1 quiet rodata=off" \
    -cpu qemu64,+smep,+smap \
    -smp cores=2,threads=1 \
    -nographic
qemu-system-x86_64 \
    -m 128M \
    -kernel ./bzImage \
    -initrd  ./rootfs.cpio \
    -append "root=/dev/ram rdinit=/init console=ttyS0 oops=panic panic=1 quiet rodata=off" \
    -cpu qemu64,+smep,+smap \
    -smp cores=2,threads=1 \
    -nographic
mkdir rootfs
cd rootfs
cpio -idvm < ../rootfs.cpio        
mkdir rootfs
cd rootfs
cpio -idvm < ../rootfs.cpio        
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
 
echo "[*] Welcome to Kernel PWN Environment"
echo "[*] Starting shell..."
 
insmod /home/babydev.ko
chmod 777 /dev/noc /tmp
cp /proc/kallsyms /tmp/coresysms.txt
/home/eatFlag &
 
exec /bin/sh
# exec su -s /bin/sh ctf
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
 
echo "[*] Welcome to Kernel PWN Environment"
echo "[*] Starting shell..."
 

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 3天前 被南行编辑 ,原因:
收藏
免费 10
支持
分享
最新回复 (3)
雪    币: 281
活跃值: (848)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
1
1天前
0
雪    币: 1559
活跃值: (3017)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
3
1
1天前
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
1
16小时前
0
游客
登录 | 注册 方可回帖
返回