2019-8956 && 2019-9213 组合提权漏洞调试分析

写在前面

​ 作为第一篇开始分析调试的CVE,虽然比较难,好在有老司机带路,顺便记录Linux kernel pwn 和cve 分析调试的思路.

​ 老司机提供的思路:查阅cve信息,定位到漏洞函数的代码,对比补丁定出调用链的末端。编写poc 出发漏洞调用链(如果已经fuzz到了poc,上述步骤会轻松一些,不过调用链相关的代码分析仍然要看),分析调用路径上的可控信息,思考利用方式(利用的部分是经验活,孰能生巧,基本功掌握熟练可以提速)

0x0 环境搭建

0x01 懒喵模式(推荐)

不想自己搭环境的喵,可以直接去NAS上下镜像载下来自己调

0x02 小鱼干模式

整理了搭配好的虚拟机,开箱即用

地址

0x021 需要下载的清单

  • Ubunut 16.04 的环境

  • Linux-4.20 源源码
    wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v4.x/linux-4.20.tar.gz

  • 编译内核所需的工具

    • gcc
    • make
    • bison
    • flex
    • libssl-dev
    • ncurses-dev
    • debootstrap

    sudo apt-get install gcc make bison flex libssl-dev ncurses-dev debootstrap

  • 拉取debian的32位的文件系统

    debootstrap --arch i386 stretch debian_32 http://ftp.cn.debian.org/debian/

    拉去文件系统辅助我们编译poc

0x022 编译kernel

  • 解压

    tar -zxvf linux-4.20.tar.gz

  • 编译

    make i386_defconfig

    图形化选择编译选项:make menuconfig

    • 编译Sctp协议依赖库: Networking support –> Networking options –> The SCTP Protocol

    • 设置debug信息: Kernel hacking –> Compile-time checks and compiler options –> Compile the kernel with debug info

    • 为了调试过程尽可能看更多的信息和变量 取消优化

      修改Makefile文件中所有KBUILD_CFLAGS中 -O2-O0

    开始编译:

    make -j2 (如果核心数多,可以增加)

0x023 调试准备

  • gdb插件:pwndbg 、gef的安装

    https://github.com/hugsy/gef

    https://github.com/pwndbg/pwndbg

  • pwntools:

    pip install pwntools

  • 针对kernel的exp编译方式

    • 方法一:挂在后编译

      sudo mount file_system_dir aim_dir # 把目标架构的文件系统挂载
      cd aim_dir # 进入目标文件系统
      gcc aim.c -o aim # 编译目标exp
      sudo umount aim_dir # 取消挂载
    • 方法二:脚本传进去[未涉及]

0x1 CVE-2019-9213分析

​ 首先记录涉及到的linux kernel 知识,kernel 里面的内容非常多,系统的学习下来基本不太现实,司机推荐的学习路线是结合cve分析进行学习。在linux-4.20版本中, 存在一个漏洞,导致用户态可以映射零地址,虽然单个cve没有什么卵用,但组合一个空指针解引用的kernel bug即可成功提权。

0x10 Ring3禁止映射零地址

​ linux内核中,出现过许多零地址解引用的漏洞,因此有了禁止Ring3(普通权限)映射零地址。

0x11 patch 分析


diff --git a/mm/mmap.c b/mm/mmap.c
index f901065..fc1809b 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2426,12 +2426,11 @@ int expand_downwards(struct vm_area_struct *vma,
{
struct mm_struct *mm = vma->vm_mm;
struct vm_area_struct *prev;
- int error;
+ int error = 0;

address &= PAGE_MASK;
- error = security_mmap_addr(address);
- if (error)
- return error;
+ if (address < mmap_min_addr)
+ return -EPERM;

/* Enforce stack_guard_gap */
prev = vma->vm_prev;

patch告诉我们,security_mmap_addr的返回之有可能比mmap_min_addr小,这样做是不安全的。

通过grep -rn -i "mmap_min_addr" 可以发现CONFIG_DEFAULT_MMAP_MIN_ADDR=4096,address可能取到0~4k,显然违反了用户态禁止映射零地址。

大师傅推荐阅读linux kernel 源码利用这个网站,带索引的跳转非常方便https://elixir.bootlin.com/linux/v4.20/source

0x12 调用链分析

​ 这个调用链有两种分析方法:

  • fuzz到crash(动态分析)[未涉及]

    有crash样本的情况下工作量稍微小一些,分析调用路径上的符号是否可控情况,便可以去构造poc

  • 只有cve的信息(静态分析)

    这种情况要根据configure文件分析所有可能的调用链,在去分析关键变量是否可由外部可控,再去尝试构造poc

    kernel Fuzz 因为时间太紧,老司机没有讲,这里用静态分析的方法。

方法可以一句话概括,根据代码里函数调用关系恢复从入口到漏洞函数处的程序执行路径,根据编译选项

调用链1:
creat_elf_table->
prq_event_thread->
do_fault->
fixup_user_fault-> find_extend_vma

调用链3:mem_write -> mem_rw -> access_remote_vm -> __access_remote_vm -> get_user_pages_remote-> __get_user_pages_locked->get_user_pages->__get_user_pages-> find_extend_vma ->expand_stack->expand_downwards-> security_mmap_addr
调用链2: 因为这里是高地址上加的,0地址很难被映射,并且编译文件中没有CONFIG_STACK_GROWUP选项,这条链不用跟
expand_upwards -> security_mmap_addr

分析的是调用链三,其他调用链可以在做尝试。

0x13 构造poc(exp)

由于这个漏洞是一个逻辑漏洞,因此很难导致程序崩溃,因此构造poc和exp的目标是看能不能想发让Ring3有机会映射0x00地址

0x2 cve-2019-8956

0x20 基础知识

0x21 patch分析

0x22 调用链分析

0x23 构造poc

0x24 可利用点分析

0x25 构造exp

0x3 附件

0x4 总结

Author: Siamese
Link: https://siamesejuly.github.io/2020/01/15/%E7%BB%84%E5%90%88%E6%8F%90%E6%9D%83%E6%BC%8F%E6%B4%9E%E8%B0%83%E8%AF%95%E5%88%86%E6%9E%90_kernel_pwn/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.