We trust in security

Blog

We trust in security - Blog
Execution - Powershell (T1086)

Introduction

This post is the second about MITRE ATT&CK Enterprise Matrix covering execution techniques using Powershell.
Powershell is a commandline interface (CLI) with command and scripting capabilities. Its main targets are System Administrators to automate task and control it.
It requires .Net framework 2.0 as minimum requirement, and it was presented with Windows vista, and included on Windows 7, 8 and10, but it is not installed by default. Windows 2008 Server R1 include it on its default installation and it can be installed on Mac and Linux.
Its capabilities allow it to interact with the operative system and applications like SqlServer, IIS or Exchange.
Powershell is not only a CLI, it is a scripting language that can define variables, operators, loops, conditions, etc… One of its main characteristics is that it is object oriented, where the input and output of a cmdlet (the name that a command-module receive) is an object, nor text like other scripting language.
Powershell may also download and run executables either on disk or directly in memory without touching the disk, this may avoid some kind of Antivirus softwares.
Due to this, there are a bunch of offensive tools based on Powershell like Empire, Powersploit, PSAttack or some Metasploit payloads.
Finally, Powershell can be invoked by directly using its executable and using the interface developed using System.Management.Automation assembly exposed by .Net framework.
Regarding permissions, it requires Administrator privileges to connect to remote systems, otherwise, user privileges are enough to run it.
Most of the cases, user interaction is needed to run a loader tool that once it is launched, Powershell is invoked and run its scripts or cmdlets to perform the attack.

Attack demonstration

The initial vector for this kind of attack used to be an attachment (executable or Office documents containing macros) in an email or software downloaded from untrusted sites like piracy webs or torrents sites.
Once the user download and execute the attachment Powershell is launched, a stager is invoked on the attacker’s machine interacting with the infected machine.

missing

By executing the next command on Powershell the stager is invoked and the session begins between the attacker and victim, this could be a simulated scenario for the attack.

missing

After this PoC we create an excel file containing a macro that is launched when a button in the excel is pressed, a Powershell console is launched, and a new session is started on the empire side:

missing

missing

The next stage on a postexplotation exercise that an attacker could try in order obtain credentials on the machine that allow a lateral movement is launching tools like Mimikatz.
By using Mimikatz, the attackers can do many different attacks from extracting in memory Windows passwords tokens, generate Kerberos tickets (both, golder and silver), dumping cached credentials and so on.
In pictures bellow, we can see how, by using Powershell, LM, NTLM and SHA1 hashes are extracted for user “Master” in a Windows 7 versión, also plain text password is also extracted.

missing

Finally, the same action is reproduced in Windows 10. In this case, plain text password were not found, so only extract NTLM and SHA1 hashes:

missing

Detection

For detection part, we’ve focused on how a SIEM (Security Information and Event Management) can monitor PowerShell activity from a device that has the agent installed on it. In this case, the agent was installed on an endpoint. The main idea is not to install an agent on every organization devices but on those devices that are considered critical for organization or those that are going to be allowed to perform PowerShell actions under privileged permissions. Also, the agent could be installed on devices that should be monitored for security or/and compliance.
In this case, we are going to use Sumologic, a cloud native SIEM platform, to monitor a Windows platform behavior and perform queries that would become alerts when there are a series of cases that would be suspicious of malicious activity. This queries would perform more actions than alerting, such as launch a local based script where the agent is installed, or perform a connection to a third-party service that can take other kind of actions previously configured, like an Azure connection, an Slack message, an AWS Lambda function or a ServiceNow connection.
Basic PowerShell logging is available for all Windows 7 versions, Windows Server 2008 and above, but advanced event audit is only available since PowerShell version 4:
When collecting Windows events, we should consider that there are both of them that are important for PowerShell exploitation monitoring. According to the Win32_NTLogEvent, both sources are:

  • SourceName = “Microsoft-Windows-PowerShell/Operational”
  • SourceName = “PowerShell”

