0x00 写在前面:一份踩坑与学习记录
这篇文章是我复现 2019 年 SUCTF 内核题 SUDriver 时的一篇学习笔记。作为内核安全的初学者,在死磕这道题的过程中,我花了不少时间尝试自己动手把 Exploit 给搓出来。在这个过程中踩了不少坑,也学到了很多,特此记录下来。
文章主要记录了以下几个学习点:
漏洞原语的转化: 记录了自己如何利用格式化字符串泄露地址绕过 KASLR,并尝试将驱动中的越界写(OOB Write)转化为可以劫持内核执行流的原语。
堆风水(Heap Feng Shui)的尝试与失败经验: 在选择覆盖对象时,我一开始其实想用 timerfd_ctx,但实操后发现越界写不可避免地破坏了红黑树节点,苦苦找不到合适的 Gadget。在不断的试错后,最后才换成了利用 seq_operations 结构体进行堆喷射(Heap Spraying)。
栈迁移与执行流恢复: 梳理了在成功覆盖 seq_operations->start 指针后,如何计算与 pt_regs 的偏移来进行栈迁移(Stack Pivot),以及如何借助 swapgs_restore_regs_and_return_to_usermode 函数绕过 KPTI,最终跌跌撞撞地安全返回用户态。
本人目前是一名大三学生,正在二进制漏洞和 Linux 内核方向艰难摸索。深知自己距离行业的标准还有很长的路要走,很多底层机制理解得也还不够透彻。将笔记发出来主要是想向各位前辈请教,同时本人也正在寻找全国范围内的实习机会,渴望能有一个真实的实战环境让我继续学习。文章中难免存在错漏,恳请各位师傅轻喷并指正!
Github链接:546K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6^5L8Y4q4B7L8Y4x3`.
个人博客:69aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5L8Y4q4B7L8Y4y4Q4x3X3g2Y4K9i4c8Z5N6h3u0Q4x3X3g2A6L8#2)9J5c8R3`.`.
0x01 环境分析与漏洞审计
启动脚本中开启了 smep 和 kaslr,内核版本为 4.20.12
#! /bin/sh
qemu-system-x86_64 \
-m 128M \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=0 kaslr" \
-monitor /dev/null \
-nographic 2>/dev/null \
-smp cores=2,threads=1 \
-s \
-cpu kvm64,+smep
/ $ cat /sys/devices/system/cpu/vulnerabilities/meltdown
Mitigation: PTI
Linux (none) 4.20.12 #1 SMP Mon Feb 25 20:42:55 CST 2019 x86_64 GNU/Linux
0x02 驱动分析
ioctl
0x73311337 是创建一个堆块,大小自定义,最大 0xFFE,0x13377331 是释放堆块,在利用中没什么用
void __fastcall sudrv_ioctl(__int64 a1, int a2, __int64 a3)
switch ( a2 )
case 0x73311337:
if ( (unsigned __int64)(a3 - 1) <= 0xFFE )
su_buf = (char *)_kmalloc(a3, 4718624LL);
break;
case 0xDEADBEEF:
if ( su_buf )
sudrv_ioctl_cold_2((__int64)su_buf);
break;
case 0x13377331:
kfree(su_buf);
su_buf = 0LL;
break;
格式化字符串漏洞
明显的一个格式化字符串漏洞,可以用于泄露内核基地址
void __fastcall sudrv_ioctl_cold_2(__int64 a1)
printk(a1);
JUMPOUT(0x38LL);
越界写漏洞
这个函数是IDA识别错误,底层还是write函数,参数分别为文件操作符,写入数据,写入长度,长度我们可以自定义,所以这是一个越界写
__int64 sudrv_write()
if ( (unsigned int)copy_user_generic_unrolled(su_buf) )
return -1LL;
else
return sudrv_write_cold_1();
0x03 利用思路
有任意地址写,而且内核版本 4.20.12 的环境下 ,可以大概是只能利用越界写修改结构体指针的方式控制内核执行流,我原本打算利用 tty_struct 结构体的,但是这个环境没有挂载 devpts 伪文件系统,所以用了 seq_operations 结构体来做平替
小提示 : 这道题没有开启在 4.14 引入的 Hardened freelist 所以,劫持 modprobe_path 也是一个不错的选择
踩坑 : 我还尝试了 timerfd_ctx 结构体,但是越界写破坏了红黑树节点,虽然程序没有开启 smap,我们可以在用户态伪造红黑树节点,但是因为该版本的timerfd_ctx 结构体偏移问题,没有合适的 gadget 可用,最后放弃了
堆喷构造利用环境
真的是很奇怪,不知道为什么用户堆块就是进不去梳子型的堆喷....
//基础的用户态状态保存,绑核和获取驱动交互接口
save_status();
bind_core(0);
dev_fd = open("/dev/meizijiutql", O_RDWR);
if (dev_fd < 0)
perror("[-] Failed to open /dev/meizijiutql");
exit(EXIT_FAILURE);
printf("[+] Successfully opened device!\n");
我采用了 [堆喷,创建堆块,堆喷] 的方式完成堆布局
#define MAX_SPRAY_SIZE 1000
for (int i = 0;i < MAX_SPRAY_SIZE-300;i++)
operations_id[i] = open("/proc/self/stat",O_RDONLY);
if (
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-291413.htm
[分享]2019-SUCTF-SUDriver [seq_operations]
173 浏览
0 回复
暂无回复,快来抢沙发吧!