Capability Abstraction
Feb 6 2020
By: Jared Atkinson • 15 min read
In 2020, the SpecterOps detection team members are making an effort to relate the concepts of their blog posts to the Funnel of Fidelity. The Funnel of Fidelity is a model that SpecterOps uses to contextualizes the phases that must occur to successfully detect and remediate an attack. Our hope is this helps readers understand where the post fits into the context of their overall detection program. This specific post is focused on a strategy that can be applied to the Detection phase of the funnel.

Introduction
This is the first of a multipart blog series by the SpecterOps detection team. The goal of this series is to introduce and discuss foundational detection engineering concepts. To make these concepts as consumable as possible, we are focusing the entire series around Kerberoasting. Focusing on this technique allows readers to focus on the strategies presented in each article instead of worrying about the details of the technique itself. The focus of this post is a concept we call “capability abstraction.” The idea is that an attacker’s tools are merely an abstraction of their attack capabilities, and detection engineers must understand how to evaluate abstraction while building detection logic.
What is Abstraction?
You may be wondering what I mean when I say “abstraction.” In the context of this article, abstraction is a concept by which implementation details are hidden from/automated for the user. Abstraction typically manifests itself in layers, called abstraction layers, that systematically hide complexity with each layer providing a more superficial interface than the last. Abstraction is an important design element because technology products often target a user base that fundamentally does not understand how the products work but can still benefit from the use of the technology. Ultimately, one of the main goals of abstraction is to hide the complex inner workings of something to make it more approachable to a wider audience.
A second reason for abstraction is to streamline interaction with technology. In information technology, we often say, “if you have to do it more than once, automate it.” Similarly, attackers may want to streamline their attack procedures as much as possible, within their acceptable OPSEC constraints. We see this concept expressed in the tools that adversaries use. Each tool provides some level of abstraction in the form of a common interface for operators to use.
I believe that if we can peel back the abstraction layers to understand the fundamental attack, we can make more informed decisions regarding the best ways to detect an attack technique.
What is Kerberoasting?
I want to spend some time talking about Kerberoasting before getting too far down the capability abstraction rabbit hole. In a macro sense, Kerberoasting is an attack technique that allows attackers to convert Kerberos ticket-granting service tickets (TGS tickets) into passwords. At a micro (more detailed) level, Kerberos authentication uses a service principal name (SPN) to uniquely identify a service instance. Each SPN associates a service instance (think web server, SQL Server, etc.) to a service logon account. To authenticate to a remote system, a user must request a TGS ticket for the target service which is identified by the SPN. A portion of the TGS ticket is encrypted using the password hash of the service logon account that is associated with the target SPN. To Kerberoast, attackers extract that encrypted portion and attempt to brute force the decryption of that section until they are successful. Once they successfully decrypt the ticket, they know the service logon account’s password, can assume the identity of that account, and log in to any systems that the account has access to.
If you are interested in digging deeper into Kerberoasting before continuing, I recommend checking out Tim Medin’s Attacking Kerberos: Kicking the Guard Dog of Hades presentation where he introduced the concept (the slides can be found here). Additionally, harmj0y has written a few blog posts on the topic, specifically his Kerberoasting without Mimikatz post where he links to numerous other sources on the topic.
Abstraction of Kerberoasting
Throughout this discussion, this post leverages a visual graphic referred to as an “abstraction map” that aims to help readers follow along with the relationships between the abstraction layers as they are discussed. The idea is to reveal and track the abstraction layers as we discover them. At this point, we haven’t explored Kerberoasting at all, so our initial abstraction map below is empty.

Let’s get started!
Tools
It is important to consider that attack tools represent the most superficial layer of abstraction; however, a quick review of publicly shared detection logic reveals that defenders often focus detection engineering efforts on tool-specific signatures. These superficial detections are not inherently bad but are prone to blind spots (false negatives) without further contextual data. The goal of evaluating abstraction’s role in an attack technique is to inform detection engineering efforts. This process can help avoid false negatives, learn about potential telemetry needs, understand new attack implementations, and determine when the detection approach sufficiently covers our target technique.
Invoke-Kerberoast
Invoke-Kerberoast is a PowerShell advanced function that allows an attacker to request a Kerberos service ticket for a target account. In addition to requesting the service ticket, Invoke-Kerberoast also extracts the encrypted portion of the ticket and returns it in a format that can be cracked offline using popular password cracking tools like John the Ripper and Hashcat. Below is an example of executing Invoke-Kerberoast to request a ticket in a test domain.

At this point, the perspective of kerberoasting is limited to just a single tool (Invoke-Kerberoast). Let’s take a second to update the abstraction map with Invoke-Kerberoast:

When considering the current limited perspective of the problem, it makes sense why it may be common for detection engineers to create a simple detection for Invoke-Kerberoast and call it a day. Detections built for Invoke-Kerberoast at this abstraction layer might focus on the PowerShell function name (Invoke-Kerberoast), a string within the code (“@harmj0y”), or even the cryptographic hash of the script itself. These are great first steps and it would be silly to ignore these simple detections, but what happens when a new tool is released to accomplish the same attack technique?
Rubeus Kerberoast
Rubeus is a utility that includes many capabilities for abusing Kerberos, but the “kerberoast” command is specifically related to Kerberoasting. It turns out that Rubeus’s kerberoast command is functionally equivalent to Invoke-Kerberoast, but the capability is packaged a bit differently.

The abstraction map can now be updated to include Rubeus’s kerberoast command to the tools layer.

While Invoke-Kerberoast is written in PowerShell, Rubeus is written in C#, so there may be differences in how detections can be implemented. For instance, the PowerShell function name and a cryptographic hash of Invoke-Kerberoast are not relevant to detecting Rubeus. Instead, a detection engineer might focus on writing signatures for command-line arguments or maybe they would create additional detections using the cryptographic hash of Rubeus.
As you can imagine, this approach scales very poorly because of potentially infinite tool implementations variants for Kerberoasting. Instead, consider viewing tools as the most superficial layer of abstraction and dig into the actual functionality of Invoke-Kerberoast and Rubeus.
Managed Code
At this point in the journey, two unique Kerberoasting tools have been identified. Now it is important to think about what Invoke-Kerberoast and Rubeus kerberoast have in common. Those familiar with PowerShell and C# might remember that both languages are built on top of the .NET Framework, which is an abstraction itself that allows programmers to focus on functionality without worrying about complex programming topics like memory management, security, portability, etc. Keeping this in mind, it is reasonable to expect that these two tools might show some similarities when evaluated at the managed code layer. When comparing the code used to request service tickets from Invoke-Kerberoast and Rubeus, it is revealed that they both rely on a call to a constructor overload for the KerberosRequestorSecurityToken .NET Class as shown below.


As you can see, while the tools are written in different languages, they both eventually rely on the KerberosRequestorSecurityToken class. In theory, if a detection could be built to focus on the use of this particular class, a single detection could be used for both tool implementations.
Its time to add a new layer to the abstraction map for “Managed Code.” Notice that this layer has one entry that covers both tool implementations.

Hopefully, the value of peeling back the abstraction is beginning to become clear. However, it is important to note that while peeling back layers of abstractions allow for a more broad or comprehensive detection, it also means that the detection is possibly losing precision, which leads to a higher likelihood of false positives. For instance, what are the legitimate use cases for the KerberosRequestorSecurityToken class in .NET and how can analysts differentiate legitimate use from malicious use? This is a topic that a colleague of mine will discuss in more depth in a future blog post.
Windows API Functions
Imagine that you write detection logic to alert on the use of the KerberosRequestorSecurityToken .NET class. Should you stop there and consider Kerberoasting a solved problem? What happens if someone writes a Kerberoasting tool in a language that doesn’t interact with .NET? Maybe they decide to write it in C++. Well, as mentioned earlier, .NET is an abstraction layer itself, so let’s dig deeper to see what is happening underneath.
Luckily, .NET is open source, so the source code (.NET Reference Source Code) is readily accessible. Using the reference source, detection engineers can dig into the KerberosRequestorSecurityToken class’s implementation to understand how it works under the hood. With a bit of investigation, it turns out that the KerberosRequestorSecurityToken class functionality (constructor) that is used by Invoke-Kerberos and Rubeus’s kerberoast command is built on two Windows API functions from Secur32.dll, namely AcquireCredentialsHandle and InitializeSecurityContext.
The Windows API is a set of functions that allow programmers to interact with the operating system in a predefined and documented way. Think of these as yet another layer of abstraction that dictates how software can interact with hardware components, such as memory, network interfaces, or input devices. It is also theoretically possible for attackers to call InitializeSecurityContext without flowing through the KerberosRequestorSecurityToken .NET class.
Below are screenshots from the .NET Reference Source showing how .NET implements AcquireCredentialsHandleW and InitializeSecurityContextW via a concept called Platform Invoke (P/Invoke).


As a result, this function provides yet another layer where detection engineers may be able to build detection logic.

A logical assumption might be that a Kerberoasting tool written in a language that doesn’t use .NET would also use the InitializeSecurityContext API function. Let’s take a look at a tool written in C++ and see if that assumption is correct.
Almost everyone in information security is familiar with mimikatz as a tool that allows attackers to dump plaintext passwords from memory. What fewer know is that mimikatz is a complex tool that provides numerous capabilities, one of which is the ability to request arbitrary service tickets for Kerberoasting. Below is an image that shows mimikatz being used to request the same service ticket that we requested with Invoke-Kerberoast and Rubeus.