We can find both sources inside events related to PowerShell actions, which depending on its version, would correspond to the following Event ID’s:

  • Windows 7 and Server 2008 and above:
    • PowerShell version 2 thru 4:
      • Windows PowerShell log
        • Event ID’s: 400, 500, 501 and 800.
  • Windows 8.1 and Server 2012 and above:
    • PowerShell version 3 and 4:
      • Windows PowerShell log
        • Event ID’s: 400, 500, 501 and 800
      • Microsoft-Windows-PowerShell/Operational log
        • Event ID: 4104
  • Windows 7 and Server 2008 and above:
    • PowerShell version 5:
      • Windows PowerShell log
        • Event ID’s: 200, 400, 500 and 501
      • Microsoft-Windows-PowerShell/Operational log
        • Event ID: 4100, 4103 and 4104

There are other Event ID’s related with PowerShell activity, such as 4105 and 4106, but they are very noisy and not such important for security monitoring.
On the other hand, there are another Event ID’s that we must consider before start the event analysis:

Event ID Log Name Description
104 System The Windows PowerShell or PowerShell Operational log was cleared.
4657 Security Log A Registry value was modified.
4688 Security Log New Process Name or Process Command Line was executed.

Once this EventIDs are familiar to us, we can proceed to create rules for our SIEM, extracting the desired fields. In Sumologic, we can do this with parse operator:

   _sourcename = "Microsoft-Windows-PowerShell/Operational"
    | parse "CategoryString = \"*\";" as category_string nodrop
    | parse "Type = \"*\";" as event_type nodrop
    | parse "Logfile = \"*\";\n" as logfile nodrop
    | parse "EventIdentifier = *;\n" as event_id nodrop
    | parse "SourceName = \"*\";\n" as srcname nodrop
    | parse "User = \"*\";\n" as user nodrop

For better accuracy, it’s very important to know that this EventID wouldn’t be always related to malicious actions, because them are also related to legit activities.
Is for this why we should consider some actions that the PowerShell console performs focusing on the executed commands.
A very clear example of suspicious behavior would be the encoding of the launched PowerShell command, because it makes no sense that an admin who performs legit actions needs to encode its commands. For this purpose, we need to take care of the Host Application field of the PowerShell event log, which has an output like this:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand

As we can see, an encoded command is launched using the -EncodedCommand flag, but we also can find the -encoded flag. So, we are going to look for “encoded” string inside the Host Application field that will allow us to detect this obfuscation attempts. It’s very important to convert all to lower case, avoiding possible sensibilities. We can do this in Sumologic as follows:

    | parse regex "Host Application =(?<host_app> .*?) [A-Za-z0-9]+\/[A-Za-z0-9\/]+\=" multi nodrop
  | toLowerCase(host_app) as host_app | where host_app matches "*encoded*"

Sumologic also allow us to find and decode base64 strings. To perform this action, we can use this instruction in our query.

    | parse regex " (?<base64_command>[A-Za-z0-9]+\/[A-Za-z0-9\/]+\=)" multi nodrop
    | base64Decode(base64_command) as base_64_command_decoded
    | replace(base_64_command_decoded,/\x00/,"") as base_64_command_decoded
    | toLowerCase(base_64_command_decoded) as base_64_command_decoded_normalized
    | where !(isEmpty(base_64_command_decoded_normalized))

