这个题目是内核空间的栈溢出,关于内核模块的漏洞网上有很多分析,在这里我打算只说下自己通过这个题目获得的知识,在此记录一下
Load CTF Files to QEMU
CTF files包含了4个文件
1 | ├── bzImage |
start.sh是系统的启动脚本,可以看到开启了kaslr
1 | qemu-system-x86_64 \ |
- qemu-system-x86_64:模拟x86_64架构
- -m:指定RAM大小
- -kernel:加载的内核镜像
- -initrd:指定内核启动的文件系统
- -s:相当于 -gdb tcp::1234,绑定端口,使用gdb调试
还提供了vmlinux,这里说下vmlinux和bzImage的关系refference
vmlinux是未压缩的、静态链接的、可执行的、但是不能bootable的内核文件
vmlinuz是由vmlinux经过压缩后,可以bootable的内核文件,它实际上就是zImage
或bzImage
bzImage是vmlinuz经过gzip压缩后的文件,适用于大内核
zImage是vmlinuz经过gzip压缩后的文件,适用于640k
内存的小内核
但是题目提供的vmlinux和内核使用的不一样,所以需要从文件系统中提取文件
提取cpio文件系统中的文件
1 | mkdir core |
vmlinux是未压缩的内核文件,可以从这里找Gadgets
core.ko就是存在漏洞的内核模块
init是启动的配置文件
1 |
|
poweroff -d 120 -f & 设置了定时关机,影响调试,需要注释掉echo 1 > /proc/sys/kernel/kptr_restrict
限制不能从 /pro/kallsyms
获取内核符号表信息
/proc/kallsyms包含了kernel image和所有动态加载模块的符号表。如果一个函数被编译器内联(inline)或者优化掉了,则它在/proc/kallsyms有可能找不到。此外,如果不是root用户,则显示/proc/kallsyms中的地址都是0:
但是cat /proc/kallsyms > /tmp/kallsyms
这句,将kallsyms拷贝了一份在/tmp目录下,可以在这里查找commit_creds和prepare_kernel_cred的地址
Privilege Transfer
Since userspace and kernel space are in different memory segment with different permission, we need some instructions to switch the spaces.
To switch from userspace to kernel space, we need to use syscall. To switch back,we need to use two instructions as blew.
1 | swapgs |
The typical use of SWAPGS is to keep the ‘user’ GS (which likely has a base address of 0) in the GSBase MSR, and the address of the kernel’s per-CPU structure in KernelGSBase. Upon entry to Ring 0 (through a system call, software interrupt, or some other method), the kernel will use SWAPGS so accessing [GS:0]
will get the pointer to the kernel data. Upon leaving Ring 0, SWAPGS will be called again, to switch GS back to the ‘user’ GS. refference
Iretq instruction recovery the context of userspace, thus we come back from kernelspace to userspace. Before use this instruction,some value should be stored in the stack, and the stack construction as blew.
当使用IRET指令返回到相同保护级别的任务时,IRET会从栈将返回指令指针
、返回代码段选择器
以及EFLAGS映像
分别弹入RIP、CS以及RFLAGS寄存器,然后恢复执行中断的程序或过程。
当使用IRET指令返回到不同保护级别时,IRET不仅会从栈弹出以上内容,还会弹出RSP
以及SS
。
栈上保存了trap frame,返回到用户模式的时候,恢复信息从以下结构中读取:
1 | struct trap_frame { |
1 |
|
privilege escalation
getting privilege escalation via commit_creds(prepare_kernel_cred(0)
The above privilege escalation payload allocates a new credential struct (with uid = 0, gid = 0, etc.) and applies it to the calling process.
debugging
target remote:1234通过端口调试,gdb ./vmlinux启动,只加载了 kernel 的符号表,模块加载进内核后,并没有作为vmliunx的一部分传给gdb,因为需要加载内核模块的符号表,由于模块也是一个ELF文件,需要知道模块的.text的节区地址。这个信息分别保存/sys/module/stack_smashing/sections/.text中
1 | gdb ./vmlinux |
1 | / $ id |