Protected Processes Part 3 : Windows PKI Internals (Signing Levels, Scenarios, Root Keys, EKUs & Runtime Signers)

Introduction

In this last part of our series on protected processes in Windows 8.1, we’re going to be taking a look at the cryptographic security that protects the system from the creation or promotion of arbitrary processes to protected status, as well as to how the system is extensible to provide options for 3rd party developers to create their own protected processes.

In the course of examining these new cryptographic features, we’ll also be learning about Signing Levels, a concept introduced in Windows 8. Finally, we’ll examine how the Code Integrity Library DLL (Ci.dll) is responsible for approving the creation of a protected process based on its associated signing level and digital certificate.

Signing Levels in Windows 8

Before Windows 8.1 introduced the protection level (which we described in Part 1 and Part 2), Windows 8 instituted the Signing Level, also sometimes referred to as the Signature Level. This undocumented number was a way for the system to differentiate the different types of Windows binaries, something that became a requirement for Windows RT as part of its requirement to prohibit the execution of Windows “desktop” applications. Microsoft counts among these any application that did not come from the Windows Store and/or which was not subjected to the AppContainer sandboxing technology enforced by the Modern/Metro programming model (meanwhile, the kernel often calls these “packaged” applications).

I covered Signing Levels in my Breakpoint 2012 presentation, and clrokr, one of the developers behind the Windows RT jailbreak, blogged about them as well. Understanding signing levels was critical for the RT jailbreak: Windows introduced a new variable, SeILSigningPolicy, which determined the minimum signing level allowed for non-packaged applications. On x86, this was read from the registry, and assumed to be zero, while on ARM, this was hard-coded to “8”, which as you can see from clrokr’s blog, corresponds to “Microsoft” – in effect allowing only Microsoft-signed applications to run on the RT desktop. The jailbreak, then, simply sets this value to “0”.

Another side effect of Signing Levels was that the “ProtectedProcess” bit in EPROCESS was removed — whether or not a Windows 8 process is protected for DRM purposes (such as Audiodg.exe, which handles audio decoding) was now implied from the value in the “SignatureLevel” field instead.

Signing Levels in Windows 8.1

In Windows 8.1, these levels have expanded to cover some of the needs introduced by the expansion of protected processes. The official names Microsoft uses for them are shown in Table 1 below. In addition, the SeILSigningPolicy variable is no longer initialized through the registry. Instead, it is set through the Secure Boot Signing Policy, a signed configurable policy blob which determines which binaries a Windows 8.1 computer is allowed to run. The value on 8.1 RT, however, remains the same – 8 (Microsoft), still prohibiting desktop application development.

Windows 8.1 Signing Levels

Signing LevelName
0Unchecked
1Unsigned
2Custom 0
3Custom 1
4Authenticode
5Custom 2
6Store
7Custom 3 / Antimalware
8Microsoft
9Custom 4
10Custom 5
11Dynamic Code Generation
12Windows
13Windows Protected Process Light
14Windows TCB
15Custom 6

Furthermore, unlike the Protection Level that we saw in Parts 1 and 2, which is a process-wide value most often used for determining who can do what to a process, the Signature Level is in fact subdivided into both an EXE signature level (the “SignatureLevel” field in EPROCESS) as well as a DLL signature level (the “SectionSignatureLevel” field in the EPROCESS structure). While the former is used by Code Integrity to validate the signature level of the primary module binary, the latter is used to set the minimum level at which DLLs on disk must be signed with, in order to be allowed to load in the process. Table 2, which follows, describes the internal mapping used by the kernel in order to assign a given Signature Level for each particular Protected Signer.

Protected Signers to Signing Level Mappings

Protected SignerEXE Signature LevelDLL Signature Level
PsProtectedSignerNoneUncheckedUnchecked
PsProtectedSignerAuthenticodeAuthenticodeAuthenticode
PsProtectedSignerCodeGenDynamic Code GenerationStore
PsProtectedSignerAntimalwareCustom 3 / AntimalwareCustom 3 / Antimalware
PsProtectedSignerLsaWindowsMicrosoft
PsProtectedSignerWindowsWindowsWindows
PsProtectedSignerWinTcbWindows TCBWindows TCB

Scenarios and Signers

When the Code Integrity library receives a request from the kernel to validate an image (i.e.: to perform page hash or image hash signature checks), the kernel sends both the signing level (which it determined based on its internal mapping matching Table 2 from above) as well as a bit mask called the Secure Required. This bit mask explains to Code Integrity why image checking is being done. Table 3, shown below, describes the possible values for Secure Required.

Secure Required Bit Flags

Bit ValueDescription
0x1Driver Image. Checks must be done on x64, ARM, or if linked with /INTEGRITYCHECK.
0x2Protected Image. Checks must be done in order to allow the process to run protected.
0x4Hotpatch Driver Image. Checks must be done to allow driver to hotpatch another driver.
0x08Protected Light Image. Checks must be done in order to allow the process to run PPL.
0x10Initial Process Image. Check must be done for User Mode Code Signing (UMCI) reasons.
Based on this bit mask as well as the signing level, the Code Integrity library converts this information into a Scenario. Scenarios describe the signing policy associated with a specific situation in which signature checking is being done.

The system supports a total of 18 scenarios, and their goal is three-fold: determine the minimum hash algorithm that is allowed for the signature check, and determine if only a particular, specific Signer is allowed for this scenario (a Signer is identified by the content hash of the certificate used to sign the image) and which signature level the Signer is allowed to bestow.

Table 4 below describes the standard Scenarios and their associated Security Required, Signing Level, and minimum Hash Algorithm requirements.

Scenario Descriptions and Hash Requirements

ScenarioSecure RequiredSigning LevelHash Algorithm
0N/AWindows TCBCALG_SHA_256
1Hotpatch ImageWindowsCALG_SHA_256
2N/AMicrosoftCALG_SHA1
3N/AStoreCALG_SHA1
4Protected ImageAuthenticodeCALG_SHA1
5Driver ImageN/ACALG_SHA1
6N/AAuthenticodeCALG_SHA1
7N/ADynamic Code GenerationCALG_SHA_256
8*N/AN/ACALG_SHA_256
9N/ACustom 0CALG_SHA_256
10N/ACustom 1CALG_SHA_256
11N/ACustom 2CALG_SHA_256
12N/ACustom 3CALG_SHA_256
13N/ACustom 4CALG_SHA_256
14N/ACustom 5CALG_SHA_256
15N/ACustom 6CALG_SHA_256
16N/AWindows Protected LightCALG_SHA_256
17**N/AN/ACALG_SHA_256
18N/AUnchecked or InvalidCALG_SHA1

* Used for checking the Global Revocation List (GRL)
** Used for checking ELAM drivers

From this table we can see three main types of scenarios:

  • Those designed to match to a specific signing level that is being requested (0, 1, 2, 3, 6, 9, 10, 11, 12, 13, 14, 15, 16)
  • Those designed to support a specific “legacy” scenario, such as driver loads or DRM protected processes (1, 4, 5)
  • Those designed for specific internal requirement checks of the cryptographic engine (8, 17)

As expected, with Microsoft recommending the usage of SHA256 signatures recently, this type of signature is enforced on all their internal scenarios, with SHA1 only being allowed on driver and DRM protected images, Windows Store applications, and other generic Microsoft-signed binaries (presumably for legacy support).

The scenario table described in Table 4 is what normally ships with Code Integrity on x86 and x64 systems. On ARM, SHA256 is a minimum requirement for almost all scenarios, as the linked MSDN page above explained. And finally, like many of the other cryptographic behaviors in Code Integrity that we’ve seen so far, the table is also fully customizable by a Secure Boot Signing Policy.

When such a policy is present, the table above can be rewritten for all but the legacy scenarios, and custom minimum hash algorithms can be enforced for each scenario as needed. Additionally, the level to scenario mappings are also customizable, and the policy can also specify which “Signers”, identified by their certificate content hash, can be used for which Scenario, as well as the maximum Signing Level that a Signer can bestow.

Accepted Root Keys

Let’s say that the Code Integrity library has received a request to validate the page hashes of an image destined to run with a protection level of Windows TCB, and thus presumably with Scenario 0 in the standard configuration. What prevents an unsigned binary from satisfying the scenario, or perhaps a test-signed binary, or even a perfectly validly signed binary, but from a random 3rd party company?

When Code Integrity performs its checks, it always remembers the Security Required bit mask, the Signature Level, and the Scenario. The first two are used early on to decide which Root CA authorities will be allowed to participate in the signature check — different request are subject to different accepted root keys, as per Table 5 below.

Note that in these tables, PRS refers to “Product Release Services”, the internal team within Microsoft that is responsible for managing the PKI process and HSM which ultimately signs every officially released Microsoft product.

Accepted Root Keys

Secure RequiredSigning LevelAccepted Root Keys
Protected ImageN/APRS Only
Hotpatch ImageN/ASystem and Self Signed Only
Driver ImageN/APRS Only
N/AStoreWindows and PRS Only
N/AWindowsWindows and PRS Only
N/AWindows TCBPRS Only
N/AAuthenticodePRS, Windows, Trusted Root

Additionally, Tabke 6 below describes overrides that can apply based on debug options or other policy settings which can be present in the Secure Boot Signing Policy:

Accepted Root Key Overrides

OptionEffect on Root Key Acceptance
Policy Option 0x80Enables DMD Test Root
Policy Option 0x10Enables Test Root
/TESTSIGNING in BCDEnables Test Root for Store and Windows TCB Signing Levels. ?Also enables System Root, Self Signed Root and allows an Incomplete Signing Chain for other levels.

Two final important exceptions apply to the root key selection. First, when a custom Secure Boot Signing Policy is installed, and it contains custom signers and scenarios, then absolutely all possible root keys, including incomplete chains, are allowed. This is because it will be the policy that determines which Signer/Hash, Scenario/Level mapping is valid for use, not a hard-coded list of keys.

The second exception is that certain signature levels are “runtime customizable”. We’ll talk more about these near the end of this post, but for now, keep in mind that for any runtime customizable level, all root keys are also accepted. We’ll see that this is because just like with custom signing policies, runtime customizable levels have additional policies based on the signer and other data.

As you can see, this first line of defense prohibits, for example, non PRS-signed image from ever being loaded as a driver or as a DRM-protected process. It also prevents any kind of image from ever reaching a signing level of Windows TCB (thus prohibiting the underlying protection level from ever being granted).

Of course, just looking at root keys can’t be enough — the Windows Root Key is used to sign everything from a 3rd party WHQL driver to an ELAM anti-malware process to a DRM-protected 3rd party Audio Processing Object. Additional restrictions exist in place to ensure the proper usage of keys for the appropriately matching signature level.

Modern PKI enables this through the presence of Enhanced Key Usage (EKU) extensions in a digital signature certificate, which are simply described by their unique OID (Object Identifier, a common format for X.509 certificates that describes object types).

Enhanced Key Usages (EKUs)

After validating that an image is signed with an appropriate certificate that belongs to one of the allowed root keys, the next step is to decide the signing level that the image is allowed to receive, once again keeping in mind the security required bit mask.

First of all, a few checks are made to see which root authority ultimately signed the image, and whether or not any failures are present, keeping account of debug or developer policy options that may have been enabled. These checks will always result in the Unsigned (1), Authenticode (4) or Microsoft (8) signature level to be returned, regardless of other factors.

In the success cases, the following EKUs, shown in Table 7, are used in making the first-stage determination:

EKU to Signing Level Mapping

