新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook

一、前言

众所周知,在 Windows 系统上如果要加载一个驱动,要么在系统启动时关闭签名校验,要么给自己驱动签名。干 Windows 研究的大伙又没有干 Web的有钱,签名买是不可能买的,就只能嫖嫖泄露的签名才能维持的了生活这样子,但很多时候泄露的签名是被加到黑名单里的,签名一旦被使用就会被各大安全、保护软件来回鞭尸,那么有没有办法在不关闭签名校验且驱动没有签名的情况下,加载一下我们自己写的驱动呢?答案是有的(水字数)。

一般来说有两种方法,且两种方法都是需要一个正规厂家的带签名和带任意地址读写漏洞的驱动。初学者不要看到需要漏洞就放弃尝试,这种漏洞非常常见,可以从各大主板、显卡、杀软、驱动管理、交友软件中慢慢挖掘。虽然安全厂商目前安全生产水平已经逐渐提高,但不代表之前版本的驱动没有漏洞,耐心找一找基本上还是能够找到的,不过这两种方法对漏洞要求有所不同。

第一种方法是要求漏洞任意地址读写任意多个字节数的内存,那么就直接内存加载就行了,只要驱动不带异常处理相关代码,基本上这一套就可以解决了。

第二种方法就是使用带有任意地址读写一个字节或以上内存漏洞的驱动,这种方法就是使用 DSE bypass,通过寻找 DSE 开关相关的全局变量,利用瞬时修改的方式(rp差到极致可能会触发PG),加载自己的驱动。

二、内存加载驱动

本文参考了开源项目 kdmapper,该项目仅支持系统版本17134以上,项目使用的是 Intel 的一个带有任意地址读写漏洞的驱动:iqvw64e.sys。引发漏洞的 ioctl 为 0x80862007,利用该 ioctl 可以 map 内存、读写内存、获取物理页等等,该漏洞相关参考资料可以另行查询,本文不详细讲述该漏洞的背景。

2.1 通过漏洞调用内核API

通过 NtQuerySystemInformation 查询 SystemModuleInformation 获取 NTOS 的基地址,利用任意读写漏洞来获取 NTOS 的导出表,通过函数名比较来获取相关的内核 API 地址。图片[1]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[1] 利用漏洞读写内存部分

图片[2]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[2] 利用漏洞 map 内存部分

获取到内核 API 地址之后,根据系统版本 Hook 相关内核 API,调用完之后一定要恢复 Hook,当然就算这样也有几率会触发 PG。

这里只讲一下操作系统版本为19041的方式,是通过 Hook NTOS 的中NtQueryInformationAtom 这个 API 来进行的,将函数的头部 Hook 到需要调用的 API 之中,最后调用三环的 NtQueryInformationAtom 触发调用内核中该函数。
Hook 是通过发送 ioctl 来进行的,由于函数是只读内存,只读内存的 Hook 需要 map。图片[3]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[3] map只读内存进行 Hook

图片[4]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[4] Hook 部分

2.2 内核加载驱动流程

项目开头主要查看一下环境是否适配,如果适配,则将 Intel 驱动文件释放到temp 目录之下,使用 SCM 加载 Intel 驱动文件。图片[5]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[5] 使用 SCM 加载 Intel 驱动

在三环中将自己的驱动文件 PE 展开,修复导入表、修复重定位等导入表项,将展开好的 PE 文件拷贝至内核内存。图片[6]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[6]  PE展开

解析 OEP 利用漏洞调用内核 API ,即可调用自己的黑驱动的 DriverEntry,而且后面也会将 PE 头给抹除。

图片[7]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[7] 抹除PE头

如果是采用这种方式加载的驱动的话,还可以考虑使用 HIDE VAD 等方式隐藏一下自己的驱动模块。

2.3 参考项目及资料

https://github.com/TheCruZ/kdmapperhttps://github.com/422926799/Mhyprot2DrvControl

三、通过 DSE bypass 的方式加载驱动

本文主要参考 DSEFix 项目,在该项目中所使用的驱动程序为 VirtualBox 的驱动程序,所以不太兼容 VirtualBox 的虚拟机。关于 DSE bypass 这个技术点,研究人员找到的较早的参考资料是由 mj0011(yyds)所写的 PPT。

3.1 内核全局变量 g_CiEnabled  与 g_CiOptions

当系统版本低于 Windows8 时,关闭 DSE 的方式是通过从 NTOS 中找到全局变量 g_CiEnabled,将变量修改为0;当系统版本为 Windows8 及以上版本的时候则从 CI.dll 中找到全局变量 g_CiOptions ,置位为6绕过 DSE ,但Windows8 及以上版本存在PG,瞬时修改还是会有几率触发 PG。图片[8]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[8] 根据版本构造 Shellcode 绕过 DSE

3.2 DSE byPass 流程

1、判断系统版本,通过 NtQuerySystemInformation 查找到对应系统版本需要找的模块基地址。低于 Windows8 版本时,通过特征查找 g_CiEnabled 全局变量。查找全局变量的方式还较为简单,直接使用特征码加偏移的方式查找即可。图片[9]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[9]  查找 g_CiEnabled 全局变量

2、Windows 8 及以上版本,查找 g_CiOptions 变量。

较为通用的方法为解析 CI.dll 的导出表,找到 CiInitialize 的函数地址,以 CiInitialize 的函数地址为 base 找到 CipInitialize 的函数地址,再以CipInitialize 为 base ,通过特征码搜索得到 g_CiOptions 的地址。图片[10]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[10] 通过 CiInitialize 找到 CipInitialize  的函数地址图片[11]-新生吃老饭之无签驱动的两种加载方式 – 作者:Threatbook-安全小百科

图[11] 通过 CipInitialize 的地址特征码搜索获取 g_CiOptions 的地址

通过给驱动发送 ioctl 修改相应的全局变量,即可使用 SCM 加载自己的无签名驱动,再恢复全局变量原有的值,以防触发 PG 。通过这种方式加载驱动的话,需要注意抹除痕迹、隐藏行为,例如 PiDDBCacheTable ,而且这样也不是很好断链,断链会触发 PG 。如果一定要使用这种方式加载驱动的话,推荐还是加载一个驱动的 PELoader ,需要清空注册表信息,然后在 DriverEntry 中加载了 PE 之后返回STATUS_UNSUCCESSFUL,可以做到更加无痕。

3.3 参考项目及其资料

https://github.com/hfiref0x/DSEFix

https://www.powerofcommunity.net/poc2012/mj0011.pdf

四、总结

在具有读写漏洞的驱动上还可以做到其它的恶意行为,如进行句柄权限的修改对抗等等。本文作者旨在分析 Rootkit 中加载阶段较为常用的手法,敬请期待后续 Rootkit 分析报告。

来源:freebuf.com 2021-07-16 15:31:51 by: Threatbook

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论