Capability Abstraction Case Study: Detecting Malicious Boot Configuration Modifications

Building durable and reliable detection often comes down to the accuracy of the assumptions we make about attacker tradecraft and the way it interacts with the telemetry sources we have at our disposal. In our work with clients and students, the Adversary Detection team has come up with a model for making better assumptions that we refer to as capability abstraction. In simple terms, capability abstraction provides a way to describe how a given attack technique interacts with the internal components of a targeted system. The abstraction map that this process produces helps us to understand the common denominator between distinct implementations of the same technique. Once that picture begins to form, both the evasion opportunities available for attackers and the types of telemetry that are likely to encompass those evasions often become clearer than they were before.

This blog will attempt to apply this abstraction process to a common technique employed by Ransomware-as-a-Service (Raas) operators to prepare target systems for the deployment of ransomware. While this post will only cover a specific example, the goal is to provide a clear example of the analysis steps necessary to build a capability abstraction map and how we can use that to build a detection that accounts for obvious evasion opportunities.

Detecting RaaS Operations

After years of high-profile ransomware operations and ever-increasing ransom demands, the underground economy that supports these operations has become increasingly complicated and inter-related. The dominance of the Ransomware-as-a-Service (RaaS) business model has characterized this trend and offers several unique challenges for organizations seeking to optimize their detection posture for their seemingly inevitable encounter with these groups.

Defenders have found themselves attempting to detect and mitigate a huge number of RaaS providers who have managed to coordinate the efforts of a diverse range of individuals in a whole-of-society approach to cybercrime. While researchers may find some of the tradecraft employed by individual operators affiliated with RaaS operations lacking in a “sophistication”, it is hard to deny that they have been wildly successful.

Despite the relative simplicity of the operational blueprint employed by most RaaS operators, defenders still have a difficult job on their hands when evaluating whether their current detection posture is adequate in this context.

There are two main factors that make this prospect difficult:

1) The sheer number of distinct RaaS groups. “RaaS groups” refers to the higher-level organizers of ransomware operations who coordinate the efforts of developers, case/affiliate managers, “customer support” agents, affiliates, and others. The ‘service’ aspect of the term ‘Ransomware-as-a-Service’ refers to the way in which these groups offer proven ransomware variants and payment infrastructure to independent operators.

2) The diversity of TTPs associated with ransomware operations up to the point when ransomware is deployed in a target environment. In addition to the fact that the affiliates associated with a given RaaS group are constantly shifting, affiliates can include both individuals and other well-organized groups that specialize in selling access to compromised environments. This means that tradecraft associated with RaaS operations is widely varied outside of attack techniques that are directly related to the deployment of ransomware.

The division of labor between RaaS providers and RaaS affiliates.

With these observations in mind, it’s important to attempt to identify where the TTPs associated with RaaS are most likely to converge. One such point of convergence occurs when RaaS operators make changes to Windows services, configuration items, and backup/recovery files that would otherwise aid incident responders attempting to recover data without paying for a decryption key. These actions normally occur just prior to the actual deployment of ransomware in a victim environment.

In MITRE ATT&CK terminology, this activity could be categorized under the “Impact” tactic using techniques like T1490 — Inhibit System Recovery and T1489 — Service Stop. More specifically, many RaaS operators employ native Windows system utilities like vssadmin, bcdedit, and wbadmin to disable various system services and boot options that may allow for data recovery without paying the ransom.

For the purposes of this blog, we are going to focus our energy on a specific tool that is employed by adversaries to inhibit system recovery: bcdedit.exe. I’ve chosen this tool not only because its usage has been observed in a number of RaaS incidents, but also because vx-underground recently tweeted that they had identified “a novel method to modify Windows boot configs” and were seeking input on detection strategies prior to release.

If you would like to check out the PoC code that inspired that tweet (and this blog post), it is available here . Many thanks to @am0nsec for putting together this PoC (and thoroughly commenting the code)! This PoC code is most accurately categorized as a re-implementation of BCDEdit’s functionality, which is quite helpful for the purpose of determining what MUST occur for an attacker to modify boot configuration options.

Ransomware and Boot Configuration Options