EKU OID ValueEKU OID NameGranted Signing Level
1.3.6.1.4.311.76.3.1Windows StoreStore *
1.3.6.1.4.311.76.5.1Dynamic Code GeneratorDynamic Code Generation
1.3.6.1.4.311.76.8.1Microsoft PublisherMicrosoft
1.3.6.1.4.311.10.3.5Windows Hardware Driver VerificationMicrosoft
1.3.6.1.4.311.10.3.6Windows System Component VerificationWindows
1.3.6.1.4.311.10.3.20Windows Kits ComponentMicrosoft **
1.3.6.1.4.311.10.3.23Windows TCB ComponentWindows TCB
1.3.6.1.4.311.10.3.25Windows Third Party Application ComponentAuthenticode
1.3.6.1.4.311.10.3.26Windows Software Extension VerificationMicrosoft

* Configurable by Secure Boot Signing Policy
** Only if Secure Boot Signing Policy Issued by Windows Kits Publisher

Next, the resulting signature level is compared with the initial desired signature level. If the level fails to dominate the desired level, a final check is made to see if the signing level is runtime customizable, and if so, this case is handled separately as we’ll see near the end of this post.

Finally, if the resulting signature level is appropriate given the requested level, a check is made to see if the Security Required includes bits 2 (Protected Image) and/or 8 (Protected Light Image). If the latter is present, and if the Windows signature level (12) is requested, two additional EKUs are checked for their presence — at least one must be in the certificate:

  • 1.3.6.1.4.1.311.10.3.22, Protected Process Light Verification
  • 1.3.6.1.4.1.311.10.3.22, Protected Process Verification

In the former case, i.e.: a Security Required bit mask indicating a Protected Image, then if the Windows TCB signature level (14) was requested, only the latter EKU is checked.

System Components

You can right-click on any PE file in Windows Explorer which has an embedded certificate and click on the “Digital Signatures” tab in the “Properties” window that you select from the context menu. By double-clicking on the certificate entry, and then clicking on “View Certificate”, you can scroll down to the “Enhanced Key Usages” line and see which EKUs are present in the certificate.

Here’s some screenshots of a few system binaries, which should now reveal familiar EKUs based on what we’ve seen so far.

First of all, here’s Audiodg.exe. All it has is the “Windows” EKU.

audiodg

Next up, here’s Maps.exe, which has the “Store” EKU:

Maps

And finally, Smss.exe, which has both the “Windows” and “Windows TCB” EKU, as well as the “Windows Process Light Verification” EKU.

smss

Runtime Signers 

We’ve mentioned a few cases where the system checks if a signature level is runtime customizable, and if so, proceeds to additional checks. As of Windows 8.1, in the absence of a Secure Boot Signing Policy, only level 7 fits this bill, which corresponds to “Custom 3 / Antimalware” from our first table. If a policy is present, then all the signature levels that have “Custom” in them unsurprisingly also become customizable, as well as the “Windows Protected Process Light” (13) level.

Once a level is determined to be customizable, the Code Integrity library checks if the signing level matches that of any of the registered runtime signers. If there’s a match, the next step is to authenticate the certificate information chain with the policy specified in the runtime signer registration data. This information can include an array of EKUs, which must be present in order to pass the test, as well as the contents hash of at least one signer, of the appropriate hash length and hashing algorithm.

If all policy elements pass the test, then the requested signature level will be granted, bypassing any other default system EKU or root key checks.

How does the system register such runtime signers? The Code Integrity library contains two API calls, SeRegisterSigningInformation and SeUnregisterSigningInformation through which runtime signers can be registered and deregistered. These calls are made by the kernel by SeRegisterElamCertResources which is done either when an Early-Launch Anti Malware (ELAM) driver has loaded (subject to the rules surrounding obtaining an ELAM certificate), or, more interestingly, at runtime when instructed so by a user-mode caller.

That’s right — it is indeed possible through calling the NtSetSystemInformation API, if using the SystemRegisterElamCertificateInformation information class, to pass the full path to a non-loaded ELAM driver binary. By using SeValidateFileAsImageType, the kernel will call into the Code Integrity library to check if the image is signed, using Scenario 17, which you’ll recall from Table 3 above is the ELAM scenario. If user-mode did not pass in a a valid ELAM driver, the request will simply fail.

Once SeRegisterElamCertResources is called in either of these cases, it calls SepParseElamCertResources on the MICROSOFTELAMCERTIFICATEINFO section in order to parse an MSELAMCERTINFOID resource. Here is, for example, a screenshot of the resource data matching this name in Microsoft’s Windows Defender ELAM driver (Wdboot.sys):

ElamCert

This data is formatted according to the following rules below, which can be used in an .rc file when building your own ELAM driver. The sample data from the Windows Defender ELAM driver is also shown alongside in bold for easier comprehension.

MicrosoftElamCertificateInfo  MSElamCertInfoID
{
    <# of Entries, Max 3>,  -> 1
    L”Content Hash n\0”,    -> f6f717a43ad9abddc8cefdde1c505462535e7d1307e630f9544a2d14fe8bf26e
    <Hash Algorithm n>,     -> 0x800C (CALG_SHA_256)
    L”EKU1n;EKU2n;EKU3n\0”, -> 1.3.6.1.4.1.311.76.8.1;1.3.6.1.4.1.311.76.11.1
    … up to 2 more blocks …
}

This data is then packaged up into the runtime signer blob that is created by CiRegisterSigningInformation API and will be used for comparisons when the signing level matches — note that the kernel always passes in “7” as the signature level for the signer, since the kernel API is explicitly designed for ELAM purposes.

On the other hand, the internal CiRegisterSigningInformation API can be used for arbitrary signing levels, as long as the current policy allows it and the levels are runtime customizable. Also note that the limitation on up to 3 EKUs and 3 Signers is also enforced by the kernel and not by the Code Integrity library.

Running as Anti-Malware Protected Process Light

In the previous posts we explained some of the protections offered to PPLs and the different signers and levels available. In this post, we started by seeing how the presence of EKUs and particular root authority keys causes the system to allow or deny a certain binary from loading with the requested signature level (and thus protection level), as well as to how DLLs can be prohibited to load in such processes unless they too match a minimum signing level.

This should explain why a process like Smss or Csrss is allowed to run with a given protection level, but it didn’t quite explain why MsMpEng.exe or NisSrv.exe were allowed to run as PPLs, because their certificate EKUs, shown below, don’t match any specially handled level:

msmpeng

However, by taking a look at last section on runtime signers, as well as using the CertUtil utility to dump the content hash of the certificate used to sign the Windows Defender binaries you’ll note a distinct match between the information present in the resource section of the driver, and the information in the certificate.  See below for both the signature hash and the EKU presence:

elamhash

Because of the ELAM driver, this specific hash and EKU are registered as a runtime signer, and when the service launches, recall that by using the Protected Service functionality we saw in the previous post, the Windefend service requests a Win32 protection level of 3 — or an NT protection level of 0x31.  In turn, this translates to Signing Level 7 — because this level is runtime customizable, a the runtime signer check is then performed, and the hash and EKU is matched.

As we mentioned above, the SystemRegisterElamCertificateInformation information class can be used to request parsing of an ELAM driver’s resource section in order to register a runtime signer. It turns out that this undocumented information class is exposed through the new InstallELAMCertificateInfo API in Windows 8.1, which any 3rd party can legitimately call in order to tap into this behavior, as long as the driver is ELAM signed.

You don’t actually need to have any code in the ELAM driver, just enough of a valid PE image such that the kernel-mode loader can parse the .rsrc section and recover the MicrosoftElamCertificateInfo resource section.

Furthermore, recall that for runtime signers, all the usual root key and EKU checks are gone, instead relying on the policy that was registered. In other words, the system allows you to function as your own 3rd party CA, and issue certificates with custom content hashes for different signers. Or better yet, it is possible to attach custom EKUs to one’s binaries, in order to separate other binaries your organization may be signing.

Analysis

We have covered the details of these new cryptographic features in great detail.  Now I’d like to point out a few observations about the shortcomings and potential issues inherent to these new features.

As great and extensible as the new PPL system (and its accompanying PKI infrastructure) is, it is not without its own risks. For one thing, any company with an ELAM certificate can now create buggy user-mode processes (remember folks, these are AV companies we’re talking about…) that not only you can’t debug, but you also can’t terminate from user-mode. Although yes, on platforms without SecureBoot, this would be possible by simply using a kernel debugger or custom drivers, imagine less tech-savvy users stuck without being able to use Task Manager.

Additionally, a great deal of reliance seems to have been put on EKUs, which were relatively unknown in the past and mostly only used to define a certificate as being for “SSL” vs “Code Signing”. One can only hope that the major CAs are smart enough to have filters in place to avoid arbitrary EKUs being associated with 3rd party Authenticode certificates. Otherwise, as long as a signature level accepts a non-PRS root key, the infrastructure could easilyy be fooled by an EKU that a CA has allowed into a certificate.

Finally, as with all PKI implementations, this one is not without its own share of bugs. I have independently discovered means to bypass some of the guarantees being made around PPLs, and to illegitimately create an Antimalware process, as I posted in this picture.  I obviously don’t have an ELAM certificate (and the system is not in test-signing mode), so this is potentially a problem. I’ve reported the issue to Microsoft and am waiting more information/feedback before talking about this issue further, in case it is a legitimate bug that needs to be fixed.

Conclusion and Future Work

In this final post on protected processes, we delved deeply into the PKI that is located within the Code Integrity library in Windows 8.1, and we saw how it provides cryptographic boundaries around protected processes, PPLs, and signature levels reserved for particular usages.

At the same time, we talked about how custom signing policies, delivered through Secure Boot, can customize this functionality, and saw up to 6 “Custom” signing levels that can be defined through such a policy. Finally, we looked at how some of these signing levels, namely the Antimalware level by default, can be extended through runtime signers that can be registered either pre- or post-boot through special resource sections in ELAM drivers, thus leading to custom 3rd party PPLs.

In the near future, I intend to contribute patches to Process Hacker in order to add a new column to the process tree view which would show the process protection level in its native NT form, as this data is available through the NtQueryInformationProcess API call in Windows 8.1. The tooltip for this data would then show the underlying Signer and Level, based on the kernel headers I pasted in the earlier blog posts.

Last but not least, the term “Secure Boot Signing Policy” appears numerous times without a full explanation as to what this is, how to register one, and what policies such a construct can contain. It only seems fair to dedicate the next post to this topic – stay tuned!

Credits

The contents of this blog series could not have been made possible without the help and contributions of:

  • lilhoser
  • myriachan
  • msuiche

The Evolution of Protected Processes Part 2: Exploit/Jailbreak Mitigations, Unkillable Processes and Protected Services

Introduction

In this continuing series on the improvements of the protected process mechanism in Windows, we’ll move on past the single use case of LSASS protection and pass-the-hash mitigation through the Protected Process Light (PPL) feature, and into generalized system-wide use cases for PPLs.

In this part, we’ll see how Windows uses PPLs to guard critical system processes against modification and how this has prevented the Windows 8 RT jailbreak from working on 8.1. We’ll also take a look at how services can now be configured to run as a PPL (including service hosts), and how the PPL concept brings yet another twist to the unkillable process argument and semantics.

System Protected Processes

To start the analysis, let’s begin with a simple WinDBG script (you should collapse it into one line) to dump the current PID, name, and protection level of all running processes:

lkd> !for_each_process "
r? @$t0 = (nt!_EPROCESS*) @#Process;
.if @@(@$t0->Protection.Level) 
{
.printf /D \"%08x <b>[%70msu]</b> level: <b>%02x</b>\\n\",
    @@(@$t0->UniqueProcessId),
    @@(&@$t0->SeAuditProcessCreationInfo.ImageFileName->Name),
    @@(@$t0->Protection.Level)
}
"

The output on my rather clean Windows 8.1 32-bit VM, with LSA protection enabled as per the last post, looks something like below. I’ve added the actual string representation of the protection level for clarity:

script2

As a reminder, the protection level is a bit mask composed of the Protected Signer and the Protection Type:

PS_PROTECTED_SIGNER
PsProtectedSignerNone = 0n0
PsProtectedSignerAuthenticode = 0n1
PsProtectedSignerCodeGen = 0n2
PsProtectedSignerAntimalware = 0n3
PsProtectedSignerLsa = 0n4
PsProtectedSignerWindows = 0n5
PsProtectedSignerWinTcb = 0n6
PsProtectedSignerMax = 0n7
PS_PROTECTED_TYPE
PsProtectedTypeNone = 0n0
PsProtectedTypeProtectedLight = 0n1
PsProtectedTypeProtected = 0n2
PsProtectedTypeMax = 0n3

This output shows that the System process (the unnamed process), as has been the case since Vista, continues to be a full-fledged protected process, alongside the Software Piracy Protection Service (Sppsvc.exe).

The System process is protected because of its involvement in Digitial Rights Management (DRM) and because it might contain sensitive handles and user-mode data that a local Administrator could have accessed in previous versions of Windows (such as XP). It stands to reason that Sppsvc.exe is protected due to similar DRM-like reasons, and we’ll shortly see how the Service Control Manager (SCM) knew to launch it with the right protection level.

The last protected process we see is Audiodg.exe, which also heralds from the Vista days. Note that because Audiodg.exe can load non-Windows, 3rd party “System Audio Processing Objects” (sAPOs), it only uses the Authenticode Signer, allowing it to load the DLLs associated with the various sAPOs.

We also see a number of “WinTcb” PPLs – TCB here referring to “Trusted Computing Base”. For those familiar with Windows security and tokens, this is not unlike the SeTcbPrivilege (Act as part of the Operating System) that certain highly privileged tokens can have. We can think of these processes as essentially the user-mode root chain of trust provided by Windows 8.1. We’ve already seen that SMSS is responsible for launching LSASS with the right protection level, so it would make sense to also protect the creator. Very shortly, we’ll revisit what actual “protection” is really provided by the different levels.

Finally, we see the protected LSASS process as expected, followed by two “Antimalware” PPLs – the topic of which will be the only focus of Part 3 of this series – and one “Windows” PPL associated with a service host. Just like the SPP service, we’ll cover this one in the “Protected Services” section below.

Jailbreak and Exploit Mitigation

Note that it’s interesting that Csrss.exe was blessed with a protection level as well. It isn’t responsible for launching any special protected processes and doesn’t have any interesting data in memory like LSASS or the System process do. It has, however, gained a very nefarious reputation in recent years as being the source of multiple Windows exploits – many of which actually require running inside its confines for the exploit to function. This is due to the fact that a number of highly privileged specialized APIs exist in Win32k.sys and are meant only to be called by Csrss (as well as the fact that on 32-bit, Csrss has the NULL page mapped, and it also handles much of VDM support).

Because the Win32k.sys developers did not expect local code injection attacks to be an issue (they require Administrator rights, after all), many of these APIs didn’t even have SEH, or had other assumptions and bugs. Perhaps most famously, one of these, discovered by j00ru, and still unpatched, has been used as the sole basis of the Windows 8 RT jailbreak. In Windows 8.1 RT, this jailbreak is “fixed”, by virtue that code can no longer be injected into Csrss.exe for the attack. Similar Win32k.sys exploits that relied on Csrss.exe are also mitigated in this fashion.

Protected Access Rights

Six years ago in my Vista-focused protected process post, I enumerated the documented access rights which were not being granted to protected processes. In Windows 8.1, this list has changed to a dynamic table of elements of the type below:

RTL_PROTECTED_ACCESS
+0x000 DominateMask        : Uint4B
+0x004 DeniedProcessAccess : Uint4B
+0x008 DeniedThreadAccess  : Uint4B
 
PAGE:821AD398 ; _RTL_PROTECTED_ACCESS RtlProtectedAccess[]
PAGE:821AD398 <0,   0, 0>                [None]
PAGE:821AD398 <2,   0FC7FEh, 0FE3FDh>    [Authenticode]
PAGE:821AD398 <4,   0FC7FEh, 0FE3FDh>    [CodeGen]
PAGE:821AD398 <8,   0FC7FFh*, 0FE3FFh*>  [Antimalware]
PAGE:821AD398 <10h, 0FC7FFh*, 0FE3FFh*>  [Lsa]
PAGE:821AD398 <3Eh, 0FC7FEh, 0FE3FDh>    [Windows]
PAGE:821AD398 <7Eh, 0FC7FFh*, 0FE3FFh*>  [WinTcb]

Access to protected processes (and their threads) is gated by the PspProcessOpen (for process opens) and PspThreadOpen (for thread opens) object manager callback routines, which perform two checks.

The first, done by calling PspCheckForInvalidAccessByProtection (which in turn calls RtlTestProtectedAccess and RtlValidProtectionLevel), uses the DominateMask field in the structure above to determine if the caller should be subjected to access restrictions (based on the caller’s protection type and protected signer). If the check fails, a second check is performed by comparing the desired access mask with either the “DeniedProcessAccess” or “DeniedThreadAccess” field in the RtlProtectedAccess table. As in the last post, clicking on any of the function names will reveal their implementation in C.

Based on the denied access rights above, we can see that when the source process does not “dominate” the target protected process, only the 0x3801 (~0xFC7FE) access mask is allowed, corresponding to PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_SUSPEND_RESUME, PROCESS_TERMINATE, and PROCESS_SET_LIMITED_INFORMATION (the latter of which is a new Windows 8.1 addition).

On the thread side, THREAD_SET_LIMITED_INFORMATION, THREAD_QUERY_LIMITED_INFORMATION, THREAD_SUSPEND_RESUME, and THREAD_RESUME are the rights normally given, the latter being another new Windows 8.1 access bit.

Pay attention to the output above, however, and you’ll note that, this is not always the case!

Unkillable Processes

In fact, processes with a Protected Signer that belongs to either Antimalware, Lsa, or WinTcb only grant 0x3800 (~0xFC7FF) – in other words prohibiting the PROCESS_TERMINATE right. And for the same group that prohibits PROCESS_TERMINATE, we can also see that THREAD_SUSPEND_RESUME is also prohibited.

This is now Microsoft’s 4th system mechanism that attempts to prevent critical system process termination. If you’ll recall, Windows Server 2003 introduced the concept of “critical processes”, which Task Manager would refuse to kill (and cause a bugcheck if killed with other tools), while Windows 2000 had introduced hard-coded paths in Task Manager to prevent their termination.

Both of these approaches had flaws: malware on Windows 2000 would often call itself “Csrss.exe” to avoid user-initiated termination, while calling RtlSetProcessIsCritical on Vista allowed malware to crash the machine when killed by AV (and also prevent user-initiated termination through Task Manager). Oh, and LSASS was never a critical process – but if you killed it, SMSS would notice and take down the machine. Meanwhile, AV companies were left at the mercy of process-killing malware, until Vista SP1 added object manager filtering, which allowed removing the PROCESS_TERMINATE right that could be granted to a handle.

It would seem like preventing PROCESS_TERMINATE to LSASS, TCB processes, and anti-virus processes is probably the mechanism that makes the most sense – unlike all other approaches which relied on obfuscated API calls or hard-coded paths, the process protection level is a cryptographic approach that cannot be faked (barring a CA/PKI failure).

Launching Protected Services

As SMSS is created by the System process, and it, in turn, creates LSASS, the SCM, and CSRSS, it makes sense for all of these processes to inherit some sort of protection level based on the implicit process creation logic in each of them. But how did my machine know to launch the SPP service protected? And why did I have one lone PPL service host? It turns out that in Windows 8.1, the Service Control Manager now has the capability of supporting services that need to run with a specific protection level, as well as performing similar work as the kernel when it comes to defending against access to them.

In Windows 8.1, when the SCM reads the configuration for each service, it eventually calls ScReadLaunchProtected which reads the “LaunchProtected” value in the service key. As you can see below, my “AppXSvc” service, for example, has this set to the value “2”.

registry

You’ll see the “sppsvc” service with this value set to “1”, and you’ll see “Windefend” and “WdNisSvc” at “3”. All of these match the new definitions in the Winsvc.h header:

1
2
3
4
5
6
7
//
// Service LaunchProtected types supported
//
#define SERVICE_LAUNCH_PROTECTED_NONE                    0
#define SERVICE_LAUNCH_PROTECTED_WINDOWS                 1
#define SERVICE_LAUNCH_PROTECTED_WINDOWS_LIGHT           2
#define SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT       3

The SCM saves the value in the SERVICE_RECORD structure that is filled out by ScAddConfigInfoServiceRecord, and when the service is finally started by ScLogonAndStartImage, it is converted to a protection level by using the g_ScProtectionMap array of tagScProtectionMap structures. WINDOWS becomes 0x52, WINDOWS_LIGHT  becomes 0x51, and ANTIMALWARE_LIGHT becomes 0x31 – the same values shown at the very beginning of the post.

tagScProtectionMap
+0x000 ScmProtectionLevel   : Uint4B
+0x004 Win32ProtectionLevel : Uint4B
+0x008 NtProtectionLevel    : Uint4B
 
.data:00441988 ; tagScProtectionMap g_ScProtectionMap[]
.data:00441988 <0, 0, 0>    [None]
.data:00441988 <1, 1, 52h>  [Windows Protected]
.data:00441988 <2, 2, 51h>  [Windows Light]
.data:00441988 <3, 3, 31h>  [Antimalware Light]

This now explains why NisSrv.exe (WdNisSvc), MsMpEng.exe (Windefend) were running as “Antimalware”, a Protected Signer we haven’t talked about so far, but which will be the sole focus of Part 3 of this series.

In addition, the command-line Sc.exe utility has also been updated, with a new argument “qprotection”, as seen in the screenshot below:

qprotection

Protected SCM Operations

When analyzing the security around protected services, an interesting conundrum arises: when modifying a service in any way, or even killing it, applications don’t typically act on the process itself, but rather communicate by using the SCM API, such as by using ControlService or StopService. In turn, responding to these remote commands, the SCM itself acts on its subjugate services.

Because the SCM runs with the “WinTcb” Protected Signer, it “dominates” all other protected processes (as we saw in RtlTestProtectedAccess), and the access checks would be bypassed. In other words, a user with only SCM privileges would use the APIs to affect the services, even if they were running with a protection level. However, this is not the case, as you can see in my attempt below to pause the AppX service, to change its configuration, and to stop it – only the latter was successful.

appxsvc

This protection is afforded by new behavior in the Service Control Manager that guards the RDeleteService, RChangeServiceConfigW, RChangeServiceConfig2W, RSetServiceObjectSecurity, and RControlService remote function calls (RPC server stubs). All of these stubs ultimately call ScCheckServiceProtectedProcess which performs the equivalent of the PspProcessOpen access check we saw the kernel do.

