What traces does PowerShell leave behind when it was ran from the macro?
Well, beside the event 4688
in the Security
event log which might contain the command line used to run PowerShell, we have nothing (see episode S01E03. In a way, we are kinda lucky because the aforementioned event does contain the command sent to PowerShell in base64 and this command was not running outside code.
powershell -encodedcommand SW52b2tlLVdtaU1ldGhvZCAtQ2xhc3MgU3RkUmVnUHJvdiAtTmFtZSBTZXRTdHJpbmdWYWx1ZSAtQXJndW1lbnRMaXN0ICgyMTQ3NDgzNjQ5LCJTT0ZUV0FSRSIsIlllcyIsIkNhbkJlRGVsZXRlZCIpIDsgSW52b2tlLVdlYlJlcXVlc3QgLVVyaSAiaHR0cDovL3BlcmR1LmNvbSI=
Which translates to:
Invoke-WmiMethod -Class StdRegProv -Name SetStringValue -ArgumentList (2147483649,"SOFTWARE","Yes","CanBeDeleted") ; Invoke-WebRequest -Uri "http://perdu.com"
The decoding could be done using the following PowerShell code:
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String("SW52b2tlLVdtaU1ldGhvZCAtQ2xhc3MgU3RkUmVnUHJvdiAtTmFtZSBTZXRTdHJpbmdWYWx1ZSAtQXJndW1lbnRMaXN0ICgyMTQ3NDgzNjQ5LCJTT0ZUV0FSRSIsIlllcyIsIkNhbkJlRGVsZXRlZCIpIDsgSW52b2tlLVdlYlJlcXVlc3QgLVVyaSAiaHR0cDovL3BlcmR1LmNvbSI="))
.
So we know what ran in our case, but what if we didn't have this Command Line auditing enabled (which is not enabled default), or what if the command was just a web call to grab content from the internet and run it? In that case the 4688
would be a starting place, but we wouldn't get much out of it.
The transcript will store all inputs and outputs sent to the console. It is not enabled by default. Can be enabled on a session with Start-Transcript
or at the system level:
Local Computer Policy
↘ Computer Configuration
↘ Administrative Templates
↘ Windows Components
↘ Windows PowerShell
➡ Turn on PowerShell Transcription
It does not show the code of the function or the code of a script if they are called from the console. For example, if you enabled the transcription feature and run ./MyScript.ps1
you will see that the script was launched in the transcript but you will not see the content of the script. Same goes with functions (unless the content of the function was created within the console session, but that's a corner case).
If one had enabled PowerShell transcription, this is what we would have seen:
In our case, if the Module Logging was enabled (for the Microsoft.PowerShell.* module at least) we would have seen a series of event 800
in the Windows PowerShell
event log, such as:
This feature can be enabled with the following parameter:
Local Computer Policy
↘ Computer Configuration
↘ Administrative Templates
↘ Windows Components
↘ Windows PowerShell
➡ Turn on Module Logging
💡 Note that it can be a very verbose logging. If enabled at all time, it will raise challenges to be able to collect, parse and analyze the data it generates (even if it is collected by a SIEM).
Speaking of verbose logs, the code block logging can be enabled with the following parameter:
Local Computer Policy
↘ Computer Configuration
↘ Administrative Templates
↘ Windows Components
↘ Windows PowerShell
➡ Turn on Script Block Logging
If enabled (not the case by default) it would generate an event 4104
in the Microsoft-Windows-PowerShell/Operational
event log:
It shows the decode full script.
Very practical to know what happened in an interactive terminal. Not useful in our case as the macro did not run into an interactive PowerShell terminal.
This file can be found under <user profile>/AppData/Roaming/Microsoft/WindowsPowerShell/PSReadLine/ConsoleHost_history.txt
. It seems to roll over after 20kb of size.
Event ID | Event Log | Enabled by default | Builtin feature | Notes |
---|---|---|---|---|
800 |
Windows PowerShell |
No | Yes | Could be very verbose |
4104 |
Microsoft-Windows-PowerShell/Operational |
No | Yes | Could be very verbose |
To which we can add the transcription feature output which is also not enabled by default.