As ransomware campaigns have exploded in scope and scale in recent years, defenders and incident responders have developed several methods to recover their encrypted data without paying a ransom. At a high level, there are only two paths to avoid paying a ransom:

  1. Reverse the encryption process (decryption of data)
  2. Restore data from backup or some other recovery process

But just like any other kind of actor, RaaS groups have taken notice of these developments and have adjusted their tradecraft to account for them. The various recovery features built into the Windows operating system are an obvious target.

One such technique for impairing Windows-native data recovery functionality is the modification of Boot Configuration Data using BCDEdit’s “/set” parameter. Boot configuration data is stored in the registry and tracks several configuration items that govern Windows startup processes. RaaS affiliates seem to be particularly interested in boot configuration data settings related to boot status policy, whether recovery mode is enabled, and whether the system can be booted into safe mode. Each of these configuration items plays its own part in increasing the efficacy of ransomware for RaaS affiliates.

Let’s take a look at some example bcdedit commands for modifying each of these boot configuration items.

Boot Status Policy

bcdedit.exe /set {default} bootstatuspolicy ignoreallfailures

This command modifies the current “boot status policy” settings and will force the system to boot normally, rather than into the Windows recovery environment (Windows RE), if there is a failed boot, failed shutdown, or failed checkpoint during the startup process.

RaaS actors do not want administrators to be able to access the System Image Recovery feature in Windows RE. If already configured, administrators could restore data encrypted by ransomware from a prior snapshot of that data.

Recovery Mode

bcdedit.exe /set {default} recoveryenabled no

Disables Windows RE entirely. While changing the “bootstatuspolicy” boot configuration item to IgnoreAllFailures will stop the boot loader from loading into the recovery environment when there are startup errors, this command will stop administrators from doing so manually.

Safeboot

bcdedit.exe /set {default} safeboot minimal

This configuration item controls whether the system will boot into Safe Mode the next time that it is restarted. This command is a bit different than the other two. Its goal is to evade detection during the encryption process rather than impede recovery. While this doesn’t fit into the Inhibit System Recovery MITRE technique we are focusing on in this post, I am including it here because this functionality is implemented in am0nsec’s PoC that we will dig into below.

Now that we have an idea of how attackers are likely to use BCDEdit, it’s time to run these commands ourselves and determine how this native system utility modifies boot configuration data and what kinds of forensic artifacts result from that action.

Dynamic Analysis: BCDEdit.exe

There are a couple of research questions that we want to answer about BCDEdit so that we can identify a viable detection strategy:

  • Which Win32 API functions does BCDEdit call to modify boot configuration data?
  • What kinds of securable objects does BCDEdit interact with to modify boot configuration data?

We will start to answer these questions using Process Monitor. Process Monitor will give us a high level representation of how our example BCDEdit commands interact with the registry, the file system, processes, and/or other hosts (via network connections).

In the interest of brevity, we are only going to dive into one of the commands listed above because the specific parameters specified when using BCDEdit’s /set parameter do not change the method by which boot configuration data is modified.

The first step is to prepare Process Monitor to capture details related to the execution of our example command. We will be running the following once we are ready: bcdedit.exe /set bootstatuspolicy ignoreallfailures

Once our Process Monitor capture is populated, we can filter the output to only show actions taken by BCDEdit.exe. A simple Google search related to BCDEdit will bring us to Microsoft documentation that tells us that boot configuration data is stored in the registry, so let’s start our Process Monitor analysis by looking at registry operations.

Our capture recorded a large number of events under the banner of “Registry Activity”, but most of these events are meant to support the creation of a specific registry sub-key and to set a value under that sub-key. We can exclude all of those supporting events with the following filter:

Process Monitor filter settings to only show RegCreateKey and RegSetValue events sourced from bcdedit.exe.

Applying this filter leaves us with two events:

  1. The creation of the sub-key at HKLM\BCD00000000\Objects\{9f83643f-4a91-11e9-9501-b252ac81e352}\Elements\250000e0
RegCreateKey event for changing the boot status policy setting

While it is not always precise or helpful, Process Monitor provides a snapshot of the call stack at the time that the event was recorded. For RegCreateKey operation, we can see that BCDEdit called the ntdll!ZwCreateKey system call.

