Configuring & Monitoring Verbose Powershell CLI Usage with Splunk UF - cfloquetprojects/homelab GitHub Wiki

Introduction

It's not often we defenders leave tools beloved by our adversaries embedded on systems they wish to compromise, but with such a powerful and prevalent tool, we aren't given much of a choice. PowerShell is a Windows command-line utility commonly used by attackers and sysadmins alike to gather information from a specific host or environment, and often readily available to be exploited for those already in your network.

In order to combat this issue head on, we will be leveraging domain-level group policy objects (GPOs) as well as the Splunk Universal Forwarder agent to both transcribe and centralize logs from distinct Windows 10 systems on any & all PowerShell commands.

Unfortunately, despite PowerShell being so robust in its capabilities, by default it doesn't actually leave many artifacts behind on the localhost of it's own execution, let alone verbose transcripts of the different commands run or what their associated results were.

  • We will be walking through enabling not only logging the PowerShell commands being run by a user on a given host, but also also full transcript of data being returned within that terminal to understand what information may have been yielded.

Resources

Agenda:

In order for us to truly begin to "win" investigations into PowerShell usage in our environment, we will need to accomplish the following, all of which will be covered in this lab:

  1. Edit domain-level GPOs to enable the following different logging features:

    • Module Logging (4103), in the context of PowerShell auditing, records the pipeline execution details (which includes variable initialization and command invocations), but doesn't reliably capture any & all commands executed, which is why we will also be enabling Script Block Logging and especially Transcription Logging.
    • We can enable Script Block Logging (4104), which will record the input of every PowerShell command on the host, but if we really want the most verbose output we should turn on transcription, which will write not only the commands entered but also their respective results as the attacker sees them.
    • Lastly, and perhaps most importantly, is transcription, which creates a unique record of every PowerShell session, including all input and output text. The notable exception to what gets recorded here (which is recorded by Module and Script Block logging) is the contents of executed scripts, or outputs written to other destinations such as the file system.
  2. Configure write-only permissions for domain-level users to PowerShell transcript folder

  3. Monitoring folder/files where PowerShell event logs will be stored using Splunks' Universal Forwarder

  4. Using props.conf and regex on the Indexer/Heavy Forwarder instance in order to parse fields, as well as trim unnecessary information and reduce noise/license impact.

  5. Identifying malicious PowerShell using common TTPs mapped to the MITRE ATT&CK framework associated with this attack vector.

  6. Create advanced searches which leverage the DECRYPT2 app (see resources) to decrypt encoded commands.

Pre-Flight Check:

  • We will be using essentially two distinct machines in todays lab, but this is built upon previous labs where we have configured Active Directory, Splunk Enterprise, and other important infrastructure on servers that will not be covered in todays lab.

  • At the very least, you'll need a dedicated domain controller as well as a domain-joined Win10 client, which will be used to deploy group policies to and monitor PowerShell activity on.

  • The machines I am using involve a management server running on Windows Server 2016, with a Splunk Universal forwarder configured on it that has a separate Deployment Server for installing/removing apps, but this is not entirely necessary and the edits we will do later can be done at $SPLUNK_HOME/etc/system/local/inputs.conf.

Make sure you have an indexer or heavy forwarder configured to listening for the data that we will be sending to it prior to continuing as well.

Configuring Permissions for PowerShell Transcription Folder:

In order for us to collect transcribed PowerShell logs, we will need to define a folder path where the .txt files which will be generated can be stored, which I have chosen to store in C:\Windows\Logs\powershell

💣 It's critical that (as shown in the screenshot below) we leave Modify permissions unchecked for this user group, as this is what essentially disallows log tampering.

[0]

Configuring Domain-Level GPOs for PowerShell Module, Script Block, and Transcription Logging:

First things first, on either our domain controller or dedicated management host (whichever has direct access to edit/modify existing domain-level group policy objects) within Server Manager navigate to Tools > Group Policy Management:

💣 Make sure you are logging into the device you wish to manage group policies using an account which is part of the Domain Administrators group within the AD domain you are managing. Otherwise, you will not be able to make any changes to domain-level group policy objects (GPOs).

[1]

Once we have our Group Policy Management console open, navigate to your domain (which in my home lab is yellowstone.local, but in your environment is almost certainly different) navigate and right click to "Edit" the Default Domain Policy, as shown in the screenshot below:

[2]

This will open up the specific (domain-level) Group Policy Editor, which is how we will interact with and modify our GPOs via, but the changes we make can also be done at the registry level using an elevated terminal.

Navigate to Windows PowerShell within Windows Components as shown below, where we will be met with options to configure the different features that were discussed earlier, such as Module Logging, Script Block Logging, as well as Transcription:

[3]

Double click (or right click and Edit) first the Turn on Module Logging setting where we can configure all PowerShell modules to be monitored, as represented by the *, or what's often referred to as a wildcard.

[4]

As shown in the screenshot above be sure to Apply your changes prior to either hitting OK, or Next Setting in order to move onto configuring Script Block Logging, as shown in the screenshot below:

💡 Enabling invocation logging here basically logs events when the invocation of commands or scripts start or stop, which often gives additional information about when a malicious script started/stopped running, again bolstering our ability to create timelines based on these events.

[5]

Lastly, we need to turn on/configure PowerShell transcription, and configure a proper output directory (to a folder which has write-only permissions for all domain-level users, which we have done in a previous step):

💡 Invocation Headers are important to include because this is what allows us to get a start time for each terminal session, which helps during an incident response investigation to establish timelines etc.

[6]

Finally, your Windows PowerShell GPO settings at a domain level should look something like below, which you can push to all domain-joined clients using the gpupdate /force command, also shown below:

[7]

[8]

Validating Proper Logging Configuration using PowerShell:

PS> $basePath = 'HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'

PS> Set-ItemProperty $basePath -Name EnableScriptBlockLogging -Value "1"