Note that decoding is done from base64 using UTF-8 while Powershell uses UTF-16LE format. This is why we have removed the byte /\x00/, which differences UTF-16 against UTF-8. Once decoded, we could look for some PowerShell calls that could be suspicious of malicious exploitation. Some of them are:

  • Set-ExecutionPolicy, Set-MasterBootRecord
  • Get-WMIObject, Get-GPPPassword, Get-Keystrokes, Get-TimedScreenshot, Get-VaultCredential, Get-ServiceUnquoted, Get-ServiceEXEPerms, Get-ServicePerms, Get-RegAlwaysInstallElevated, Get-RegAutoLogon, Get-UnattendedInstallFiles, Get-Webconfig, Get-ApplicationHost, Get-PassHashes, Get-LsaSecret, Get-Information, Get-PSADForestInfo, Get-KerberosPolicy, Get-PSADForestKRBTGTInfo, Get-PSADForestInfo, Get-KerberosPolicy
  • Invoke-Command, Invoke-Expression, iex, Invoke-Shellcode, Invoke–Shellcode, Invoke-ShellcodeMSIL, Invoke-MimikatzWDigestDowngrade, Invoke-NinjaCopy, Invoke-CredentialInjection, Invoke-TokenManipulation, Invoke-CallbackIEX, Invoke-PSInject, Invoke-DllEncode, Invoke-ServiceUserAdd, Invoke-ServiceCMD, Invoke-ServiceStart, Invoke-ServiceStop, Invoke-ServiceEnable, Invoke-ServiceDisable, Invoke-FindDLLHijack, Invoke-FindPathHijack, Invoke-AllChecks, Invoke-MassCommand, Invoke-MassMimikatz, Invoke-MassSearch, Invoke-MassTemplate, Invoke-MassTokens, Invoke-ADSBackdoor, Invoke-CredentialsPhish, Invoke-BruteForce, Invoke-PowerShellIcmp, Invoke-PowerShellUdp, Invoke-PsGcatAgent, Invoke-PoshRatHttps, Invoke-PowerShellTcp, Invoke-PoshRatHttp, Invoke-PowerShellWmi, Invoke-PSGcat, Invoke-Encode, Invoke-Decode, Invoke-CreateCertificate, Invoke-NetworkRelay
  • EncodedCommand, New-ElevatedPersistenceOption, wsman, Enter-PSSession, DownloadString, DownloadFile
  • Out-Word, Out-Excel, Out-Java, Out-Shortcut, Out-CHM, Out-HTA, Out-Minidump, HTTP-Backdoor, Find-AVSignature, DllInjection, ReflectivePEInjection, Base64, System.Reflection, System.Management
  • Restore-ServiceEXE, Add-ScrnSaveBackdoor, Gupt-Backdoor, Execute-OnTime, DNS_TXT_Pwnage, Write-UserAddServiceBinary, Write-CMDServiceBinary, Write-UserAddMSI, Write-ServiceEXE, Write-ServiceEXECMD
  • Enable-DuplicateToken , Remove-Update, Execute-DNSTXT-Code, Download-Execute-PS, Execute-Command-MSSQL, Download_Execute, Copy-VSS, Check-VM, Create-MultipleSessions, Run-EXEonRemote, Port-Scan, Remove-PoshRat, TexttoEXE, Base64ToString, StringtoBase64, Do-Exfiltration, Parse_Keys, Add-Exfiltration, Add-Persistence, Remove-Persistence, Find-PSServiceAccounts, Discover-PSMSSQLServers, Discover-PSMSExchangeServers, Discover-PSInterestingServices, Discover-PSMSExchangeServers, Discover-PSInterestingServices

Any Set-, Get-, Invoke-, Out-, Write- and all PowerShell arguments can be shortened like iex, ex, etc so be careful and look for these too.
To perform a detection for this kind of calls, we should extract all encoded commands, decode them and then look on these calls. Obviously, we can find more techniques to obfuscate code, like the one that consists on alternating lower and upper cases letters, so it’s very important to convert all text to lower case to avoid this.
At this point, it’s very important not to look for only one of them but, anyway, it’s quite suspicious that the PowerShell command comes encoded so let’s take a look on the following Yara rule, that we are going to implement to make a more accurate detection:

    rule PowerShellSuspiciousStrings
    {
        strings:
        $ps1 = "powershell" nocase wide ascii
        $ps2 = "IEX" nocase wide ascii
        $ps3 = "new-object" nocase wide ascii
        $ps4 = "webclient" nocase wide ascii
        $ps5 = "downloadstring" nocase wide ascii
        $ps6 = "Hidden" nocase wide ascii
        $ps7 = "invoke" nocase wide ascii
        $ps8 = "Get-Random -input" nocase wide ascii
        $ps9 = "bypass" nocase wide ascii
        $ps10 = "shellcode" nocase wide ascii
        $ps11 = "Enter-PSSession" nocase wide ascii
        $ps12 = "-NoP" nocase wide ascii
        $ps13 = "-Enc" nocase wide ascii
        $ps14 = "-NonI" nocase wide ascii
        $ps15 = "downloadfile" nocase wide ascii
        $ps16 = "Invoke-Expression" nocase wide ascii
        $ps17 = "Start-Process" nocase wide ascii
        $ps18 = "ShellExecute" nocase wide ascii
        $ps19 = "[System.Convert]::" nocase wide ascii
        $ps20 = "FromBase64String(" nocase wide ascii
        $ps21 = "New-Object System.IO." nocase wide ascii
        $ps22 = "[System.Net." nocase wide ascii
        $ps23 = "System.Reflection.AssemblyName" nocase wide ascii
        $ps24 = "cG93ZXJzaGVsbC" nocase wide ascii
        $ps25 = "UG93ZXJTaGVsbC" nocase wide ascii
    condition:
        4 of them and file_type contains "text" and new_file and positives > 0 and positives < 15
    }