Call Stack for the above RegCreateKey event

2. Setting the value at HKLM\BCD00000000\Objects\{9f83643f-4a91-11e9-9501-b252ac81e352}\Elements\250000e0\Element

RegSetValue event for changing the boot status policy setting

If we take a look at the call stack at the time that this RegSetValue operation occurred, we can see that BCDEdit is making a call to ntdll!NtSetValueKey.

Call Stack for the above RegSetValue event

At this point we have determined that BCDEdit must modify the registry in some way to modify boot configuration data. Specifically, we saw the creation of a new sub-key and value for that sub-key. We don’t know why this specific sub-key was created or what would occur if we provided different command-line arguments to BCDEdit.

While it’s completely acceptable to move directly to analysis of our PoC code, it is often helpful to supplement analysis findings with additional research before attempting to determine how distinct implementations of the same attack technique differ.

We know that the registry is responsible for storing boot configuration data, but how is this data structured? How does the operating system make use of it at startup?

Windows Boot Loader Architecture

Startup processes on a Windows host are initially handled by the Windows boot manager. You may have heard of the Windows boot loader as well, but these two components are distinct. In fact, their functionality is implemented in two distinct binaries — bootmgr.exe and winload.exe, respectively. There is an additional binary that handles the resumption of Windows after a system exits sleep mode, called winresume.exe, but it is not particularly relevant to our interests here.

Running BCDEdit with no parameters supplied will display some basic metadata associated with the boot manager and the boot loader.

BCDEdit output (with no parameters)

The Windows boot manager has no concept of the requirements for a given operating system. Instead, it uses pre-defined Boot Configuration Data (BCD) to identify a boot loader entry for at least one version of Windows. Once a Windows boot loader entry is found, the boot manager displays the boot menu, loads the relevant OS-specific boot loader, and passes the necessary boot parameters to the boot loader.

Boot Configuration Data in the Registry

All Boot Configuration Data is located in the registry under the HKLM\BCD00000000\ sub-key. This sub-key includes options related to the boot manager as well as OS-specific boot loader objects.

The boot manager object and the various boot loader objects are differentiated by sub-keys named with GUID values located under the HKLM\BCD00000000\Objects sub-key. Each of these object sub-keys contain a description sub-key as well as a number of numerically-named sub-keys under HKLM\BCD00000000\Objects\{GUID}\Elements. These “elements” define the boot options that are applied for a given boot loader object.

Sub-keys related to Boot Configuration Data

Geoff Chappel has compiled a particularly useful list of the numeric values associated with specific boot object elements. For example, we can see that 0x23000003 corresponds to the BCDE_BOOTMGR_TYPE_DEFAULT_OBJECT element.

The boot manager object’s options can be found underneath HKLM\BCD00000000\Objects\{9dea862c-5cdd-4e70-acc1-f32b344d4795}\Elements. The most important element within this sub-key is HKLM\BCD00000000\Objects\{9dea862c-5cdd-4e70-acc1-f32b344d4795}\Elements\23000003, which contains the GUID of the default OS-specific bootloader that should be loaded by the boot manager.

On my Windows 10 virtual machine, this value was {9f83643f-4a91-11e9-9501-b252ac81e352}. We can confirm that this GUID refers to the Windows 10 boot loader object by querying the value stored in HKLM\BCD00000000\Objects\{9f83643f-4a91-11e9-9501-b252ac81e352}\Elements\12000004 (BCDE_LIBRARY_TYPE_DESCRIPTION), which contains the string “Windows 10”.

The rest of the elements located under the {9f83643f-4a91-11e9-9501-b252ac81e352}\Elements\ sub-key represent other types of metadata and, more importantly, boot options specific to Windows 10. These sub-keys are where both bcdedit and am0nsec’s re-implementation conduct most of their work.

Now that we have done our homework on how boot configuration data is stored in the registry and how the boot manager makes use of it at startup, let’s start digging in to our PoC code to see how it differs from the native BCDEdit utility.

Static Analysis: PoC

After getting a hold of the PoC code, I started with static analysis. This is mostly personal preference as I like to focus on source code first when I have access to it. Doing so allows me to develop a good understanding of what I should be looking for during later dynamic analysis.

