As defenders, we need to keep pace with all kinds of different aspects of the attack surface. For Windows, the attack surface seems to just continue beyond our grasp every way we look, especially when we start to dig into trust and the registry. As previously outlined in the Splunk Threat Research Team’s blog, "From Registry With Love: Malware Registry Abuses," the vast methods used by adversaries to persist and abuse the Windows registry goes deep. One technique worthy of a deep-dive is T1553.003 - Subvert Trust Controls: SIP and Trust Provider Hijacking. Research in 2018 by Matt Graeber shined a bright light on the subject of subverting trust in Windows during the SpecterOps keynote presented at DerbyCon and whitepaper.
In this blog, we offer a focused exploration on key aspects of Windows security and practical defense strategies, including:
(larger visual here)
SIPs are integral to the security architecture of the Windows operating system, playing a pivotal role in managing the handling and verification of digital signatures across various file types. SIPs are designed to dictate how the operating system processes and verifies signatures, ensuring the integrity of files and applications. This is particularly important for defenders to understand, as SIPs can be exploited by malicious actors to bypass security measures or to illegitimately sign malicious code.
Adversaries may tamper with SIP and trust provider components to deceive the operating system and application control tools during signature validation checks. Windows uses Authenticode digital signatures in user mode to verify a file's origin and integrity. The signature validation is managed through the WinVerifyTrust API function, which coordinates with the appropriate trust provider responsible for validating signature parameters.
Adversaries may abuse this architecture to subvert trust controls and bypass security policies. They might hijack SIP and trust provider components to misclassify malicious code as legitimately signed by modifying specific registry values. These modifications can redirect to maliciously-crafted dynamic link libraries (DLLs), which can falsely validate any file signature or indicate that a validation was successful. Additionally, such hijacks can enable persistent code execution, as these malicious components may be invoked by any application performing code signing or signature validation.
In summary, SIPs are vital for helping to maintain the security and integrity of the Windows operating system. A comprehensive grasp of SIPs equips defenders with the knowledge needed to anticipate potential attack vectors and proactively fortify the security of their Windows environments.
For more information and details on SIP, check out these resources:
As defenders, we may not need to know every intricate value for how to generate a SIP, but we do need to understand how traces occur and where to look. For testing, we look to the SIP DLL sample provided by Matt Graebers and another DLL sample from a project by Grzegorz Tworek. With that, we can easily load one of these up. The idea is that, once a new SIP is loaded, it should load everytime certificate validation occurs.
We’ll first test out https://github.com/gtworek/PSBits/tree/master/SIP which includes the source and compiled DLL.
This test is quite easy to do. Once the DLL is downloaded, run the following to register it
Regsvr32.exe GTSIPProvider.dll.
Now, this DLL will be registered as a new SIP.
To remove it, run:
Regsvr32.exe /u GTSIPProvider.dll
Traces left:
Sources | Trace |
---|---|
Registry | HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllCreateIndirectData\{00000000-DEAD-BEEF-DEAD-DEADBABECAFE} HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllGetCaps\{00000000-DEAD-BEEF-DEAD-DEADBABECAFE} HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllGetSignedDataMsg\{00000000-DEAD-BEEF-DEAD-DEADBABECAFE} HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllIsMyFileType2\{00000000-DEAD-BEEF-DEAD-DEADBABECAFE} HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllPutSignedDataMsg\{00000000-DEAD-BEEF-DEAD-DEADBABECAFE} HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllRemoveSignedDataMsg\{00000000-DEAD-BEEF-DEAD-DEADBABECAFE} HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllVerifyIndirectData\{00000000-DEAD-BEEF-DEAD-DEADBABECAFE} |
File | C:\AtomicRedTeam\atomics\T1553.003\bin\GTSIPProvider.dll |
Process CommandLine | "cmd.exe" /c "regsvr32.exe C:\AtomicRedTeam\atomics\T1553.003\bin\GTSIPProvider.dll" "C:\Windows\system32\regsvr32.exe" GTSIPProvider.dll |
Module Load | C:\AtomicRedTeam\atomics\T1553.003\bin\GTSIPProvider.dll C:\Users\Administrator\Downloads\GTSIPProvider.dll |
Event Log: CAPI2 | EventCode=82 Error - “Call_CryptSvcCatDBEnumCatalogs_NotFound” targetFilePath='\Users\Administrator\Downloads\GTSIPProvider.dll |
An Atomic Red Team test is available here.
To use the Atomic, run the following:
Invoke-Atomictest T1553.003
In our second test, we will utilize the POC that Matt Graeber shared in his SIP keynote talk.
The DLL is here: https://github.com/mattifestation/PoCSubjectInterfacePackage
Similarly as before, we can load it with regsvr32 and unload it. From the repo:
regsvr32 C:\path\to\MySip.dll
Upon installing this SIP via regsvr32, any file you create with the .foo, .bar, or .baz file extension will validate properly with the embedded certificate.
A normal uninstallation of this SIP is performed as follows (from an elevated prompt):
regsvr32 /u C:\path\to\MySip.dll
Now, this DLL is bit more thorough in registering as a proper SIP. Note that when a binary is ran, certificate validation occurs, or properties of a binary are viewed events are generated by different Image (processes):
There were a few thousand events regarding MySIP.dll loading into sdiagnhost.exe, dllhost.exe and regsvr32.exe.
With the registry, as before, we see similar modifications:
Registry:
From our research, these registry keys typically do not get created daily, therefore monitoring for modifications should be high fidelity enough to alert on.
Now, semi-directly related to SIP, during our research and diving into Matt’s work on subverting trust, part of it was focused on copying metadata from a signed binary to a non-signed binary.
For our final test, we perform a similar attack where we utilize https://github.com/minisllc/metatwin, which is described as “a file resource cloner. Metadata, including digital signature, is extracted from one file and injected into another. Note: Signatures are copied, but no longer valid.” In layman terms, it will clone the data from a signed binary and place it in another.
To test we performed the following:
Before signing Mimikatz:
Invoke-MetaTwin -Source C:\Windows\System32\notepad.exe -Target c:\temp\mimikatz.exe -Sign
When running the meta twin cmdlet -
Now, in the new path we have a signed MimiKatz -
C:\Users\Administrator\Downloads\metatwin-master\metatwin-master\20231019_193037\20231019_193037_mimikatz.exe
In the whitepaper, Matt shares a few sources for defenders to review. Let’s dig into 3 and see how we can use Splunk.
The Splunk Security Content Analytic Story - Subvert Trust Controls SIP and Trust Provider Hijacking - has the following analytics to assist with identifying trust provider modifications -
Name |
Technique |
Type |
For inventory, we can perform this in a few ways. To start, we know that the 6 registry paths will be where we want to monitor. On a single endpoint, we can run the following script:
$registryPaths = @( "HKLM:\SOFTWARE\Microsoft\Cryptography\Providers", "HKLM:\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0", "HKLM:\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 1", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Cryptography\Providers", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Cryptography\OID\EncodingType 0", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Cryptography\OID\EncodingType 1" ) function Get-RegistryValuesRecursively { param ( [Alias("Path")] [string]$registryPath ) if (Test-Path $registryPath) { try { $values = Get-Item -LiteralPath $registryPath | Select-Object -ExpandProperty Property foreach ($value in $values) { $valueData = Get-ItemProperty -LiteralPath $registryPath -Name $value Write-Output "Key: $registryPath" Write-Output "Value: $value" Write-Output "Data: $($valueData.$value)" Write-Output "----" } } catch { Write-Warning "Could not retrieve values from $registryPath. Error: $($_.Exception.Message)" } try { $subkeys = Get-ChildItem -LiteralPath $registryPath foreach ($subkey in $subkeys) { # Recursive call for the subkey Get-RegistryValuesRecursively -registryPath $subkey.PSPath } } catch { Write-Warning "Could not retrieve subkeys from $registryPath. Error: $($_.Exception.Message)" } } else { Write-Warning "Path does not exist: $registryPath" } } foreach ($registryPath in $registryPaths) { Get-RegistryValuesRecursively -registryPath $registryPath }
The output will look like such:
If you want to output to CSV it’s a simple modification:
$registryPaths = @( "HKLM:\SOFTWARE\Microsoft\Cryptography\Providers", "HKLM:\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0", "HKLM:\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 1", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Cryptography\Providers", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Cryptography\OID\EncodingType 0", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Cryptography\OID\EncodingType 1" ) # Output CSV file path $outputCsvPath = "C:\path\to\your\output.csv" function Get-RegistryValuesRecursively { param ( [Alias("Path")] [string]$registryPath ) if (Test-Path $registryPath) { try { $values = Get-Item -LiteralPath $registryPath | Select-Object -ExpandProperty Property foreach ($value in $values) { $valueData = Get-ItemProperty -LiteralPath $registryPath -Name $value # Create a custom object and output it [PSCustomObject]@{ "Key" = $registryPath "Value" = $value "Data" = $valueData.$value } } } catch { Write-Warning "Could not retrieve values from $registryPath. Error: $($_.Exception.Message)" } try { $subkeys = Get-ChildItem -LiteralPath $registryPath foreach ($subkey in $subkeys) { Get-RegistryValuesRecursively -registryPath $subkey.PSPath } } catch { Write-Warning "Could not retrieve subkeys from $registryPath. Error: $($_.Exception.Message)" } } else { Write-Warning "Path does not exist: $registryPath" } } $registryOutput = foreach ($registryPath in $registryPaths) { Get-RegistryValuesRecursively -registryPath $registryPath } # Export the output array to a CSV file $registryOutput | Export-Csv -Path $outputCsvPath -NoTypeInformation
Now the output is a CSV:
Now, you may want to gather these values across a fleet of endpoints and there is better way than with scripted inputs in a inputs.conf.
Here is how.
Create a new app or modify an existing, modify and add the following to your inputs.conf:
Get the inputs here -
https://gist.github.com/MHaggis/75dd5db546c143ea67703d0e86cdbbd1
The scripted inputs will query the registry keys and gather all the values recursively. Modify the schedule to run monthly, weekly or daily as needed.
In Splunk, the output is as such:
As described in a prior blog here we used CAPI2 logs to identify certificate extraction using EventID 70. In this run, we added EventID 81 and 82 to help capture when trust for a binary is being verified.
[WinEventLog://Microsoft-Windows-CAPI2/Operational] disabled = 0 renderXml = 1 whitelist = $XmlRegex='(?:70|81|82).+' index = win
Note this event log is meant to be for debugging which is why we are filtering down to three EventIDs. Even though we filtered down to three, 81 is fairly noisy for a single endpoint, therefore monitor/tuner/filter as needed.
For registry monitoring, based on our prior work with inventory, we created an analytic that looks for the registry modifications.
tstats `security_content_summariesonly` count values(Registry.registry_key_name) as registry_key_name values(Registry.registry_path) as registry_path min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Registry where Registry.registry_path IN ("*\\SOFTWARE\\Microsoft\\Cryptography\\Providers\\*", "*\\SOFTWARE\\Microsoft\\Cryptography\\OID\\EncodingType*", "*\\SOFTWARE\\WOW6432Node\\Microsoft\\Cryptography\\Providers\\*", "*\\SOFTWARE\\WOW6432Node\\Microsoft\\Cryptography\\OID\\EncodingType*") Registry.registry_value_name IN ("Dll","$DLL") by Registry.dest , Registry.user Registry.registry_value_name, Registry.registry_value_data `security_content_ctime(lastTime)` `security_content_ctime(firstTime)` `drop_dm_object_name(Registry)`
This blog serves as a vital resource for security analysts, blue teamers, and anyone utilizing Splunk in their cybersecurity toolkit. Delving into the intricacies of SIP and Trust Provider Hijacking, especially T1553.003, empowers the cybersecurity community with critical insights into a significant aspect of the Windows attack surface.
Understanding SIPs is not just about adding another concept to your knowledge base; it's about equipping yourself to better identify, understand, and counteract advanced threats that exploit these mechanisms. The Splunk Threat Research Team’s insights, combined with the hands-on testing experiences shared here, are designed to enhance your ability to detect and respond to these types of security challenges.
By exploring SIPs as outlined in this blog you can recognize the tactics, techniques, and procedures associated with their misuse. This understanding can be crucial to you in developing more effective Splunk detections, ultimately fortifying your defenses against sophisticated adversaries that might leverage SIPs for malicious purposes.
In conclusion, our exploration into SIPs within the Windows framework has been an enlightening journey, emphasizing the intricacies of the attack surface and the importance of staying vigilant in Windows security. We delved into the specifics of SIP and Trust Provider Hijacking, focusing on the notable T1553.003. This exploration wasn't just theoretical; it was a hands-on, practical approach to understanding the nuances of modern cybersecurity defenses.
Through our tests, we've gained valuable insights into the mechanisms of registering and removing SIP DLLs, and the significance of monitoring registry changes. Additionally, the exploration into metadata copying has shed light on more subtle aspects of trust provider integrity.
Moreover, the integration of Splunk as a tool for monitoring security content, managing inventory, and tracking registry alterations is demonstrably invaluable.
You can find the latest Splunk content about security analytic stories on GitHub and in Splunkbase. Splunk Security Essentials also has all these detections now available via push update.
For a full list of security content, check out the release notes on Splunk Docs.
Any feedback or requests? Feel free to put in an issue on Github and we’ll follow up. Alternatively, join us on the Slack channel #security-research. Follow these instructions if you need an invitation to our Splunk user groups on Slack.
We would like to thank Michael Haag for authoring this post and the entire Splunk Threat Research Team for their contributions including Teoderick Contreras, Mauricio Velazco, Lou Stella, Bhavin Patel, Rod Soto, Eric McGinnis, and Patrick Bareiss.
The Splunk platform removes the barriers between data and action, empowering observability, IT and security teams to ensure their organizations are secure, resilient and innovative.
Founded in 2003, Splunk is a global company — with over 7,500 employees, Splunkers have received over 1,020 patents to date and availability in 21 regions around the world — and offers an open, extensible data platform that supports shared data across any environment so that all teams in an organization can get end-to-end visibility, with context, for every interaction and business process. Build a strong data foundation with Splunk.