In this case, the condition specifies that there should be at least for of the possible cases, so we are going to translate this Yara rule to Sumologic query language for search for these different options.

    | where  base_64_command_decoded_normalized matches "*powershell*" or base_64_command_decoded_normalized matches "*iex*" or base_64_command_decoded_normalized matches "*new-object*" or base_64_command_decoded_normalized matches "*webclient*" or base_64_command_decoded_normalized matches "*downloadstring*" or base_64_command_decoded_normalized matches "*hidden*" or base_64_command_decoded_normalized matches "*invoke*" or base_64_command_decoded_normalized matches "*get-random -input*" or base_64_command_decoded_normalized matches "*bypass*" or base_64_command_decoded_normalized matches "*shellcode*" or base_64_command_decoded_normalized matches "*enter-pssession*" or base_64_command_decoded_normalized matches "*-nop*" or base_64_command_decoded_normalized matches "*-enc*" or base_64_command_decoded_normalized matches "*-noni*" or base_64_command_decoded_normalized matches "*downloadfile*" or base_64_command_decoded_normalized matches "*invoke-expression*" or base_64_command_decoded_normalized matches "*start-process*" or base_64_command_decoded_normalized matches "*shellexecute*" or base_64_command_decoded_normalized matches "*[system.convert]::*" or base_64_command_decoded_normalized matches "*frombase64string(*" or base_64_command_decoded_normalized matches "*new-object system.io.*" or base_64_command_decoded_normalized matches "*[system.net.*" or base_64_command_decoded_normalized matches "*system.reflection.assemblyname*" or base_64_command_decoded_normalized matches "*cg93zxjzagvsbc*" or base_64_command_decoded_normalized matches "*ug93zxjtagvsbc*"

The last part of the condition should be written three more times to exactly translate the Yara rule, but in this case, we aren’t going to do that because an encoded PowerShell command is a very clear indicator that a Security Analyst must take a look on this if the rule matches once after the command decoding process.

So, for the exploitation events that we’ve generated before, we can verify that all of them are encoded PowerShell commands:

missing

Here we can see the results of the query:

missing

As we can see, we can identify all previously explained fields:

  • Logfile: Microsoft-Windows-PowerShell/Operational
  • EventID: 4103
  • Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noP -sta -w 1 -encoded

We also can see that this command performs an attempt to connect to an IP for download data, which would be the malicious payload.

Anyway, we can find commands that aren’t encoded. We cand find those ones inside the “Message” field of the event log. We can extract them using the following sentence:

    | parse "Message = \"*\n};" as pscommands nodrop

