Tactical to Functional
This note is based on an excellent blog series by Jared Atkinson and explores the ways we can and should go deeper than the Tactics, Techniques & Procedures.
Taxonomy
- Functions, both documented and undocumented, of the Win API and syscalls
- Standard Functions (
syscalls
)- Responsible for transitioning execution from user-mode to kernel-mode
- e.g.: NtCreateThreadEx
- Sub-operations (
ntdll!NtQueryInformation*
,ntdll!NtSetInformation*
)- Here the parameters passed to the function have strong influence over what the function does, so we need to take them into consideration to properly assign the operation.
- Remote Procedure Calls (
rpcrt4!NdrClientCall*
)- Functions built on RPC Procedures do not result in syscalls, at least not directly. An RPC procedure transfers execution from the client application/host, the Win32 API function, to the server application/host.
- Local Security Authority Functions (
LsaCallAuthenticationPackage
)- LSA Functions rely on a specific RPC Procedure called
SspirCallRpc
, which the Microsoft Security Support Provider Interface (MS-SSPI) implements. MS-SSPI offers an extensible framework for authentication whereby Microsoft and third-party vendors can extend how users authenticate to the system. - As a result, all interactions with “Authentication Packages” flow through the same RPC Procedure. However, the Local Security Authority (LSA) determines how to handle each request based on the specified Authentication Package and AP function.
- So it is not be enough to observe the invocation of
SspirCallRpc
- LSA Functions rely on a specific RPC Procedure called
- Driver IOCTLs (
kernel32!DeviceIOControl
,ntdll!NtfsControlFile
)- Responsible for the transfer of execution from the client application to the kernel driver.
- These functions rely on making using Input/Output Control Codes with kernel drivers to trigger sub-routines of the drivers.
- Compound Functions
- Individual functions that perform multiple operations
- e.g.:
kernel32!Toolhelp32ReadProcessMemory
which performs Process Access and Process Read
- Local Functions (
memcpy
orGetCurrentProcess
)- Functions that never cross a relevant client/server boundary like that of user-mode/kernel-mode, RPC client/server, or LSA client/auth package.
- e.g.:
kernel32!GetCurrentProcess
, It returns -1, which functions as a pseudo-handle for the calling process. As a result, the function call stack is essentially non-existent and not particularly interessting from the detection engineering perspective.
- Standard Functions (
- Operations work as a container for teleologically equivalent functions
- Synonyms
- Functional Synonyms are tools which rely on the exact same function calls to achieve their outcome.
- Procedural Synonyms are tools which sequence the same operations in the same order as each other, but use differing functions or sequence them differently.
- Sub-Technical Synonyms are tools that differ at the procedural level, but still implement the same sub-technique.
Method
-
Understand the source code of the different implementations of the technique/sub-technique and enumerate the functions used. → Capability Abstraction
-
Understand that there are alternatives to these functions and that a single function can have multiple purposes (Compound Functions). So abstract the individual functions into what purpose(s) they serve, aka. Operations.
-
Consider that there are alternative Operation Paths (e.g.: instead of accessing a process, maybe we can clone it and access that), so enumerate these as well.
-
Now search for all possible, also the undocumented, functions that implement the operations in the different Operation Paths.
What Exactly Do We Define as a Procedure?
A procedure is “a sequence of operations that, when combined, implement a technique or sub-technique.”
The procedures are the pattern of steps to execute, not the execution of the steps. E.g.:
- Enumerate processes to obtain the process identifier for lsass.exe.
- Open a handle to lsass.exe with the PROCESS_VM_READ access right.
- Read the memory of lsass.exe to obtain the credentials stored within.
The vital point here is not the steps; it is what the steps represent. Each step corresponds with an operation:
- Step 1 == Process Enumerate
- Step 2 == Process Access
- Step 3 == Process Read
The steps define a sequence of operations that implements a sub-technique, specifically, the sequence of operations that is instantiated by Mimikatz’s sekurlsa::logonPasswords
command.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Relevant Note(s): Capability Abstraction
Footnotes
-
https://posts.specterops.io/on-detection-tactical-to-functional-d71da6505720 ↩
-
https://posts.specterops.io/on-detection-tactical-to-functional-37ddcd75234b ↩
-
https://posts.specterops.io/on-detection-tactical-to-functional-45e41fef7af4 ↩
-
https://posts.specterops.io/on-detection-tactical-to-functional-5ff667af633b ↩
-
https://posts.specterops.io/on-detection-tactical-to-functional-fef1e09d3174 ↩
-
https://posts.specterops.io/on-detection-tactical-to-function-810c14798f63 ↩
-
https://posts.specterops.io/on-detection-tactical-to-functional-ceb3ad0e3809 ↩
-
https://posts.specterops.io/beyond-procedures-digging-into-the-function-call-stack-88c082aeb573 ↩
-
https://posts.specterops.io/on-detection-from-tactical-to-functional-1349e51e1a03 ↩
-
https://posts.specterops.io/on-detection-tactical-to-functional-f37c9b0b8874 ↩
-
https://posts.specterops.io/on-detection-tactical-to-functional-d214f64c580b ↩
-
https://posts.specterops.io/on-detection-tactical-to-functional-a3a0a5c4d566 ↩
-
https://posts.specterops.io/behavior-vs-execution-modality-3318e8e81739 ↩
-
https://posts.specterops.io/part-14-sub-operations-5e119cf610b1 ↩
-
https://posts.specterops.io/part-15-function-type-categories-5942e70e05ab ↩
-
https://posts.specterops.io/part-16-tool-description-e09506ebc2c7 ↩