It quickly became apparent that the PoC primarily revolves around registry modifications. It accesses and modifies the registry using several Nt* API calls. Since the code is quite straightforward (and well commented), it’s simple to produce a summary of the actions it takes.

  1. Obtain a handle to a BCD synchronization mutant.
  2. Obtain the necessary handles to the registry keys and sub-keys that contain Boot Configuration Data (BCD) for the default OS-specific boot object (ie. The boot object associated with Windows 10).
  • Obtain the GUID for the default OS loader by inspecting the value in HKLM\BCD00000000\Objects\{9dea862c-5cdd-4e70-acc1-f32b344d4795}\Elements\23000003.
  • Obtain a handle to the sub-key under HKLM\BCD00000000\Objects\ that matches that GUID. In the case of a Windows 10 host, that GUID would be {9f83643f-4a91-11e9-9501-b252ac81e352}.

3. Using the handle to the Windows 10 OS loader sub-key, obtain a handle to the child “Elements” sub-key.

4. If the Elements sub-keys of interest do not exist by default, create them. Each of these sub-keys correspond to a specific boot configuration option.

5. Modify boot object element values for configuration options relevant to “system recovery”. The DACL for each sub-key must be modified to provide write permissions to the current user before any change is made.

  • Enable safeboot (HKLM\BCD00000000\Objects\{9f83643f-4a91-11e9-9501-b252ac81e352}\Elements\25000080\Element)
  • Disable recovery mode (HKLM\BCD00000000\Objects\{9f83643f-4a91-11e9-9501-b252ac81e352}\Elements\16000009\Element)
  • Update boot status policy (HKLM\BCD00000000\Objects\{9f83643f-4a91-11e9-9501-b252ac81e352}\Elements\250000E0\Element)

6. Restore the original DACL ACEs for the modified sub-keys.

The primary implementation difference between am0nsec’s proof of concept and bcdedit.exe is the specific Windows API function calls made. More specifically, am0nsec’s implementation calls a different API (ntdll!NtCreateKey) than bcdedit.exe does (ntdll!ZwCreateKey). However, further analysis will show that this distinction is superficial and does not change our detection strategy in a significant way.

Dynamic Analysis: PoC

As we predicted based on our above static analysis, dynamic analysis using ProcMon showed a significant amount of registry interaction.

Process Monitor output showing a whole lot of registry interactions (events of interest highlighted)

The above screenshot shows only a snippet of the activity that was recorded, but it illustrates the general pattern of interaction with each element of interest within the Windows 10 boot object. Remember that each element sub-key under HKLM\BCD00000000\Objects\{BOOT_OBJECT_GUID}\Elements represents a different boot option. To simplify our analysis a bit, let’s focus on the modifications made for one of the three boot configuration options of interest.

In the screenshot, the PoC is attempting to set a specific value for the element 25000080, which corresponds to the BCDE_OSLOADER_TYPE_SAFEBOOT option. The two most important events related to this key are:

  1. The creation of the HKLM\BCD00000000\Objects\{9f83643f-4a91-11e9-9501-b252ac81e352}\Elements\25000080\sub-key
  2. Setting the value of HKLM\BCD00000000\Objects\{9f83643f-4a91-11e9-9501-b252ac81e352}\Elements\25000080\Element to 0
RegCreateKey and RegSetValueKey events

Despite the fact that our static analysis of the PoC code showed that it creates registry keys by calling ntdll!NtCreateKey, Process Monitor has logged ntdll!ZwCreateKey as the function responsible for the RegCreateKey operation (just like we saw with BCDEdit.exe). In reality, these API calls point to the same function in ntdll.dll, but we will discuss this point more when we build our abstraction map.

Stack trace for the RegCreateKey event

Like BCDEdit, the PoC code calls ntdll!NtSetValueKey to set a value for the newly created Elements sub-key.

Stack trace for the RegSetValue event

With this in mind, the main difference between the example command we ran to analyze the behavior of BCDEdit.exe and the PoC written by am0nsec is that the latter rolls all three of our example commands into one binary.

All RegCreateKey and RegSetValue events

The below table shows a summary of these registry interactions:

Boot Configuration Data sub-keys modified by am0nsec’s PoC code

Identifying Choke Points for Boot Option Modification

At this point we know that while it is possible to create an executable that implements BCDEdit.exe’s functionality, adversaries cannot avoid making modifications to specific registry sub-keys. With this in mind, we can build out an abstraction map the reflects this constraint.

The below abstraction map summarizes our analysis of BCDEdit and am0nsec’s PoC. It seeks to break down registry interaction into layers that represent how that interaction is mediated by Windows. When we summarize our findings in this way, it becomes clear that the PoC code we analyzed is functionally and methodologically indistinct from the native BCDEdit utility.

You may have noticed that the Win32API and Undocumented API layers seem a bit out of line with our static and dynamic analysis findings. We saw NtCreateKey explicitly used in our PoC code, but Process Monitor logged a call to ZwCreateKey when the PoC was run. This is likely the result of Process Monitor not distinguishing these function calls from one another. When we load ntdll.dll into IDA, we can see that NtCreateKey and ZwCreateKey are implemented in (and exported from) the same function definition. They have separate entries in the export table (entry 287 and entry 1814) but both result in the same call to syscall(0x1D) when called from usermode.

NtCreateKey/ZwCreateKey implementation in ntdll.dll

The same is also true for NtSetValueKey and ZwSetValueKey.

NtSetValueKey/ZwSetValueKey implementation in ntdll.dll

Detecting Boot Options Modifications in the Registry

Now that we have determined that writing new keys and values to the registry is unavoidable if an attacker wants to modify Boot Configuration options, we can begin to build our detection strategy. We now know that it is feasible to create our own utility that carries out the same registry interactions that BCDEdit would, so we know that we can’t focus our detection around process names and process command line parameters. Instead we need to focus on the securable objects layer (registry keys and their values).

The first step is identifying data sources that record some event every time that:

  1. A specific registry sub-key is created
  2. A value is set for a specific registry sub-key

While most modern EDR agents can generate events related to these actions, it is also possible to do so with native Windows data sources. Specifically, we can create System Access Control List (SACL) entries for the securable objects (sub-keys) we wish to monitor that will only generate Windows Security events when a process requests specific types of access.

A SACL is a component of a security descriptor, which may be set for every securable object in the Windows operating system. By adding an Access Control Entry (ACE) to a securable object’s SACL and enabling the relevant audit policy, we can generate 4663 events (“An attempt was made to access an object.”) whenever a process attempts to create sub-keys and set values related to boot options. Digging into the utility of SACLs and how to use them at scale is outside of the scope of this article, but Dane Stuckey at Palantir wrote a blog post that covers a lot of these questions.

SACLs and Windows Security Events

Because these events can be quite noisy if not properly configured, we want to ensure that we are only setting SACLs on the specific sub-keys that are relevant to boot options that attackers may want to modify prior to the deployment of ransomware.

We already know that two of the boot configuration items that attackers are interested in require the creation of a new sub-key before the relevant value can be set, so our SACL needs to take that into account.

One of the gotchas related to SACLs is that you cannot set a SACL on securable objects that do not already exist. Since two of the boot configuration items that we are interested in (BCDE_OSLOADER_TYPE_SAFEBOOT and BCDE_OSLOADER_TYPE_BOOT_STATUS_POLICY) don’t exist by default, we will need to set our SACL at the sub-key one level up in the registry from the sub-keys we are looking for.

To monitor for the creation of a sub-key at HKLM\BCD00000000\Objects\{9f83643f-4a91–11e9–9501-b252ac81e352}\Elements\25000080, we will set a SACL on the sub-key at HKLM\BCD00000000\Objects\{9f83643f-4a91–11e9–9501-b252ac81e352}\Elements where the requested access mask includes the Set Value access right or the Create Subkey access right.

SACL Configuration via RegEdit

When we apply the above SACL and run our compiled PoC, we see a large number of events populated in the Windows Security Event Log. When an attacker has to create a new sub-key before setting the relevant value, our SACL generates three events:

  • Event 4656: A handle to an object was requested
Windows Event ID 4656
  • Event 4663: An attempt was made to access an object