Mimikatz’s kerberos::ask command can now be added to the list of tools that can be used for Kerberoasting as shown below:

Now, how can a detection engineer determine if mimikatz builds on InitializeSecurityContext? Again this is a case where the tool is open source, so the source can be checked to see if mimikatz calls the InitializeSecurityContext.
Inspecting the source code for the kerberos::ask command reveals that it is implemented by the kuhl_m_kerberos_ask function.

The kuhl_m_kerberos_ask function then calls a function called LsaCallKerberosPackage.

The LsaCallKerberosPackage function goes on to finally call a Windows API function called LsaCallAuthenticationPackage.

It appears that this logical assumption turned out to be incorrect. Instead of calling InitializeSecurityContext, mimikatz calls LsaCallAuthenticationPackage. It appears that there are at least two Windows API functions that can request service tickets. Let’s take a moment and update our abstraction map.

By now you probably know the next question that needs to be answered! Is there common ground between InitializeSecurityContext and LsaCallAuthenticationPackage?
Remote Procedure Call
It appears that at least two Windows API functions that can be used to request a service ticket, but do those API functions converge to a shared abstraction layer underneath? To help figure this out, I worked with Matt Graeber (@mattifestation) to analyze security.dll (the library that both functions live in). During our analysis, it appeared that both InitializeSecurityContext and LsaCallAuthenticationPackage made RPC calls. Additional inspection showed that both API functions interact with the RPC interface with a UUID of 4f32adc8–6052–4a04–8701–293ccf2096f0.
A quick Google search of the UUID returned Matt Nelson’s (@enigma0x3) GitHub gist where he listed all of the RPC interfaces on his machine. The see screenshot below shows that the RPC interface in question is implemented by sspisrv.dll.

If you are interested in a deeper dive into how this was discovered, check out Adam Chester’s (@_xpn_) excellent post titled Exploring Mimikatz Part 2. In his post, Adam explains how he examined a very similar situation involving the inner workings of mimikatz’s Security Support Provider capability.
A new “RPC” layer can now be added to the abstraction map to show how both InitializeSecurityContext and LsaCallAuthenticationPackage ultimately rely on the same RPC call behind the scenes.

Network Protocol
You might be asking yourself, “well wouldn’t the attacker have to call one of the API functions that we talked about to make the network request in the first place?” The answer can be found in our old friend Rubeus. Rubeus has many bits of functionality that focus on abusing the Kerberos protocol. The “kerberoast” functionality was covered earlier, but a different command called “asktgs” allows an attacker to request a service ticket by building a raw TGS-REQ packet and parsing the raw TGS-REP response. Think of this as the sustainable, local, handcrafted, artisanal approach to Kerberoasting. Rubeus asktgs is shown below:

The abstraction map must be updated to include Rubeus’s asktgs at the tool layer. Unfortunately, this implementation has no overlaps with any of the abstraction layers the have been discovered thus far. This sounds like an opportunity for more digging!

Fundamentally, Kerberos is a network authentication protocol where a user authenticates with a central entity, called the Kerberos Key Distribution Center (KDC). Kerberoasting requires that a service ticket or multiple service tickets be requested to facilitate offline password cracking. To make this request, a network connection from the client to the KDC must be made and the network request must be in the form of a Ticket Granting Service Request (TGS-REQ).
Below, are packet captures of Invoke-Kerberoast, Rubeus kerberoast, mimikatz kerberos::ask, and Rubeus asktgs. Notice that while there are slight differences in the packet captures, each tool makes the same TGS-REQ request, which is highlighted in each image.




The abstraction map is completed by adding a Network Protocol layer that covers all implementations of Kerberoasting. Keep in mind that while the RPC and Network Protocol layers are more inclusive of Kerberoasting implementations, they are also more inclusive of legitimate activity. Simply put, it is not always ideal to build detections at lower abstraction layers.

Keep in mind that this abstraction map may not consider every possible abstraction involved in Kerberoasting. Can you think of any additional abstraction layers? If so, please share them in the comments!
Conclusion
I’ve commonly heard attackers talk about how important it is to understand how their attack tools work, but I’ve rarely heard a similar sentiment from my defensive family. Too often I see a detection engineer take an attack tool at face value. Understanding how offensive capabilities are abstracted creates an opportunity for detection engineers to evaluate their detection approach in an informed way. I believe that by taking more time to understand the technology that tools abstract away from us, we will be able to raise the bar and not just detect what we know about, but what we don’t know about as well. Capability Abstraction is just one tool that you can add to your detection engineering tool chest.
The next posts in this series will explain strategies for understanding the pros and cons of detections at different layers of abstraction and how to leverage an abstraction map to determine the most effective approach to layering detection logic to create a comprehensive detection approach.