Then, we’re going to apply our detection query again, which previously has been converted to lower case to avoid case sensibility. This time, we need to be more restrictive with our detection rule, because these commands are written in clear text and in this case, they could give us some false positives, because some calls could be legit. This is why we are going to look for two detections, but probably we would need to look for three or more:

    | toLowerCase(pscommands) as pscommands
    | where pscommands matches "*powershell*" and pscommands matches "*iex*" and (pscommands matches "*new-object*" or pscommands matches "*webclient*" or pscommands matches "*downloadstring*" or pscommands matches "*hidden*" or pscommands matches "*invoke*" or pscommands matches "*get-random -input*" or pscommands matches "*bypass*" or pscommands matches "*shellcode*" or pscommands matches "*enter-pssession*" or pscommands matches "*-nop*" or pscommands matches "*-enc*" or pscommands matches "*-noni*" or pscommands matches "*downloadfile*" or pscommands matches "*invoke-expression*" or pscommands matches "*start-process*" or pscommands matches "*shellexecute*" or pscommands matches "*[system.convert]::*" or pscommands matches "*frombase64string(*" or pscommands matches "*new-object system.io.*" or pscommands matches "*[system.net.*" or pscommands matches "*system.reflection.assemblyname*" or pscommands matches "*cg93zxjzagvsbc*" or pscommands matches "*ug93zxjtagvsbc*") and (pscommands matches "*new-object*" or pscommands matches "*webclient*" or pscommands matches "*downloadstring*" or pscommands matches "*hidden*" or pscommands matches "*invoke*" or pscommands matches "*get-random -input*" or pscommands matches "*bypass*" or pscommands matches "*shellcode*" or pscommands matches "*enter-pssession*" or pscommands matches "*-nop*" or pscommands matches "*-enc*" or pscommands matches "*-noni*" or pscommands matches "*downloadfile*" or pscommands matches "*invoke-expression*" or pscommands matches "*start-process*" or pscommands matches "*shellexecute*" or pscommands matches "*[system.convert]::*" or pscommands matches "*frombase64string(*" or pscommands matches "*new-object system.io.*" or pscommands matches "*[system.net.*" or pscommands matches "*system.reflection.assemblyname*" or pscommands matches "*cg93zxjzagvsbc*" or pscommands matches "*ug93zxjtagvsbc*")

Here we can see the results of previous query:

missing

In this case, we can see that our rule has detected a Mimikatz execution.

Other kind of events that we should monitor carefully are the following ones:

  • Security Log (4688):
    • Looks for Execution Policy bypass and No Profile executions (less noisy).
  • PowerShellOperational Log (4100):
    • Looks for Execution Policy bypass and No Profile executions (less noisy).
  • PowerShell Operational Log (4104):
    • Looks for PowerShell modules greater than 1000 characters (PowerShell v4-5).
  • Security Log (4688):
    • Looks for PowerShell using a large amount of odd characters (e.g. ticks ‘ and Percent %).
  • Windows PowerShell Log (400):
    • Looks for PowerShell using a large amount of odd characters (e.g. ticks ‘ and Percent %) (PowerShell v2-5).
  • Security Log (4688):
    • Looks for PowerShell web downloads.
  • PowerShell Operational Log (4104):
    • Looks for PowerShell web downloads (PowerShell v4-5).
  • Windows PowerShell Log (400):
    • Looks for PowerShell web downloads (PowerShell v2-5).
  • Windows PowerShell Log (400):
    • Looks for Base64 module data, isolate the Base64 for conversion (PowerShell v2-5).
  • Sysmon Log (7):
    • Look for PowerShell System.Management.Automation loading NOT by PowerShell.
  • Some DLLs:
    • System.Management.Automation.dll
    • System.Management.Automation.ni.dll
    • System.Reflection.dll
  • WMI Calls (they are not necessarily malicious, but could indicate WMI is being used in an attack):
    • Invoke-WMIMethod
    • Get-WMIObject

Mitigation

It may be possible to remove PowerShell from systems when not needed, but a review should be performed to assess the impact to an environment, since it could be in use for many legitimate purposes and administrative functions. When PowerShell is necessary, restrict PowerShell execution policy to administrators and to only execute signed scripts. Be aware that there are methods of bypassing the PowerShell execution policy, depending on environment configuration. Disable/restrict the WinRM Service to help prevent uses of PowerShell for remote execution.