As you can see in the C representation of ScCheckServiceProtectedProcess that I’ve linked to, the SCM will gate access to protected services to anyone but the TrustedInstaller service SID. Other callers will get their protection level queried, and be subjected to the same RtlTestProtectedAccess API we saw earlier. Only callers that dominate the service’s protection level will be allowed to perform the corresponding SCM APIs – with the interesting exception around the handling of the SERVICE_CONTROL_STOP opcode in the RControlService case.

As the code shows, this opcode is allowed for Windows and Windows Light services, but not for Antimalware Light services – mimicking, in a way, the protection that the kernel affords to such processes. Here’s a screenshot of my attempt to stop Windows Defender:

windefend

Conclusion

In this post, we’ve seen how PPL’s usefulness extend beyond merely protecting LSASS against injection and credential theft.  The protected process mechanism in Windows 8.1 also takes on a number of other roles, such as guarding other key processes against modification or termination, preventing the Windows RT jailbreak, and ultimately obsoleting the “critical process” flag introduced in older Windows versions (as a side effect, it is no longer possible to kill Smss.exe with Task Manager in order to crash a machine!). We’ve also seen how the Service Control Manager also has knowledge of protected processes and allows “protected services” to run, guarding access to them just as the kernel would.

Finally, and perhaps most interestingly to some readers, we’ve also seen how Microsoft is able to protect its antivirus solution (Windows Defender) with the protected process functionality as well, including even preventing the termination of its process and/or the stopping of its service. Following the EU lawsuits and DOJ-settlement, it was obviously impossible for Microsoft to withhold this capability from 3rd parties.

In the next post in this series, we’ll focus exclusively on how a developer can write an Antimalware PPL application, launch it, and receive the same level of protection as Windows Defender.  The post will also explore mechanisms that exist (if any) to prevent such a developer from doing so for malicious purposes.

The Evolution of Protected Processes Part 1: Pass-the-Hash Mitigations in Windows 8.1

Introduction

It was more than six years ago that I first posted on the concept of protected processes, making my opinion of this poorly thought-out DRM scheme clear in the title alone: “Why Protected Processes Are A Bad Idea”. It appears that Microsoft took a long, hard look at the mechanism (granted, an impenetrable user-mode process can have interesting security benefits — if we can get DRM out of the picture), creating a new class of process yet again: the Protected Process Light, sometimes abbreviated PPL in the kernel.

Unlike its “heavy” brother, the protected process light actually serves as a type of security boundary, bringing in three useful mitigations and security enhancements to the Windows platform. Over the next three or four blog posts, we’ll see how each of these enhancements is implemented, starting this week with Pass-the-Hash (PTH) Mitigation.

We’ll talk about LSASS’ role in the Windows security model, followed by the technical details behind the new PPL model. And since it’s hard to cover any new security advancement without delving in at least a few other inter-related internals areas, we’ll also talk a little bit about Secure Boot and protected variables. Perhaps most importantly, we’ll also see how to actually enable the PtH mitigation, as it is currently disabled by default on non-RT Windows versions.

The LSASS Process

In Windows, local user accounts are hashed using a well-known algorithm (NTLM) and stored in a database called the SAM (Security Accounts Manager), which is in itself a registry hive file. Just like with other operating systems, a variety of offline, and online attacks exist in order to obtain, reset, or otherwise reuse the hashes that are stored in the SAM, going from the usual “Password Reset” boot emergency disks, to malicious privilege escalation. Additionally, a variety of other cryptographic data is also stored in the SECURITY database, yet another registry hive file. This data includes information such as secrets, saved plain-text passwords, and more.

A process called the Local Security Authority (LSASS) manages the run-time state of this information, and is ultimately responsible for all logon operations (including remote logon over Active Directory). Therefore, in order to obtain access to this data, two primary mechanisms are used:

1) File-based attacks: the SAM/SECURITY hives are accessed, either offline, or online through tricks such as using Volume Shadow Copies, and the hashes + secrets extracted. This mechanism has disadvantages in that the storage formats can change, detailed registry knowledge is needed, and LSASS will often obfuscate much of the data (such as plain-text cached passwords).

2) Process-based attacks: since the hash and secret data from #1 above is neatly loaded by LSASS in readable form (and accessible thanks to easy-to-use query APIs), it is often much more preferable to simply inject code into the LSASS process itself, which is then used to dump hashes or secrets, as well as to create tokens based on those hashes. Additionally, researchers such as Gentil Kiwi have even discovered that LSASS contains plain-text passwords using reversible symmetric cryptography (with the key stored in the LSASS process itself). Tools now exist today to not only pass-the-hash, but to also pass-the-pass. In a default Windows 8 installation, both the local user account password, as well as the Microsoft Live Services password, is available in a plaintext-retrievable way.

Obviously, both this file and the process are protected such that only the SYSTEM account can access them. But once running as Administrator, this is a simple hurdle — and since most users still run as Administrators (albeit with UAC, but that’s not a security boundary), exploits only have to escape whatever local sandbox they’re running in, get admin rights, get a system token, and inject into LSASS. And of course, in a shared computer environment, another admin on the machine can get the passwords of all the users.

What’s changed in Windows 8.1? Run Mimikatz or other pass-the-hash attacks and they still work out-of-the-box. But on a Windows 8.1 RT system (supposing one can compile for ARM), they won’t — in fact, even attempting to attach a debugger to the LSASS process will fail, regardless of user-mode permissions.

The title of this blog post gives it away: in Windows 8.1 RT, LSASS is now a protected process light. And with Registry Editor and the right key/value pair, your Windows 8.1 installation (non-RT) can take advantage of this too.

Protected Process Light Internals

Before taking a look at how to enable the mitigation, let’s see what makes a PPL tick. Unlike the simple “ProtectedProcess” bit in EPROCESS that I documented in Vista, a Windows 8.1 EPROCESS structure now has a “Protection” field of the following type:

1
2
3
4
5
_PS_PROTECTION
  +0x000 Level            : UChar
  +0x000 Type             : Pos 0, 3 Bits
  +0x000 Audit            : Pos 3, 1 Bit
  +0x000 Signer           : Pos 4, 4 Bits

Where type can be one of the following:

1
2
3
4
5
_PS_PROTECTED_TYPE
  PsProtectedTypeNone = 0n0
  PsProtectedTypeProtectedLight = 0n1
  PsProtectedTypeProtected = 0n2
  PsProtectedTypeMax = 0n3

and Signer can be one of these (excited about some of these other values? future blog posts will uncover more on signers and PPLs):

1
2
3
4
5
6
7
8
9
_PS_PROTECTED_SIGNER
  PsProtectedSignerNone = 0n0
  PsProtectedSignerAuthenticode = 0n1
  PsProtectedSignerCodeGen = 0n2
  PsProtectedSignerAntimalware = 0n3
  PsProtectedSignerLsa = 0n4
  PsProtectedSignerWindows = 0n5
  PsProtectedSignerWinTcb = 0n6
  PsProtectedSignerMax = 0n7

Let’s do some quick math and see if the LSASS process on my hardened Windows 8.1 system matches:

lkd> !process 0 0 lsass.exe
PROCESS ffffe000049ab900
lkd> ?? ((nt!_EPROCESS*)0xffffe000049ab900)->Protection.Level
unsigned char 0x41'

Because the bits are essentially nibbles, it’s easy to read 0x41 as Lsa (0x4) + PPL (0x1).

Once a process is in the PPL state, all the protections in my previous blog post are in effect — the system protects both types of protected processes in the same way, preventing any handle open for all but a few limited rights. Additionally, the memory manager will prevent loading of DLLs that are not signed appropriately, using the Code Integrity improvements in Windows 8 that I covered in my talk at BreakPoint last year — something I plan to revisit in this blog at a later time.

Finally, although I didn’t mention this back in the Vista days, the application compatibility database is also disabled for these processes — an interesting attack vector that is blocked thanks to this.

Enabling the Pass-the-Hash Mitigation

Now that we know about this improvement to the security architecture, how can one take advantage of it on a non-RT Windows 8.1 computer ? By looking at the updated flow of Wininit.exe, the process in charge for launching LSASS, one can see that the ExecSystemProcesses routine now calls GetLsaProtectionLevel which does a registry key read of HKLM\SYSTEM\CurrentControlSet\Control\Lsa for the value RunAsPPL. Before reading the registry however, it also calls ReadLsaConfigEnvironmentVariable — the importance of which we’ll see in a bit.

Either way, as long as one of these two things is set (the environment variable or the registry key), ExecSystemProcesses will call StartSystemProcess with the CREATE_PROTECTED_PROCESS flag. In turn, the routine will utilize the new Vista Process/Thread Attribute List functionality to add attribute 0x2000B — documented as the new Windows 8.1 “Protection Level Attribute“. As you can expect, the level is set to 4, which matches the “LSA Signer” enumeration value above. And just like that, LSASS is now a PPL, and protected against even an admin (or even SYSTEM) attacker. And no, not even SE_DEBUG_PRIVILEGE will get you through. Clicking on any of the linked function names will reveal Hex-Rays output to match this flow.

As a side note, is this all you need to launch as process as protected light — a protection level in a new attribute? Astute readers have probably already dumped the EPROCESS for Wininit.exe by now and noticed that, it too, is a PPL process (albeit, with a different Level!). The security model isn’t stupid — a PPL can only be launched by another PPL (or a PP, which is even more protected), and there’s a hierarchy in the levels as well, which we’ll see in a later post. Obviously, this means that Smss.exe (Wininit’s parent) must also be a PPL, and evidently the kernel has been running as a Protected Process since Vista. You could call this a user-mode protected chain of trust. These processes aren’t the only PPLs — we’ll see a lot more in a future post, and their purpose and configurability.

Should you run off and set that registry key? Yes and no. Once LSASS runs as a PPL, this will break any 3rd party software that might be attempting to inject or modify LSASS state. And sadly, at work, I’ve seen a number of these. Additionally, LSASS has a number of extensibility points, some used as ASEPs by attackers, others used legitimately to provide enhanced security or cryptographic services. Without the right signature and EKU (which right now means a WHQL signature with Microsoft as the signer — not just any Authenticode garbage!), those DLLs, plugins, and extensions will stop working. In certain IT scenarios, this can be a catastrophic compatibility problem, no doubt why Microsoft has chosen to keep this disabled for now.

But on a home computer, where you know you don’t have specialized software, and you firmly believe that AV (and others) should leave your LSASS alone? I’d say go for it. A number of helpful event log entries in the Security log will warn you of any DLLs that failed to load in case you’re curious.

Enhanced LSASS Mitigation With Secure Boot

Leaving the endless debate and controversy around Secure Boot aside, running Windows 8.1 on an UEFI-compatible machine with Secure Boot turned on will add an additional layer of security. Set the registry key as indicated above, reboot, watch LSASS run as a PPL, and now try deleting the registry key — then reboot again. LSASS will still run as a PPL. In fact, you can even re-install Windows 8.1, and LSASS will still run as a PPL. This is because Microsoft realized — if the attacker runs as Admin/SYSTEM and can inject into LSASS, but a registry key prevents this — why wouldn’t the Admin/SYSTEM attacker simply delete the key? Outside of active-key-monitoring shenanigans (which some parts of the kernel do employ, mostly licensing), not much. And definitely an offline attacker will have no problem editing the hive directly (unless BitLocker is also active).

