取材:Windows 测试机
DESKTOP-HT6EO5E实测 + Microsoft 官方文档
这篇不从“最佳实践”讲起。先看一段真实机器输出,因为 PowerShell 安全最容易被一句 RemoteSigned 讲虚。
一台测试机先把问题摊开
通过 OpenClaw 全能力 Windows Node 在 DESKTOP-HT6EO5E 上采集到的基线如下。命令只读取状态,没有改策略。
=== identity ===
desktop-ht6eo5e\sooua
DESKTOP-HT6EO5E
=== os ===
Microsoft Windows NT 10.0.19045.0
=== ps version ===
PSVersion 5.1.19041.6456
PSEdition Desktop
=== execution policy ===
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Bypass
CurrentUser RemoteSigned
LocalMachine Undefined
=== defender status subset ===
AMServiceEnabled : False
AntivirusEnabled : False
RealTimeProtectionEnabled : False
BehaviorMonitorEnabled : False
IoavProtectionEnabled : False
NISEnabled : False
AntispywareEnabled : False这份输出挺典型:用户级是 RemoteSigned,当前进程又是 Bypass。如果只截取前半句,会误以为“机器有脚本执行策略”;如果把进程级策略也看进去,就知道这次 Agent 执行没有被 Execution Policy 拦住。
RemoteSigned 到底挡住了什么
Microsoft 文档对 execution policy 的措辞很克制:它是 safety feature,不是限制用户动作的 security system。这个差异很重要。
Execution Policy 主要影响这些事情:
- 是否加载 PowerShell profile;
- 是否允许从磁盘运行脚本;
- 脚本是否需要签名;
- 策略在哪个 scope 生效,例如 MachinePolicy、CurrentUser、Process。
它不负责这些事情:
- 阻止用户在命令行里直接输入逻辑;
- 阻止进程以
-ExecutionPolicy Bypass启动; - 判定脚本行为是否危险;
- 约束子进程、网络、文件写入或凭据访问。
所以,RemoteSigned 更像门口的提示牌:能拦住误操作,拦不住拿着钥匙的人。
我会把 PowerShell 基线拆成三层
| 层级 | 负责的问题 | 典型证据 |
|---|---|---|
| 摩擦层 | 默认能不能直接跑脚本 | Get-ExecutionPolicy -List |
| 取证层 | 跑了什么、谁跑的、上下文是什么 | Script Block Logging、Module Logging、Transcription、进程命令行 |
| 授权层 | 哪些脚本/二进制本来就不该跑 | App Control / WDAC audit 与 enforcement |
很多环境卡在第一层:把 Execution Policy 调严一点,然后认为 PowerShell 风险已经治理了。真正出事时,调查人员需要的是第二层;想减少未知执行面,靠的是第三层。
查询命令应该长这样
这些命令适合放进基线检查脚本。它们不会修复问题,只负责把当前状态说清楚。
Get-ExecutionPolicy -List
Get-ItemProperty `
-Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging `
-ErrorAction SilentlyContinue
Get-ItemProperty `
-Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription `
-ErrorAction SilentlyContinue
Get-WinEvent -LogName Microsoft-Windows-CodeIntegrity/Operational -MaxEvents 20 |
Select-Object TimeCreated, Id, ProviderName, Message在测试机上,Script Block Logging 和 Transcription 的策略注册表项没有读到显式配置;Code Integrity 最近事件能读到 VBS policy refresh 一类记录。这说明下一步不该是“再调一个 Execution Policy”,而是先把日志和应用控制审计补上。
App Control 应该先审计,再收紧
App Control for Business / WDAC 的 audit mode 有一个实际好处:它先告诉你哪些二进制、脚本和 MSI 会被策略拒绝。Microsoft 文档说明,二进制记录在 Microsoft-Windows-CodeIntegrity/Operational,脚本和 MSI 记录在 AppLocker 的 MSI and Script 日志。
这比拍脑袋写 allowlist 可靠。推荐流程:
部署 audit mode 策略
↓
收集 CodeIntegrity / AppLocker 事件
↓
按业务 owner 标注 allow / deny / exception
↓
生成补充策略并灰度
↓
再进入 enforcement一份可以直接落地的检查单
- 记录五个 ExecutionPolicy scope,不只看 CurrentUser。
- 进程创建日志必须包含 command line、parent process、user、host、script path、hash。
- Script Block Logging 用来回答“实际执行了什么内容”。
- Transcription 用来回答“交互会话里发生了什么”。
- App Control audit 用来回答“哪些东西本应被策略拒绝”。
- 所有例外都要有 owner、过期时间和回滚路径。
这台测试机的边界
DESKTOP-HT6EO5E 是专用 Windows 测试机,当前系统为 Windows NT 10.0.19045.0 / build 19045。OpenClaw Companion 已提示这台机器不满足 MXC supported build 26300,Node Sandbox unavailable,commands run uncontained。也就是说,上面的 PowerShell 输出是宿主机无 Node Sandbox 兜底的真实状态证据,不是沙箱内执行结果。当前 Defender subset 输出为 false;这可以用于验证文章步骤、失败模式和命令证据,但不能当成生产基线示例。生产环境里,Execution Policy、PowerShell 日志、Defender、App Control 和最小权限要一起设计。
一句话收束:Execution Policy 管“别误跑”,日志管“跑了什么”,App Control 才开始接近“能不能跑”。



