flying kernel 复现
1 | ➜ files cpio -idmv < rootfs.img |
- 提取 gadget
1 | ➜ files cat boot.sh |
- 开了 smep(防止
ret2usr
攻击)和 kaslr(内核地址随机化) - 用了2个核心,2个线程(限制线程数量,可能有条件竞争)
1 | ➜ rootfs cat init |
- 不允许普通用户使用
dmesg
命令 - 普通用户都无法读取内核符号地址
- /proc/kallsyms 中,其它用户(Other Users)没有权限,不能 cat
- 把“自动关机”删除,方便调试
- 加载了一个
flying.ko
的驱动文件,先对其进行逆向分析
先看看 ioctl 中注册了什么函数:
1 | __int64 __fastcall seven_ioctl(__int64 fd, __int64 command, __int64 size) |
- 0x6666:释放 sctf_buf
- 0x7777:打印 sctf_buf
- 0x5555:申请 sctf_buf(大小限制为 0x80)
PS:kmem_cache_alloc_trace 是 slab 算法中的函数,kmalloc_caches(缓存描述符)是相当重要的结构体,包含了一些缓存的管理数据,和指向实际缓存空间的指针
其他函数:
1 | unsigned __int64 __fastcall seven_write(__int64 a1, __int64 buf, unsigned __int64 size) |
- sctf_buf 中有 0x80 的 heap 空间
- seven_write 会保证该空间的最后一个字节一定有数据
漏洞分析
1 | -smp cores=2,threads=2 |
- 限制线程数量,可能有条件竞争
1 | case 0x6666: |
- kfree 没有置空 sctf_buf 指针,UAF
1 | case 0x7777: |
- printk 在内核源码中用来记录日志信息的函数,只能在内核源码范围内使用,用法和 printf 非常相似,所以存在格式化漏洞
入侵思路
我们先进行调试:
对于内核中的各个符号来说,我们也可以通过以下命令来查看一些符号在内存中的加载地址:
1 | grep <symbol_name> /proc/kallsyms # symbol_name == 驱动的名称 |
1 | / |
- 同时,调试内核时,为了加载 vmlinux 符号表,必须额外指定
-append "nokaslr"
以关闭 kernel ASLR,这样符号表才能正确的对应至内存中的指定位置,否则将无法给目标函数下断点
还可以使用以下指令来获取符号表:
1 | / |
但是 vmlinux 的符号表似乎被删除了:
1 | pwndbg> add-symbol-file vmlinux 0xffffffffc02d4000 |
- 没办法,没有符号表调试不了,一秒一步的 kernel 太难受了
先利用格式化漏洞来泄露数据:
1 | add(fd); |
1 | / $ ./exp |
- slub 分配器在
kmem_cache_cpu
中使用freelist
管理空闲对象,类似于 glibc 中的 fastbin
由于开启了 freelist 随机化和 Harden_freelist 保护,以上数据并不是正确的
1 | static inline void *get_freepointer_safe(struct kmem_cache *s, void *object) |
- 加固指针 = 空闲指针 ^ 空闲指针地址 ^ 随机数R,只要知道这些值就可以绕过 Harden_freelist
- 关于 freelist 的保护可以看这篇文章:slub堆溢出的利用 - 安全客
这里出现了两种思路:
- 利用条件竞争劫持 subprocess_info
- 泄露 random 劫持 freelist 打 modprobe_path
利用条件竞争劫持 subprocess_info
在使用 socket(22, AF_INET, 0)
时会分配结构体 subprocess_info
1 | struct subprocess_info { |
- 此对象在分配时最终会调用 cleanup 函数,如果我们能在分配过程中把 cleanup 指针劫持为我们的 ROP 链
- 为了
subprocess_info
不被破坏,我们只能覆盖 cleanup 指针
模板如下:
1 | unsigned long *info = (unsigned long*)arg; |
- 这些 gadget 都可以通过
ropper
来找 - commit_creds,prepare_kernel_cred 可以通过
grep <symbol_name> /proc/kallsyms
来找(记得开 root,关闭 kernel ASLR) - 而 user_cs 这些寄存器的值,可以通过
save_status
来获取
1 | xchg_eax_esp = 0xffffffff81011cb0 + raw_kernel; // xchg eax, esp; ret; |
1 | u_int64_t user_cs, user_gs, user_ds, user_es, user_ss, user_rflags, user_rsp; |
接下来就是条件竞争的利用了:
- 我们的目标是在
subprocess_info->cleanup
中覆盖 ROP 链(首地址) - 由于程序有 kmalloc-128 的 UAF,所以很轻松就可以把
subprocess_info
分配到sctf_buf
中 - 但是好像不能直接修改
subprocess_info
,因为socket
调用后,马上就会执行cleanup
,根本就没有修改的时间 - 采用条件竞争,在
socket
执行后,cleanup
执行前,利用write
向cleanup
中填入 ROP 链
完整 exp:
1 |
|
小结:
这是我们组里大佬出的题,学了条件竞争后我就想试试,但太菜了一直拖到今天
现在还是只能依葫芦画瓢,但大佬的思路的 exp 我算是看懂了
我觉得我还需要大量的练习,提高熟练度