This changes with Secure Boot however, as Windows has the ability to use the standard UEFI system variable runtime routines and set a value directly in the firmware store using SetFirmwareEnvironmentVariableEx API (and its kernel equivalents such as the NtSetSystemEnvironmentVariableEx and ExSetFirmwareEnvironmentVariable routines). To be fair, this is standard UEFI behavior; what Secure Boot brings to the table is the Namespace GUID that Windows can use — which if you were paying attention you saw in the ReadLsaConfigEnviromentVariable snippet earlier.

This GUID, {77FA9ABD-0359-4D32-60BD-28F4E78F784B}, is the “Protected Store” that Windows can use to store certain system properties it wants to protect. In this case, it stores a variable named Kernel_Lsa_Ppl_Config that is associated with the RunAsPPL value in the registry (to be 100% accurate, “it” here refers to Winload.efi, which upon loading the registry executes the OslFwProtectSecConfigVars routine) . As soon as this variable is set, the registry values no longer matter — PPL is enabled for LSASS.

What prevents a user from simply deleting this variable, or setting it to zero? Witness the following snippet in the NtSetSystemEnvironmentVariableEx system call, which executes for user-mode callers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
i = 0;
while (VendorGuid[i] == ExpSecureBootVendorGuid[i])
{
    ++i;
    if (i == 4)
    {
        if (!_wcsnicmp(CapturedVarName, L"Kernel_", 7))
        {
            ExFreePoolWithTag(CapturedVarName, 0);
            return STATUS_ACCESS_DENIED;
        }
        break;
    }
}

The intent is clear — any variables stored in the Secure Boot GUID, that start with Kernel_, are inaccessible from userspace — meaning that no Windows application can attempt to reset the protection. In fact, the only way to reset the protection is to boot into a special UEFI application written by Microsoft, which will wipe the environment variable based on the user’s input. An impressive security boundary, to say the least.

Conclusion

At the end of the day, what does running as PPL really mean for your system? Based on the limited access rights that protected processes (and PPLs) provide, a process, regardless of its token, can no longer open a handle for injection and/or modification permissions toward the LSASS process. Since this is critical for injecting the DLLs and/or threads that process-based PtH tools use, their use is thwarted. Additionally, attempts to load DLLs into LSASS through other means (such as AppInit_DLLs or LSA extensions) are also blocked, since the required digital signatures are missing. It’s important to mention that file-based hash attacks are not affected by these enhancements — at the end of the day, if someone has local console access to your unlocked, non-encrypted machine, it’s not your machine anymore.

With Windows 8.1, Protected Processes have evolved — taking on additional capabilities and now working to enhance security and protect users, instead of doing the bidding of the MPAA. One such new capability is the Pass-the-Hash mitigation and general hardening of the LSASS process — but there are a lot more. It’s one of the first of many general security and cryptographic  enhancements in Windows 8.1 which provide additional boundaries around Microsoft’s code — separating it from other people’s code. But just like Apple’s entitlement system, it’s not a fully walled garden. Further posts will explore not only additional uses of PPLs by Windows’ own binaries, but also (supported) options available for 3rd parties.

 

KASLR Bypass Mitigations in Windows 8.1

Introduction

As some of you may know, back in June of 2013, I gave a talk at Recon, a security conference in Montreal, about KASLR Information Bypasses/Leaks in the Windows NT kernel, entitled “I got 99 problems but a kernel pointer ain’t one”. The point of the presentation was both to collect and catalog the many ways in which kernel pointers could be leaked to a local userspace attacker (some of which were known, others not so much), as well as raise awareness to the inadequate protection, and sometimes baffling leaking of, such data.

After sharing my slides and presentation with some colleagues from Microsoft, I was told to “expect some changes in Windows 8.1”. I was initially skeptical, because it seemed that local KASLR bypasses were not at the top of the security team’s list — having been left behind to accumulate for years (a much different state than Apple’s OS X kernel, which tries to take a very strong stance against leaking pointers). As Spender likes to point out, there will always be KASLR bugs. But in Windows, there were documented APIs to serve them on a platter for you.

Restricted Callers

Our investigation begins with an aptly named new Windows 8.1 kernel function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
BOOLEAN
ExIsRestrictedCaller (
    _In_ KPROCESSOR_MODE PreviousMode
    )
{
    PTOKEN Token;
    NTSTATUS Status;
    BOOLEAN IsRestricted;
    ULONG IntegrityLevel;
    PAGED_CODE();
 
    //
    // Kernel callers are never restricted
    //
    if (PreviousMode == KernelMode)
    {
        return FALSE;
    }
 
    //
    // Grab the primary token of the current process
    //
    Token = PsReferencePrimaryToken(PsGetCurrentProcess());
    NT_ASSERT(Token != NULL);
 
    //
    // Get its integrity level
    //
    Status = SeQueryInformationToken(Token,
                                     TokenIntegrityLevel,
                                     &IntegrityLevel);
    ObDereferenceObject(Token);
 
    //
    // If the integrity level is below medium, or cannot be
    // queried, the caller is restricted.
    //
    if (!NT_SUCCESS(Status) ||
        IntegrityLevel < SECURITY_MANDATORY_MEDIUM_RID)
    {
        IsRestricted = TRUE;
    }
    else
    {
        IsRestricted = FALSE;
    }
 
    //
    // Return the caller's restriction state
    //
    return IsRestricted;
}

This now introduces a new security term in the Windows kernel lingo — a “restricted caller”, is a caller whose integrity level is below Medium. For those unfamiliar with the concept of integrity levels, this includes most applications running in a sandbox, such as Protected Mode IE, Chrome, Adobe Reader and parts of Office. Additionally, in Windows 8 and higher, it includes all Modern/Metro/TIFKAM/MoSH/Immersive/Store applications.

So, what is it exactly that these restricted callers cannot do?

System-wide Information Mitigations

First of all, STATUS_ACCESS_DENIED is now returned when calling NtQuerySystemInformation, with the following classes:

SystemModuleInformation — Part of my (and many others) presentation, this disables the EnumSystemDrivers API and hides the load address of kernel drivers (finally!).

SystemModuleInformationEx — A new information class that was recently added in Vista and leaked as much as the one above.

SystemLocksInformation — Part of my presentation (and also found by j00ru), this leaked the address of ERESOURCE locks in the system.

SystemStackTraceInformation — Indirectly mentioned in the ETW/Performance section of my presentation, this leaked kernel stack addresses, but only if the right global flags were set.

SystemHandleInformation — Part of my presentation, and well known beforehand, this was NT’s KASLR-fail posterboy: leaking the kernel address of every object on the system that had at least one handle open (i.e.: pretty much all of them).

SystemExtendedHandleInformation — Another new Vista information class, which was added for 64-bit support, and leaked as much as above.

SystemObjectInformation — Part of my presentation, if the right global flags were set, this dumped the address of object types and objects on the system, even if no handles were open.

SystemBigPoolInformation — Part of my presentation, this dumped the address of all pool (kernel heap) allocations over 4KB (so-called “big” allocations).

SystemSessionBigPoolInformation — The session-specific little brother of the above, perfect for those win32k.sys exploits.

Thread Information Mitigations

But that’s not all! Using the well-known SystemProcessInformation information class, which famously dumps the entrypoint addresses of system threads (pretty much giving you a function pointer into almost all loaded drivers), as well as the kernel stack base and stack limit of all the threads on the system (used by j00ru in his GS-stack-cookie-guessing attacks, since the cookie is partly generated with this information), now introduces some additional checks.

First of all, there are now three information classes related to this data.

SystemProcessInformation, which is well-understood.

SystemExtendedProcessinformation, which was documented by j00ru and wj32. This returns the SYSTEM_EXTENDED_THREAD_ INFORMATION structure containing the stack base, limit, and Win32 start address.

SystemFullProcessInformation, which is new to Windows 8.1. This returns the SYSTEM_PROCESS_INFORMATION_EXTENSION below:

1
2
3
4
5
6
+0x000 DiskCounters : _PROCESS_DISK_COUNTERS (the new Windows 8 I/O counters at the disk level, copied from EPROCESS)
+0x028 ContextSwitches : Uint8B (Copied from KPROCESS)
+0x030 Flags : Uint4B (See below)
+0x030 HasStrongId : Pos 0, 1 Bit (in other words, strongly named -- AppContainer)
+0x030 Spare : Pos 1, 31 Bits (unused)
+0x034 UserSidOffset : Uint4B (The offset, hardcoded to 0x38, of the primary user SID)

(By the way, I hear Microsoft is taking suggestions on the upcoming 4th information class in Windows 9. Current leader is SystemFullExtendedProcessInformation.)

It’s unfortunate that Microsoft continues to keep these APIs undocumented — the documented Win32 equivalents require up to 12 separate API calls, all of which return the same data 12 times, with the Win32 interface only picking one or two fields each time.

Back to our discussion about KASLR, the behavior of this information class is to also apply the restricted caller check. If the caller is restricted, then the stack limit, stack base, start address, and Win32 start address fields in the thread structures will all be zeroed out. Additionally, to use the new “full” information class, the caller must be part of the Administrators group, or have the Diagnostic Policy Service SID in its token. Interestingly, in these cases the restricted caller check is not done — which makes sense after all, as a Service or Admin process should not be running below medium integrity.

Process Information Mitigations

The checks for restricted callers do not stop here however. A few more interesting cases are protected, such as in NtQueryInformationProcess, in which ProcessHandleTracing is disabled for such callers. I must admit this is something I missed in my KASLR analysis (and no obvious hits appear on Google) — this is an Object Manager feature (ironically, one which I often use) related to !obtrace and global flags, which enables seeing a full stack trace and reference count analysis of every object that a process accesses. Obviously, enabling this feature on one own’s process would leak the kernel pointers of all objects, as well as stack traces of kernel code and drivers that are in the path of the access (or running in the context of the process and performing some object access, such as during an IRP).

Another obvious “d’oh!” moment was when seeing the check performed when setting up a Profile Object. Profile Objects are a little-talked about feature of NT, which primarily power the “kernrate” utility that is now rather deprecated (but still useful for analyzing drivers that are not ETW-friendly). This feature allows the caller to setup “buckets” — regions of memory — in which every time the processor is caught with its instruction pointer/program counter cause a trace record to be recorded. In a way similar to some of the cache/TLB prediction attacks shown recently, in which the processor’s trace buffer is queried for address hits, the same could be setup using an NT profile object, which would reveal kernel addresses. In Windows 8.1, attempts to setup buckets above the userspace barrier will result in failure if the caller is restricted.

Last but not least, the ProcessWorkingSetWatch and ProcessWorkingSetWatchEx classes of NtQueryInformationProcess are also now protected. I didn’t talk about these two at Recon, and again I’m not aware of any other public research on these, but they’ve always been my favorite — especially because PSAPI, documented on MSDN, exposes Win32 friendly versions of these (see GetWsChanges). Basically, once you’ve turned WS Watch on your process, you are given the address of every hard fault, as well as the instruction pointer/program counter at the time of the fault — making it a great way to extract both kernel data and code addresses. Instead of going through the trouble of pruning kernel accesses from the working set watch log, the interface is now simply completely disabled for restricted callers.

Conclusion

Well, there you have it folks! Although a number of undocumented interfaces and mechanisms still exist to query protected KASLR pointers, the attack surface has been greatly decreased — eliminating almost all non-privileged API calls, requiring at least Medium IL to use them (thus barring any Windows Store Apps from using them). This was great work done by the kernel security team at Microsoft, and continues to showcase the new lengths at which Windows is willing to go to maintain a heightened security posture. It’s only one of the many other exciting security changes in Windows 8.1

New Security Assertions in “Windows 8”

