How to Use PowerShell Logging to Track Potential Attacks Against Office 365

How Attackers Use PowerShell

One of the sessions I attended at the recent TEC event covered the topic of attacks on Office 365. A recent session called “Attacking and Defending the Microsoft Cloud” given at the Black Hat USA 2019 conference explained how PowerShell can be used to fetch information from an Office 365 tenant, like a dump of your Azure Active Directory information.

I’m also well aware of how attackers use PowerShell to set forwarding addresses on mailboxes to redirect email. This is usually done to allow an attacker to understand the ebb and flow of messages into a target’s mailbox before launching a Business Email Compromise attack. Understanding what mailboxes have mail forwarding set is one of the reasons why tenant administrators should review the Forwarding report (Figure 1) in the Mail Flow section of the Security and Compliance Center weekly. Obviously anyone on the list should have a valid business reason to forward email outside the organization.

Reviewing the Forwarding report in the Security and Compliance Center
Figure 1: Reviewing the Forwarding report in the Security and Compliance Center

Much the same information can be retrieved with PowerShell. For example:

# Find mailboxes with mail forwarding address set
$Mbx = (Get-ExoMailbox -RecipientTypeDetails UserMailbox, SharedMailbox -ResultSize Unlimited -Properties ForwardingSmtpAddress)
$NumberForwards = 0
ForEach ($M in $Mbx) {
  If ($M.ForwardingSmtpAddress -ne $Null) { # Mailbox has a forwarding address
       $NumberForwards++
       Write-Host $M.DisplayName "is forwarding email to" $M.ForwardingSmtpAddress.Split(":")[1] } 
}
If ($NumberForwards -gt 0) { Write-Host $NumberForwards "mailboxes found with forwarding addresses"}

Enabling PowerShell Logging on a Workstation

In any case, discussing the various techniques used by attackers to exploit PowerShell isn’t the purpose of this post. Instead, I want to point out how useful it is to enable PowerShell logging on workstations. Following the introduction of PowerShell 5.0 in 2016, enabling logging was a popular recommendation in the security community (here’s an example post) with the idea that you can collect the logs generated by PowerShell activity into a repository like Splunk where they could then be queried as necessary.

It’s easy to enable PowerShell logging by updating settings in the local group policy editor (Figure 2).

Enabling PowerShell logging in the Local Group Policy Editor
Figure 2: Enabling PowerShell logging in the Local Group Policy Editor

Alternatively (and possibly more appropriately), because all we’re doing is manipulating system registry settings, we can do the same by running these PowerShell commands in an administrator session.

# Module Logging
$RegistryPath = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ModuleLogging"
$Name = "EnableModuleLogging"
$Value = "1"

If (!(Test-Path $RegistryPath))  { # Value Doesn't Exist, so create it
    New-Item -Path $RegistryPath -Force | Out-Null
    New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}
 Else {
    New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}

$Name = "ModuleNames"
$Value = "*"
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType String -Force | Out-Null

# Script Block Logging
$RegistryPath = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
$Name = "EnableScriptBlockLogging"
$Value = 1

If (!(Test-Path $RegistryPath))  { # Value Doesn't Exist, so create it
    New-Item -Path $RegistryPath -Force | Out-Null
    New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}
 Else {
    New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}

# Transcription
$RegistryPath = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\Transcription"
$Name = "EnableTranscripting"
$Value = "1"

If (!(Test-Path $RegistryPath))  { # Value Doesn't Exist, so create it
    New-Item -Path $RegistryPath -Force | Out-Null
    New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}
 Else {
    New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null}

$Name = "EnableInvocationHeader"
$Value = "1"
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null

$Name = "OutputDirectory"
$Value = "C:\Temp\PSLogs"
New-ItemProperty -Path $RegistryPath -Name $Name -Value $Value -PropertyType String -Force | Out-Null 

The Value of PowerShell Transcripts

My favorite setting is the one that enables PowerShell transcripts. The PowerShell cmdlets Start-Transcript and End-Transcript are available to generate transcripts manually, but I like to have automatic transcripts because I don’t have to remember to start one each time I do some work in PowerShell.

Transcripts last for the full duration of a session. In my case, because I have PowerShell open all the time, a session might last a week or more. A separate folder is used for transcripts that start each day, and transcripts are generated by user and system activity (for example, if you run the Windows printer troubleshooter, you’ll see a transcript that captures its work).

Transcripts capture details of everything that happens during a session (Figure 3). They are tremendously useful in terms of identifying what happened during a session and in tracking down why commands didn’t run successfully. As noted earlier, the logs can be copied to a repository and kept there for auditing and search purposes.

Contents of a PowerShell transcript
Figure 3: Contents of a PowerShell transcript

Given the value that you can gain from PowerShell logging, it’s something that you should consider enabling on every workstation you use to write code. Or indeed, every workstation in the organization. Hopefully you’ll never have to use the logs to track what an attacker does inside your Office 365 tenant, but at least if you have the logs, some chance exists that you might be able to discover exactly what they did.


Because PowerShell is a great way to automate common Office 365 processes or just get stuff done, we have tons of PowerShell examples in the Office 365 for IT Pros eBook. Some of them even work, which is nice…

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.