Windows Event ID 4663
  • Event 4657: A registry value was modified
Windows Event ID 4657

The above screenshots show Windows security events related to creating and setting a value for the sub-key at HKLM\BCD00000000\Objects\{9f83643f-4a91–11e9–9501-b252ac81e352}\Elements\25000080, but our SACL recorded similar events for the other two Elements sub-keys as well.

Building Detection Logic with SACL Events

Now that we understand which Windows Security Events will be generated when we set a SACL on a specific registry sub-key, we can start planning out our detection logic. There are a few different ways to go about making use of the events highlighted above and we don’t necessarily need all three of them to create a viable detection.

One potential option is to make use of 4656 events (A handle to an object was requested) for boot configuration elements that require the creation of a new registry sub-key. At a high-level, we are interested in investigating any process that creates a sub-key at HKLM\BCD00000000\Objects\{9f83643f-4a91–11e9–9501-b252ac81e352}\Elements\25000080 or HKLM\BCD00000000\Objects\{9f83643f-4a91–11e9–9501-b252ac81e352}\Elements\250000E0. 4656 events provide us with enough information to know that these sub-keys have been created and a process is attempting to retrieve a handle to one of those keys. With that in mind we can mock out a query that would look something like:

event_id = 4656 AND (object_name = \REGISTRY\MACHINE\BCD00000000\Objects\{9f83643f-4a91–11e9–9501-b252ac81e352}\Elements\25000080 OR \REGISTRY\MACHINE\BCD00000000\Objects\{9f83643f-4a91–11e9–9501-b252ac81e352}\Elements\250000E0)

The third boot configuration data element related to BCDE_LIBRARY_TYPE_AUTO_RECOVERY_ENABLED already exists by default, so BCDEdit and our PoC only need to set the value related to disabling access to the Windows Recovery Environment (Windows RE). The resulting 4657 event looks like this:

Windows Event ID 4657

Our pseudo-query for disabling recovery mode would look something like this:

event_id = 4657 AND (object_name = \REGISTRY\MACHINE\BCD00000000\Objects\{9f83643f-4a91–11e9–9501-b252ac81e352}\Elements\16000009)

While the SACL I showed above does successfully record a Windows Security event for all of the registry operations performed by both BCDEdit and our PoC code, a more storage efficient option would be to only audit “Create Subkeys” access attempts on the parent sub-key (Elements) and create a second SACL for “Set Value” access attempts only on the already existing sub-key related to the BCDE_LIBRARY_TYPE_AUTO_RECOVERY_ENABLED element (\Elements\16000009\Element).

Wrapping Up

An adversary who has achieved domain dominance to the extent that they are changing boot configuration data options is not just ready to deploy ransomware organization-wide. They are virtually guaranteed to do so almost immediately.

This does not mean that we should not write this detection. It just means that we must be realistic about:

  • its utility,
  • what it will mean for the SOC/IR teams responding to the resulting alert, and
  • the fact that we need to supplement it with detections that target earlier attack phases.

Additionally, the relatively widespread and well-documented nature of most RaaS operator TTPs means that most of those actions should already be covered under normal means detection efforts in an ideal world. By this, I mean that a robust detection ruleset would cover most pre-Impact techniques employed by RaaS operators without specifically setting out to do so.

This post intends to outline the process behind detecting a specific procedure related to making ransomware deployments more effective. It does not provide a silver bullet for detecting RaaS actors, but rather a method to begin breaking down the small number of TTPs that are specific to them. Many of the threat reports I read while writing this post suggest detecting specific command line parameters for the native BCDEdit utility, but do not discuss how these commands inevitably touch the registry. If your detection strategy does not move beyond the specific tool used to modify boot configuration data, all an attacker needs to do is write some marshalling functions and add the resulting code to their toolkit to entirely evade that strategy.

Additional Resources

Capability Abstraction Case Study: Detecting Malicious Boot Configuration Modifications was originally published in Posts By SpecterOps Team Members on Medium, where people are continuing the conversation by highlighting and responding to this story.

Article Link: Capability Abstraction Case Study: Detecting Malicious Boot Configuration Modifications | by Michael Barclay | Posts By SpecterOps Team Members