Anyone reversing “Windows 8” will now find a non-familiar piece of code, whenever a list insertion operation is performed on a LIST_ENTRY:

1
2
3
4
5
6
7
8
9
10
11
12
.text:00401B65                 mov     edx, [eax]
.text:00401B67                 mov     ecx, [eax+4]
.text:00401B6A                 cmp     [edx+4], eax
.text:00401B6D                 jnz     SecurityAssertion
.text:00401B73                 cmp     [ecx], eax
.text:00401B75                 jnz     SecurityAssertion
....
.text:00401C55 SecurityAssertion:               
.text:00401C55
.text:00401C55                 push    3
.text:00401C57                 pop     ecx
.text:00401C58                 int     29h

Or, seen from Hex-Rays:

1
2
3
4
5
if ( ListEntry->Flink->Blink != ListEntry ||
     Blink->Flink != ListEntry )
{
  __asm { int     29h   } // Note that the "push 3" is lost
}

Dumping the IDT reveals just what exactly “INT 29h” is:

lkd> !idt 29

Dumping IDT:

29: 80d5409c nt!_KiRaiseSecurityCheckFailure

Which would indicate that Win8 now has a new kind of “ASSERT” statement that is present in retail builds, designed for checking again certain common security issues, such as corrupted/dangling list pointers.

Thankfully, Microsoft was nice enough to document where this is coming from, and I’ve even been told they want to encourage its use externally. Starting in “Windows 8”, if you leave NO_KERNEL_LIST_ENTRY_CHECKS undefined, the new LIST_ENTRY macros will add a line RtlpCheckListEntry(Entry); to verify the lists between operations. This expands to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FORCEINLINE
VOID
RtlpCheckListEntry(
    _In_ PLIST_ENTRY Entry
    )
{
    if ((((Entry->Flink)->Blink) != Entry) ||
        (((Entry->Blink)->Flink) != Entry))
    {
        FatalListEntryError(
            (PVOID)(Entry),
            (PVOID)((Entry->Flink)->Blink),
            (PVOID)((Entry->Blink)->Flink));
    }
}

So what is FatalListEntryError?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FORCEINLINE
VOID
FatalListEntryError(
    _In_ PVOID p1,
    _In_ PVOID p2,
    _In_ PVOID p3
    )
{
    UNREFERENCED_PARAMETER(p1);
    UNREFERENCED_PARAMETER(p2);
    UNREFERENCED_PARAMETER(p3);
 
    RtlFailFast(FAST_FAIL_CORRUPT_LIST_ENTRY);
}

At last, we can see where the INT 29H (push 3) seems to be stemming from. In fact, RtlFastFail is then defined as:

//++
//VOID
//RtlFailFast (
//    _In_ ULONG Code
//    );
//
// Routine Description:
//
//    This routine brings down the caller immediately in the
//    event that critical corruption has been detected.
//    No exception handlers are invoked.
//
//    The routine may be used in libraries shared with user
//    mode and kernel mode.  In user mode, the process is
//    terminated, whereas in kernel mode, a
//    KERNEL_SECURITY_CHECK_FAILURE bug check is raised.
//
// Arguments
//
//    Code - Supplies the reason code describing what type
//           of corruption was detected.
//
// Return Value:
//
//     None.  There is no return from this routine.
//
//--
DECLSPEC_NORETURN
FORCEINLINE
VOID
RtlFailFast(
    _In_ ULONG Code
    )
{
    __fastfail(Code);
}

And finally, to complete the picture:

//
// Fast fail failure codes.
//
#define FAST_FAIL_RANGE_CHECK_FAILURE         0
#define FAST_FAIL_VTGUARD_CHECK_FAILURE       1
#define FAST_FAIL_STACK_COOKIE_CHECK_FAILURE  2
#define FAST_FAIL_CORRUPT_LIST_ENTRY          3
#define FAST_FAIL_INCORRECT_STACK             4
#define FAST_FAIL_INVALID_ARG                 5
#define FAST_FAIL_GS_COOKIE_INIT              6
#define FAST_FAIL_FATAL_APP_EXIT              7
 
#if _MSC_VER >= 1610
DECLSPEC_NORETURN
VOID
__fastfail(
    _In_ unsigned int Code
    )
#pragma intrinsic(__fastfail)
#endif

So there you have it, the new __fastfail intrinsic generates an INT 29H, at least on x86, and the preceding 8 security failures are registered by Windows — I assume driver developers and user application developers could define their own internal security codes as well, preferably starting with a high enough ID not to interfere with future codes Microsoft may choose to add.

The bugcheck, by the way, is defined as:

//
// MessageId: KERNEL_SECURITY_CHECK_FAILURE
//
// MessageText:
//
// A kernel component has corrupted a critical data structure.
// The corruption could potentially allow a malicious user to
// gain control of this machine.
//
#define KERNEL_SECURITY_CHECK_FAILURE ((ULONG)0x00000139L)

This is a great mechanism that should make security issues much more “visible” to users, even if it means taking the system down. Hopefully the new and improved blue screen of death — the Sad Face Of Sorrow (SFOS) — will give users more indication as to why their system had to be taken down, as the current implementation lacks the details needed to differentiate between a crash, and a security failure such as this.

Black Hat 2008 Wrap-up

This year I had the chance to present some security-related findings that I had made earlier during the year inside Win32k.sys, the Windows GUI Subsystem and Window Manager. I presented a total of four bugs, all local Denial of Service (DoS) attacks. Two of these attacks could be done on any system up to Vista SP0 (one up to Server 2008) from any account, while the other two were NULL-pointer dereferences that required administrative access to exploit (however, since they are NULL-pointer dereferences, the risk for user->kernel elevation exists in one of them).

Because the first two attacks (from any guest account) relied on highly internal behavior of the Windows NT Kernel (used in all of Microsoft’s modern operating systems today), I thought it was interesting to talk a bit about the internals themselves, and then present the actual bug, as well as some developer guidance on how to avoid hitting this bug.

I’d like to thank everyone who came to see the talk, and I really appreciated the questions and comments I received. I’ve also finally found out why Win32k functions are called “xxx” and “yyy”. Now I just need to find out about “zzz”. I’ll probably share this information in a later post, when I can talk more about Win32k internals.

As promised, you’ll find below a link to the slides. As for proof of concept code, it’s currently sitting on my home machine, so it’ll take another week. On the other hand, the slides make three of the bugs very clear in pseudo-code. I’ll re-iterate them at the bottom of the post.

One final matter — it seems I have been unable to make the bh08@alex-ionescu.com email work due to issues with my host. For the moment, please use another e-mail to contact me, such as my Google Mail account. My email address is the first letter of my first name (Alex) followed by my entire last name (Ionescu).

Slides here: Keynote and PDF.

Bug 1:
1) Handle = OpenProcess(Csrss)
2) CreateRemoteThread(Handle, …, NtUserGetThreadState, 15, …)

Bug 2:
1) Handle = CreateWindowStation(…)
2) SetHandleInformation(Handle, ProtectFromClose)
3) CloseWindowStation(Handle)

Bug 3:
1) Handle = CreateWindowStation(…)
2) Loop all handles in the process with the NtQuerySystemInformation API, or, just blindly loop every handle from 4 to 16 million and mark it as protected, or, use Process Explorer to see what the current Window Station handle is, and hard-code that for this run. The point is to find the handle to the current window station. Usually this will be a low number, typically under 0x50.
3) SetHandleInformation(CurrentWinstaHandle, ProtectFromClose)
4) SwitchProcessWindowStation(Handle);

Inside Session 0 Isolation and the UI Detection Service – Part 2

In part 1 of the series, we covered some of the changes behind Vista’s new Session 0 Isolation and showcased the UI Detection Service. Now, we’ll look at the internals behind this compatibility mechanism and describe its behavior.

First of all, let’s take a look at the service itself — although its file name suggests the name “UI 0 Detection Service”, it actually registers itself with the Service Control Manager (SCM) as the “Interactive Services Detection” service. You can use the Services.msc MMC snap-in to take a look at the details behind the service, or use Sc.exe along with the service’s name (UI0Detect) to query its configuration. In both cases, you’ll notice the service is most probably “stopped” on your machine — as we’ve seen in Part 1, the service is actually started on demand.

But if the service isn’t actually running yet seems to catch windows appearing on Session 0 and automatically activate itself, what’s making it able to do this? The secret lies in the little-known Wls0wndh.dll, which was first pointed out by Scott Field from Microsoft as a really bad name for a DLL (due to its similarity to a malicious library name) during his Blackhat 2006 presentation. The version properties for the file mention “Session0 Viewer Window Hook DLL” so it’s probably safe to assume the filename stands for “WinLogon Session 0 Window Hook”. So who loads this DLL, and what is its purpose? This is where Wininit.exe comes into play.

Wininit.exe too is a component of the session 0 isolation done in Windows Vista — because session 0 is now unreachable through the standard logon process, there’s no need to bestow upon it a fully featured Winlogon.exe. On the other hand, Winlogon.exe has, over the ages, continued to accumulate more and more responsibility as a user-mode startup bootstrapper, doing tasks such as setting up the Local Security Authority Process (LSASS), the SCM, the Registry, starting the process responsible for extracting dump files from the page file, and more. These actions still need to be performed on Vista (and even more have been added), and this is now Wininit.exe’s role. By extension, Winlogon.exe now loses much of its non-logon related work, and becomes a more agile and lean logon application, instead of a poorly modularized module for all the user-mode initialization tasks required on Windows.

One of Wininit.exe’s new tasks (that is, tasks which weren’t simply grandfathered from Winlogon) is to register the Session 0 Viewer Window Hook Dll, which it does with an aptly named RegisterSession0ViewerWindowHookDll function, called as part of Wininit’s WinMain entrypoint. If the undocumented DisableS0Viewer value isn’t present in the HKLM\System\CurrentControlSet\Control\WinInit registry key, it attempts to load the aforementioned Wls0wndh.dll, then proceeds to query the address of the Session0ViewerWindowProcHook inside it. If all succeeds, it switches the thread desktop to the default desktop (session 0 has a default desktop and a Winlogon desktop, just like other sessions), and registers the routine as a window hook with the SetWindowsHookEx routine. Wininit.exe then continues with other startup tasks on the system.

I’ll assume readers are somewhat familiar with window hooks, but this DLL providesjust a standard run-of-the-mill systemwide hook routine, whose callback will be notified for each new window on the desktop. We now have a mechanism to understand how it’s possible for the UI Detection Service to automatically start itself up — clearly, the hook’s callback must be responsible! Let’s look for more clues.

Inside Session0ViewerWindowProcHook, the logic is quite simple: a check is made on whether or not this is a WM_SHOWWINDOW window message, which signals the arrival of a new window on the desktop. If it is, the DLL first checks for the $$$UI0Background window name, and bails out if this window already exists. This window name is created by the service once it’s already started up, and the point of this check is to avoid attempting to start the service twice.

The second check that’s done is how much time has passed since the last attempt to start the service — because more than a window could appear on the screen during the time the UI Detection Service is still starting up, the DLL tries to avoid sending multiple startup requests.
If it’s been less than 300 seconds, the DLL will not start the service again.

Finally, if all the checks succeed, a work item is queued using the thread pool APIs with the StartUI0DetectThreadProc callback as the thread start address. This routine has a very simple job: open a handle to the SCM, open a handle to the UI0Detect service, and call StartService to instruct the SCM to start it.

