Information Security is an inherently adversarial problem that requires both blue and red teams to understand their relative adversaries. Over the years, defenders have sought out methodologies and organizational frameworks to better describe attacker tradecraft. Conversely, red teamers have sought to stay one step ahead by making strategic tradecraft decisions in the face of consistently improving protective controls and detection strategies.
Cartographer is intended to provide a baseline of information about the ways that specific attack techniques interact with the operating system. We call the process of describing these interactions "Capability Abstraction". While offensive and defensive teams will use this information for opposing goals, the need to understand the low-level actions abstracted by attack tooling is ever-present for both sides. Various contributors at SpecterOps have developed and published different explanatory models for describing attacker tradecraft and Cartographer attempts to not only borrow from these, but also make suggestions about how to take action on the information they convey.
The best description of Cartographer is my x33fcon talk, available here:
We have written extensively about the methodology we use to research attack techniques and the way that we organize the findings uncovered during that work. The following posts are a good place to start if you would like to learn more about capability abstraction:
With the widespread adoption of the MITRE ATT&CK Matrix, the concept of Tactics, Techniques, and Procedures (TTPs) became common parlance amongst information security professionals. While it still remains a fantastic starting point and common language to dissect attacker tradecraft, there is a lack of a clear brightline for what constitutes a procedure. In some cases, a procedure is a tool, in other instances it is a specific command and command line parameters. It is reasonable to consider these observable artifacts as Procedures, but treating tooling as the lowest-level instantiation of an attack technique does not address the fact that tools themselves are abstractions of lower-level interactions with the operating system.
If we want to dive deeper into the Procedure layer, we need to first work our way down from the Tactics layer. For every Tactic, there are one or more Techniques. Techniques can be further divided into Sub-Techniques. Each Technique and Sub-Technique has one or more procedures associated with it. But in MITRE ATT&CK parlance, there is no further hierarchical category to describe elements of attacker tradecraft that exist at a level more specific than the "Procedure".
Examining a specific procedure and attempting to describe it as more than just a "Procedure" helps understand why this term is useful in general, but may have its limits when it comes to building detection strategies (and more evasive attack tooling). The following diagram shows several layers of detail below the Procedure layer related to a specific Procedure of T1003.001: OS Credential Dumping / LSASS Memory
The diagram shows 4 Procedures that will all achieve the goal of accessing and retrieving credential material from the virtual memory of a running lsass.exe process. We are only looking at the first Procedure, Direct Access or Read. Each procedure has one or more Operations that must be completed to, in this case, access and retrieve credential material from an LSASS process. These three operations are:
1) Process Enumeration to determine the PID of lsass.exe's process
2) Process Access, which requires the calling process to get a handle to lsass.exe via Win32 API calls like OpenProcess
3) Process Read to actually read the virtual memory of the lsass.exe process into a buffer that can be saved, manipulated, output, or whatever other outcome the operator prefers.
Each Operation has one or more Functions that must be called in order to carry out that operation. The Operation itself is purely a semantic label used to desribe one or more callable Functions (in many cases, Win32 API functions) that expose the desired functionality. There is no "Process Read" Operation outside of the specific functions that allow a developer to read process memory. Nonetheless, Functions still require their own layer because just like at the layers above, attackers have multiple paths to the same successful outcome at the function layer. This particular diagram is a bit simplified for clarity's sake, but the overall takeaway that a mindful attacker has a great deal of choices with their own unique tradeoffs to make below the Procedure layer should be quite clear at this point.
At this point, we encounter two distinct layers of "Functions". The first layer represents the various "Entry Points" where a developer is able to call a function that fulfills the requirements of an Operation. The bottom-most layer in this diagram shows what happens when these Entry Point functions are called. In this example, we have highlighted the entry point kernel32!ReadProcessMemory. When this function is called, a series of other functions are called in sequence:
1) The implementation of ReadProcessMemory in kernel32.dll simply imports ReadProcessMemory from an API set named api-ms-win-core-memory-l1-1-0 (i.e. a "forwarded export")
2) This API set redirects execution to the implementation of ReadProcessMemory in kernelbase.dll
3) Kernelbase!ReadProcessMemory makes a call to the native API function ntdll!NtReadVirtualMemory/ZwReadVirtualMemory, which are responsible for making a syscall with the correct opcode value before execution is passed to the kernel.
Understanding the hierarchical nature of functional abstraction in Windows is extremely important when determining what telemetry is available for specific operations and the ways that tool developers can avoid telemetry generation in certain circumstances.
Putting it all together
If you've read this far, you may be saying "Ok, computers are complicated and filled with abstracted functionality...so what?" and I don't blame you! The key takeaway from all of this is that if we want to build more evasion resilient detection strategies, we have to understand the assumptions we are making about the low-level interactions with the operating system that stem from higher-level abstractions (i.e. tooling, Win32 API calls, native commands, etc.). The same goes for building more evasive tooling on the offensive side.
In this example, we can now make some important assertions about where attackers might spend their time if they seek to evade common detections and protective controls. We can structure these assertions around the layer at which the relevant assumptions become clear:
Procedure Layer: An attacker has at least four distinct ways to access credential material stored in LSASS process memory. There may be tradeoffs associated with one choice over another, but they will all achieve the same outcome if implemented correctly. At this layer, we don't have any specific observable artifacts or description of lower level functionality that could help us prioritize detection or evasion work.
Operations Layer: Each procedure will have distinct child layers (i.e. distinct nodes at the Operations and Functions layers), but we can already see three "gates" through which attackers must pass in order to read from process memory. They must locate the LSASS process whose memory they wish to read and they must obtain a handle to that process. This allows us to clearly focus our efforts from a detection perspective - we can choose to detect only one of these operations or all of them depending on the telemetry available, the verbosity of that telemetry, and the nature of protective controls in our environment (among other factors).
Cartographer is not currently open source, but will be soon. Once the repo is public, feel free to follow the docs to build the necessary content to add a new procedure, open an issue to discuss the accuracy of the content, or fork the repo to stand up your own instance of Cartographer.
Until then, feel free to reach out to @michaelbarclay_ on Twitter or email him at mbarclay@specterops.io.