This concludes all the work performed by the DLL — there’s no other code apart from these two routines, and the DLL simply acknowledges window hook callbacks as they come through in cases where it doesn’t need to start the service. Since the service is now started, we’ll turn our attention to the actual module responsible for it — UI0Detect.exe.

Because UI0Detect.exe handles both the user (client) and service part of the process, it operates in two modes. In the first mode, it behaves as a typical Windows service, and registers itself with the SCM. In the second mode, it realizes that is has been started up on a logon session, and enters client mode. Let’s start by looking at the service functionality.

If UI0Detect.exe isn’t started with a command-line, then this means the SCM has started it at the request of the Window Hook DLL. The service will proceed to call StartServiceCtrlDispatcher with the ServiceStart routine specific to it. The service first does some validation to make sure it’s running on the correct WinSta0\Default windowstation and desktop and then notifies the SCM of success or failure.

Once it’s done talking to the SCM, it calls an internal function, SetupMainWindows, which registers the $$$UI0Background class and the Shell0 Window window. Because this is supposed to be the main “shell”
window that the user will be interacting with on the service desktop (other than the third-party or legacy service), this window is also registered as the current Shell and Taskbar window through the SetShellWindow and SetTaskmanWindow APIs. Because of this, when Explorer.exe is actually started up through the trick mentioned in Part 1, you’ll notice certain irregularities — such as the task bar disappearing at times. These are caused because Explorer hasn’t been able to properly register itself as the shell (the same APIs will fail when Explorer calls them). Once the window is created and a handler is setup (BackgroundWndProc), the new Vista “Task” Dialog is created and shown, after which the service enters a typical GetMessage/DispatchMessage window message handling loop.

Let’s now turn our attention to BackgroundWndProc, since this is where the main initialization and behavioral tasks for the service will occur. As soon as the WM_CREATE message comes through, UI0Detect will use the RegisterWindowMessage API with the special SHELLHOOK parameter, so that it can receive certain shell notification messages. It then initializes its list of top level windows, and registers for new session creation notifications from the Terminal Services service. Finally, it calls SetupSharedMemory to initialize a section it will use to communicate with the UI0Detect processes in “client” mode (more on this later), and calls EnumWindows to enumerate all the windows on the session 0 desktop.

Another message that this window handler receives is WM_DESTROY, which, as its name implies, unregisters the session notification, destroys the window lists, and quits.

This procedure also receives the WM_WTSSESSION_CHANGE messages that it registered for session notifications. The service listens either for remote or local session creates, or for disconnections. When a new session has been created, it requests the resolution of the new virtual screen, so that it knows where and how to display its own window. This functionality exists to detect when a switch to session 0 has actually been made.

Apart from these three messages, the main “worker” message for the window handler is WM_TIMER. All the events we’ve seen until now call SetTimer with a different parameter in order to generate some sort of notification. These notifications are then parsed by the window procedure, either to handle returning back to the user’s session, to measure whether or not there’s been any input in session 0 lately, as well as to handle dynamic window create and destroy operations.

Let’s see what happens during these two operations. The first time that window creation is being acted upon is during the afore-mentionned EnumWindows call, which generates the initial list of windows. The function only looks for windows without a parent, meaning top-level windows, and calls OnTopLevelWindowCreation to analyze the window. This analysis consists of querying the owner of the window, getting its PID, and then querying module information about this process. The version information is also extracted, in order to get the company name. Finally, the window is added to a list of tracked windows, and a global count variable is incremented.

This module and version information goes into the shared memory section we briefly described above, so let’s look into it deeper. It’s created by SetupSharedMemory, and actually generates two handles. The first handle is created with SECTION_MAP_WRITE access, and is saved internally for write access. The handle is then duplicated with SECTION_MAP_READ access, and this handle will be used by the client.

What’s in this structure? The type name is UI0_SHARED_INFO, and it contains several informational fields: some flags, the number of windows detected on the session 0 desktop, and, more importantly, the module, window, and version information for each of those windows. The function we just described earlier (OnTopLevelWindowCreation) uses this structure to save all the information it detects, and the OnTopLevelWindowDestroy does the opposite.

Now that we understand how the service works, what’s next? Well, one of the timer-related messages that the window procedure is responsible for will check whether or not the count of top level windows is greater than 0. If it is, then a call to CreateClients is made, along with the handle to the read-only memory mapped section. This routine will call the WinStationGetSessionIds API and enumerate through each ID, calling the CreateClient routine. Logically, this is so that each active user session can get the notification.

Finally, what happens in CreateClient is pretty straightforward: the WTSQueryUserToken and ImpersonateLoggedOnUser APIs are used to get an impersonation logon token corresponding to the user on the session ID given, and a command line for UI0Detect is built, which contains the handle of the read-only memory mapped section. Ultimately, CreateProcessAsUser is called, resulting in the UI0Detect process being created on that Session ID, running with the user’s credentials.

What happens next will depend on user interaction with the client, as the service will continue to behave according to the rules we’ve already described, detecting new windows as they’re being created, and detection destroyed windows as well, and waiting for a notification that someone has logged into session 0.

We’ll follow up on the client-mode behavior on Part 3, where we’ll also look at the UI0_SHARED_INFO structure and an access validation flaw which will allow us to spoof the client dialog.

Inside Session 0 Isolation and the UI Detection Service – Part 1

One of the many exciting changes in Windows Vista’s service security hardening mechanisms (which have been aptly explained and documented in multiple blogs and whitepapers , so I’ll refrain from rehashing old material) is Session 0 Isolation. I’ve thought it would be useful to talk about this change and describe the behaviour and implementation of the UI Detection Service (UI0Detect), an important part of the infrastructure in terms of providing compatible behaviour with earlier versions of Windows.

As a brief refresher or introduction to Session 0 Isolation, let’s remember how services used to work on previous versions of Windows: you could run them under various accounts (the most common being System, Local Service and Network Service), and they ran in the same session the console user, which was logged-on to session 0 as well. Services were not supposed to display GUIs, but, if they really had to, they could be marked as interactive, meaning that they could display windows on the interactive window station for session 0.

Windows implemented this by allowing such services to connect to the Winsta0 Windowstation , which is the default interactive Windowstation for the current session — unlike non-interactive services, which belonged to a special “Service-0x0-xxx$” Windowstation, where xxx was a logon session identifer (you can look at the WDK header ntifs.h for a list of the built-in account identifiers (LUIDs)). You can see the existence of these windowstations by enumerating them in the object manager namespace with a tool such as Sysinternals’ WinObj.

winobj

Essentially, this meant three things: applications could either do Denial of Service attacks against named objects that the service would expect to own and create, they could feed malicious data to objects such as sections which were incorrectly secured or trusted by the service, and , for interactive services, they could also attempt shatter attacks — sending window messages with executable payloads in their buffer, exploting service bugs and causing the payload code to execute at higher privileges.

Session 0 Isolation puts an end to all that, by first having a separate session for the console user (any user session starts at 1, thus protecting named objects), and second, by disabling support for interactive services — that is, even though services may still display a UI, it won’t appear on any user’s desktop (instead, it will appear on the default desktop of the session 0 interactive windowstation).

That’s all fine and dandy for protecting the objects, but what if the service hasn’t been recompiled not to directly show a UI (but to instead use a secondary process started with CreateProcessAsUser, or to use the WTSSendMessage API) and depends on user input before continuing? Having a dialog box on the session 0 desktop without the user’s awareness would potentially have significant application compatibility issues — this is where the UI Detection Service comes into play.

If you’re like most Vista users, you’ve actually probably never seen the default desktop on session 0’s interactive windowstation in your life (or in simpler words, never “logged-on” or “switched to” session 0)! Since you can’t log on to it, and since interactive services which displayed UIs are thankfully rare, it remains a hidden mystery of Windows Vista, unless you’ve encountered such a service. So let’s follow Alice down the rabbit hole into session 0 wonderland, with some simple Service Controller (Sc.exe) commands to create our very own interactive service.

Using an elevated command prompt, create a service called RabbitHole with the following command:

sc create RabbitHole binpath= %SYSTEMROOT%\system32\notepad.exe type= interact type= own

Be careful to get the right spaces — there’s a space after each equal sign! You should expect to get a warning from Sc.exe, notifying you of changes in Windows Vista and later (the ones I’ve just described).

Now let’s start the service, and see what happens:

sc start RabbitHole

If all went well, Sc.exe should appear as if it’s waiting on the command to complete, and a new window should appear on your taskbar (it does not appear in the foreground). That window is a notification from the UI Detection Service, the main protagonist of this story.

session0detect

Get ready to click on “Show me the Message” as soon as you can! Starting an essentialy fake service through Sc.exe will eventually annoy the Service Control Manager (SCM), causing it to kill notepad behind your back (don’t worry if this happens, just use the sc start RabbitHole command again).

You should now be in Session 0 (and probably unable to read the continuation of this blog, in which case the author hopes you’ve been able to find your way back!) As you can notice, Session 0 is a rather deserted place, due to the lack of any sort of shell or even the Theme service, creating a Windows 2000-like look that may bring back tears of joy (or agony) to the more nostalgic of users.

Session0

On the other hand, this desolate session it does contain our Notepad, which you should’ve seen disappear if you stayed long enough — that would be the SCM reaching its timeout of how long it’s willing to wait around hoping for Notepad to send a “service start” message back (which it never will).

Note that you can’t start any program you want on Session 0 — Cmd.exe and Explorer.exe are examples of programs that for one reason or another won’t accept to be loaded this way. However, if you’re quick enough, you can use an old trick common to getting around early 90ies “sandbox” security applications found in many libraries and elementary schools — use the common dialog control (from File, Open) to browse executable files (switch the file type to *.*, All Files), go to the System32 folder, right-click on Explorer.exe, and select Open. Notepad will now spawn the shell, and even if the SCM kills Notepad, it will remain active — feel free to browse around (try to be careful not to browse around in IE too much, you are running with System privileges!)

That’s it for this introduction to this series. In part 2, we’ll look at what makes this service tick, and in part 3, we’ll look at a technique for spoofing the dialog to lie to the user about which service is actually requesting input. For now, let’s delete the RabbitHole, unless you want to keep it around for impressing your colleagues:

sc delete RabbitHole

ScTagQuery: Mapping Service Hosting Threads With Their Owner Service

Today I want to introduce another utility for Vista and Windows Server 2008 called ScTagQuery (short for Service Controller Tag Query), a tool which will allow you identify to which running service a certain thread inside a service hosting process (e.g Svchost.exe) belongs to, in order to help with identifying which services may be using up your CPU, or to better understand the stack trace of a service thread.

If you’ve ever had to deal with a service process on your system taking up too much CPU usage, memory, or other kinds of resources, you’ll know that Task Manager isn’t particularly helpful at finding the offending service because it only lists which services are running inside which service hosting process, but not which ones are consuming CPU time (in fact, some Svchosts host more than a dozen services!)

Task Manager’s Services Tab

Process Explorer can also display this information, as well as the names of the DLLs containing the services themselves. Combined with the ability to display threads inside processes, including their starting address (which for some service threads, identifies the service they are associated with by corresponding to their service DLL) and call stack (which helps identify additional threads that started in a generic function but entered a service-specific DLL), Process Explorer makes it much easier to map each thread to its respective service name. With the cycles delta column, the actual thread causing high CPU usage can reliably be mapped to the broken or busy service (in the case of other high resource usage or memory leaks, more work with WinDBG and other tools may be required in order to look at thread stacks and contexts).

However, with each newer Windows release, the usage of worker pool threads, worker COM or RPC communication threads, and other generic worker threads has steadily increased (this is a good thing – it makes services more scalable and increases performance in multiprocessor environments), making this technique a lot less useful. These threads belong to their parent wrapper routines (such as those in ole32.dll or rpcrt4.dll) and cannot be mapped to a service by looking at the start address. The two screenshots below represent a service hosting process on my system which contains 6 services — yet only one of the DLLs identified by Process Explorer is visible in the list of threads inside the process (netprofm.dll).

Only one service DLL is visible
Only one service DLL is visible

Again, there’s nothing wrong with using worker threads as part of your service, but it simply makes identifying the owner of the actual code doing the work a lot harder. To solve this need, Windows Vista adds a new piece of information called the Service Tag. This tag is contained in the TEB of every thread (internally called the Sub-Process Tag), and is currently used in threads owned by service processes as a way to link them with their owning service name.

WinDBG showing the Service Tag

When each service is registered on a machine running Windows Vista or later, the Service Control Manager (SCM) assigns a unique numeric tag to the service (in ascending order). Then, at service creation time, the tag is assigned to the TEB of the main service thread. This tag will then be propagated to every thread created by the main service thread. For example, if the Foo service thread creates an RPC worker thread (note: RPC worker threads don’t use the thread pool mechanism – more on that later), that thread will have the Service Tag of the Foo service.

ScTagQuery uses this Sub-Process Tag as well as the SCM’s tag database and undocumented Query Mapping API (I_ScQueryTagInformation) to display useful information in tracking down rogue services, or simply to glean a better understanding of the system’s behavior. Let’s look at the option it supports and some scenarios where they would be useful.

ScTagQuery options

During a live session, the -p, -s and -d options are most useful. The former will display a list of all service threads which have a service tag inside the given process, then map that tag to the service name. It can also function without a process ID, causing it to display system-wide data (enumerating each active process). The -s option, on the other hand, dumps which service tags are active for the process, but not the actual threads linked to them — it then links these tags to their service name. Finally, the -d option takes a DLL name and PID and displays which services are referencing it. This is useful when a thread running code inside a DLL doesn’t have an associated service tag, but the SCM does know which service is using it.

The -a and -n options are particularly useful when you’ve obtained a service tag from looking at a crash dump yourself, and run the tool on the same system after a reboot. The -t option will let you map the service name if you know the PID of the service. If the PID changed or is otherwise unrecoverable, the -a option will dump the entire SCM tag database, which includes services which are stopped at the moment. Because these mappings are persistent across reboots, you’ll be able to map the thread with the service this way.

On the other hand, if all you’re dealing with is one thread, and you want to associate it to its service, the -t option lets you do just that.

Back to that same svchost.exe we were looking at with Process Explorer, here are some screenshots of ScTagQuery identifying the service running code inside rpcrt4.dll, as well as the service referencing the fundisc.dll module.

Identifying FunDisc.dll

Identifying rpcrt4.dll

Note that Thread Pool worker threads do not have a Service Tag associated to them, so the current version of ScTagQuery on Vista cannot yet identify a service running code inside a worker pool thread inside ntdll.dll.

Finally, I should mention that ScTagQuery isn’t the only tool which uses Service Tags to help with troubleshooting and system monitoring: Netstat, the tool which displays the state of TCP/IP connections on a local machine, has also been improved to use servicre tags to better identify who owns an open port (Netstat uses new information returned by various Iphlpapi.dll APIs which now store the service tag of every new connection). With the -b option, Netstat is now able to display the actual service name which owns an active connection, and not just the process name (which would likely just be svchost.exe).

New Netstat -b behavior on Vista

The tool page for ScTagQuery is located here. You can download ScTagQuery in both 32-bit and 64-bit versions from this link. Windows Vista or higher is required.

MemInfo: Peer Inside Memory Manager Behavior on Windows Vista and Server 2008

After my departure from the ReactOS project and subsequent new work for David Solomon, it wasn’t clear how much research and development on Windows internals I would still be able to do on a daily basis. Thankfully, I haven’t given up my number one passion — innovating, pushing the boundaries of internals knowledge, and educating users through utilities and applications.

In this vein, I have been working during my spare time on various new utilities that use new undocumented APIs and expose the internals behind Windows Vista to discover more about how the operating system works, as well as to be able to provide useful information to administrators, developers, students, and anyone else in between. In this post, I want to introduce my latest tool, MemInfo. I’ll show you how MemInfo can help you find bad memory modules (RAM sticks) on your system, track down memory leaks and even assist in detecting rootkits!

One of the major new features present in Windows Vista is Superfetch. Mark Russinovich did an excellent writeup on this as part of his series on Windows Vista Kernel Changes in TechNet Magazine. Because Superfetch’s profiling and management does not occur at the kernel layer (but rather as a service, by design choice), there had to be a new system call to communicate with the parts of Superfetch that do live in the kernel, just like Windows XP’s prefetcher code, so that the user-mode service could request information as well as send commands on operations to be performed.


Here’s an image of Process Explorer identifying the Superfetch service inside one of the service hosting processes.

Because Superfetch goes much deeper than the simple file-based prefetching Windows XP and later offer, it requires knowledge of information such as the internal memory manager lists, page counts and usage of pages on the system, memory range information, and more. The new SuperfetchInformationClass added to NtQuery/SetInformationSystem provides this data, and much more.

MemInfo uses this API to query three kinds of information:

  • a list of physical address ranges on the system, which describe the system memory available to Windows
  • information about each page on the system (including its location on the memory manager lists, its usage, and owning process, if any)
  • a list system/session-wide process information to correlate a process’ image name with its kernel-mode object 
  • MemInfo ultimately provides this information to the user through a variety of command line options described when you run the utility. Some of its various uses include:

    Seeing how exactly Windows is manipulating your memory, by looking at the page list summaries.

    The Windows memory manager puts every page on the system on one of the many page lists that it manages (i.e. the standby list, the zeroed page list, etc). Windows Internals covers these lists and usage in detail, and MemInfo is capable of showing their sizes to you (including pages which are marked Active, meaning currently in-use and by the operating system and occupying physical memory (such as working sets) and not on any of the lists). This information can help you answer questions such as “Am I making good use of my memory?” or “Do I have damaged RAM modules installed?”.

    For example, because Windows includes a bad page list where it stores pages that have failed internal consistency checks, MemInfo is an easy way (but not 100% foolproof, since Windows’ internal checks might not have detected the RAM is bad) to verify if any memory hardware on the system is misbehaving. Look for signs such as a highly elevated count of pages in the zeroed page list (after a day’s worth of computer use) to spot if Windows hasn’t been fully using your RAM to its potential (you may have too much!) or to detect a large memory deallocation by a process (which implies large allocations previously done).


    Here’s MemInfo on my 32-bit Vista system, displaying summary page list information.

    Windows Vista also includes a new memory manager optimization called prioritized standby lists — the standby state is the state in which pages find themselves when they have been cached by Windows (various mechanisms are responsible for this of caching, including the cache manager and Superfetch) and are not currently active in memory. Mark covered these new lists in his excellent article as well.

    To expose this information to system administrators, three new performance counters were added to Windows, displaying the size of the prioritized standby lists in groupings: priorities 0 through 3 are called Standby Cache Reserve, 4 and 5 are called Standby Cache Normal Priority, and finally, 6 and 7 are called Standby Cache Core. MemInfo on the other hand, which can also display these new lists, is an even better tool to identify memory in the standby state, since it is able to display the size of these lists individually.

    While memory allocations on Windows XP (which could be part of application startup, the kernel-mode heap, or simple memory allocations coming from various processes) would consume pages from a single standby list and thus possibly steal away pages that more critical processes would’ve liked to have on standby, Windows Vista adds 8 prioritized lists, so that critical pages can be separated from less important pages and nearly useless pages. This way, when pages are needed for an allocation, the lower priority standby lists are used first (a process called repurposing). By making snapshots of MemInfo’s output over a period of time, you can easily see this behavior.

    Here’s MemInfo output before, during, and after a large allocation of process private memory. Notice how initially, the bulk of my memory was cached on the standby lists. Most of the memory then became Active due to the ongoing large allocation, emptying the standby lists, starting by the lowest priority. Finally, after the memory was freed, most of the memory now went on the zero page list (meaning the system just had to zero 1GB+ of data).

    Seeing to what use are your pages being put to by Windows

    Apart from their location on one of the page lists, Windows also tracks the usage of each page on the system. The full list includes about a dozen usages, ranging from non-paged pool to private process allocations to kernel stacks. MemInfo shows you the partitioning of all your pages according to their usage, which can help pinpoint memory leaks. High page counts in the driver locked pages, non-paged pool pages and/or kernel stack pages could be indicative of abnormal system behavior.

    The first two are critical resources on the system (much information is available on the Internet for tracking down pool leaks), while the latter is typically tightly maintained for each thread, so a large number may indicate leaked threads. Other usages should also expect to see a lower number of pages than ones like process private pages, which is usually the largest of the group.

    At the time of this writing, here’s how Windows is using my 4GB of memory:

    Looking at per-process memory usage, and detecting hidden processes

    Internally, Windows associates private process pages with the kernel executive object that represents processes as managed by the process manager — the EPROCESS structure. When querying information about pages, the API mentioned earlier returns EPROCESS pointers — not something very usable from user-mode! However, another usage of this API is to query the internal list of processes that Superfetch’s kernel-mode component manages. This list not only allows to take a look at how much memory, exactly, belongs to each process on the system, but also to detect some forms of hidden processes!

    Hidden processes are usually the cause of two things. The first is processes which have been terminated, but not yet fully cleaned up by the kernel, because of handles which are still open to them. Task Manager and Process Explorer will not show these processes, but MemInfo is the only tool apart from the kernel debugger which can (so you don’t have to reboot in debugging mode). See below on how MemInfo is showing a SndVol32.exe process, created by Windows Explorer when clicking on the speaker icon in the system tray — Explorer has a bug which leaks the handles, so the process is never fully deleted until Explorer is closed.

    The second cause of why a process may be hidden is a rootkit that’s hooking various system calls and modifying the information returned to user-mode to hide a certain process name. More advanced rootkits will edit the actual system lists that the process manager maintains (as well as try to intercept any alternate methods that rootkit detection applications may use), but MemInfo adds a new twist, by using a totally new and undocumented Superfetch interface in Windows Vista. It’s likely that no rootkit in the wild currently knows about Superfetch’s own process database, so MemInfo may reveal previously hidden processes on your system. Unfortunately, as with all information, it’s only a matter of time until new rootkits adapt to this new API, so expect this to be obsolete in the next two years.

    There’s many more uses for MemInfo that I’m sure you can find — including as a much faster replacement for !memusage 8 if you’ve used that command in WinDBG before. MemInfo is fully compatible with both 32-bit and 64-bit versions of Windows Vista (including SP1 and Windows Server 2008 RC1 – even though Server 2008 does not include Superfetch, because both client and server versions of the OS will use the same kernel as of Vista SP1, the API set is identical), but not any earlier version of Windows. Apart from these simple summary views, MemInfo is powerful enough to dump the entire database of pages on your system, with detailed information on each — valuable information for anyone that needs to deal with this kind of data. Furthermore, unlike using WinDBG to attach to the local kernel, it doesn’t require booting the system into debug mode.

    You can download a .zip file containing both versions from this link. Make sure to run MemInfo in an elevated command prompt — since it does require administrative privileges. The documentation for MemInfo is located on the following page (this page is part of an upcoming website on which I plan to organize and offer help/links to my tools and articles).