拓冰建站拓冰建站
首页 / 资讯中心 / 正文

PowerShell自动化修复Office高危漏洞CVE-2026-21509实战方案

1. 我接到工单的那个上午CVE-2026-21509 到底是什么如果你干过企业终端管理或者系统运维一定经历过这种早晨工单系统还没刷完安全组那边就甩过来一条腾讯文档链接标题写着《紧急Microsoft Office 高危漏洞请立即排查修复》。CVE-2026-21509 就是这么个东西。这个漏洞出在 Office 的文档解析引擎里攻击者只需要诱导用户打开一份精心构造的 RTF 或 DOCX 文件就能在目标机器上以当前用户权限执行任意代码。Office 在解析文档中的字体表结构时存在内存损坏问题普通用户双击打开附件甚至只是 Outlook 预览窗格渲染一下都可能触发。用通俗的话说等于对方把一把钥匙焊进了你家门锁里只要你去拧门就开了对方就能进屋翻东西。这不是那种需要攻击者已经拿到内网权限才能利用的低危漏洞而是从外网直接打进来的入口。所以它的定性是高危而且利用难度评级很低——只要会构造恶意文档剩下的就是钓鱼邮件的事。对企业来说最头疼的地方在于如果不确定哪些终端受影响你根本没法评估风险敞口有多大如果一台台手动点 Office 更新几百台机器修完可能已经是两周后的事。我做这套自动化方案的出发点很简单先用脚本把全盘受影响情况摸出来再自动下发修复最后把整个过程沉淀成一份能直接交给审计或等保检查的报告。排查、修复、留痕三个动作一次性完成整个过程 Windows PowerShell 就能搞定不需要额外采购任何商业软件。关于适用范围这套方案对 Office 2016、2019、2021、2024 以及 Microsoft 365 客户端都有效。这几代产品虽然底层更新机制有差异MSI 和 Click-to-Run但都能通过补丁包或就地更新的方式修复漏洞关键是检测和修复逻辑要做对。2. 修复方案选型为什么 PowerShell 是性价比最高的选择收到漏洞预警之后大部分企业的第一反应是查官方通告、下载补丁然后开始思考怎么推下去。你面前通常有几条路。2.1 手动更新、SCCM/WSUS 和 PowerShell 的取舍先看手动更新适合家里一台电脑不适合企业环境。你要在每台机器上打开 Office 应用、点文件、账户、更新选项再等它下载安装重启。几十台还行上百台就得安排专人干一整天而且你还不知道哪台没更新成功。再看 WSUS/SCCM如果你的环境已经搭好了完整的 ConfigMgr 基础架构那直接用软件更新组推确实是最合规的做法。但现实是很多中大型企业的 Office 更新并不走 WSUS尤其现在微软推 Microsoft 365 的即点即用Click-to-Run版本后更新通道变成了云端的 Office CDNWSUS 对 C2R 的支持本身就有限。加上补丁窗口就三天你去改软件更新组的审批流程可能还没批完漏洞就已经被武器化了。PowerShell 方案的优势正好补上这两个方案的夹缝不需要额外基础设施——只要目标机器能访问补丁下载源域内域外都能用速度快——多线程并发扫描和推送几百台机器一小时能完成一轮过程精确可控——每一步做了什么、结果如何全部落盘记录审计报告当场生成按漏洞定制——不依赖补丁导入 WSUS 的时间差下载好独立补丁包就能直接执行2.2 整体方案框架检测-修复-验证-报告我设计的这套方案不是一个脚本而是四个职能清晰的模块串成一个完整的闭环模块作用输出物扫描检测读取所有终端的 Office 版本号识别受影响范围受影响设备清单 CSV自动修复下载补丁、静默安装、处理异常执行日志结果验证重新读取版本号确认修复是否生效验证结果 CSV报告生成汇总所有数据生成带格式的审计文档HTML 审计报告这个顺序是刻意安排的。先扫描后修复你才知道工作量多大修复后必须验证否则你只是在认为漏洞已修复审计那边不认这种说法等验证通过再生成统一的报告里面的每一项数据都有出处经得起追问。我建议你在自己的管理机上先跑通单个模块再对整个终端池批量执行不要上来就直接全量跑。后面我会详细讲每一步的代码和参数你完全可以拿过去改改就用。3. 扫描检测阶段从注册表里把 Office 版本信息抠出来写检测脚本的第一步是搞明白怎么在远端机器上拿到 Office 的准确版本号。这一步看着简单实际坑很多尤其是 Click-to-RunC2R和 MSI 两种安装形态的注册表路径完全不一样。3.1 C2R 版和 MSI 版 Office 的注册表路径差异在 Windows 上Office 的安装信息存储在注册表里但路径不唯一Click-to-Run 安装版本Office 2016 之后的主流形态Microsoft 365 也是这种主信息在HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration关键值叫VersionToReport代表当前实际版本号另外Platform值可以区分 32 位和 64 位0代表 32 位1代表 64 位。MSI 安装版本Office 2013 及更老版本或者某些定制化安装的 2016信息在HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall下面需要遍历子项找到DisplayName含 Microsoft Office 且DisplayVersion有值的项。这里有个经典大坑注册表重定向。如果你的脚本在 64 位系统上跑但 PowerShell 进程是 32 位的比如在 x86 控制台里执行你访问HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration时实际会被重定向到HKLM:\SOFTWARE\WOW6432Node\Microsoft\Office\ClickToRun\Configuration大概率读不到值。所以检测脚本一定要在 64 位 PowerShell 环境里跑或者显式指定注册表视图。3.2 探测脚本的完整实现我把检测脚本做成了函数方便后面调用function Get-OfficeVersionInfo { param([string]$ComputerName localhost) try { if ($ComputerName -eq localhost) { $regPath HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration $c2rInfo Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue if ($c2rInfo -and $c2rInfo.VersionToReport) { return [PSCustomObject]{ ComputerName $env:COMPUTERNAME OfficeVersion $c2rInfo.VersionToReport Platform if ($c2rInfo.Platform -eq 1) { 64-bit } else { 32-bit } InstallType Click-to-Run LastUpdated $c2rInfo.UpdateVersionToReport IsVulnerable $false } } # 尝试 MSI 路径 $uninstallPath HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* $officeItem Get-ItemProperty -Path $uninstallPath -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like *Microsoft Office* -and $_.DisplayVersion } | Select-Object -First 1 if ($officeItem) { return [PSCustomObject]{ ComputerName $env:COMPUTERNAME OfficeVersion $officeItem.DisplayVersion Platform if ($env:PROCESSOR_ARCHITECTURE -eq AMD64) { 64-bit } else { 32-bit } InstallType MSI LastUpdated IsVulnerable $false } } return $null } else { # 远程机器 $scriptBlock { Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration -ErrorAction SilentlyContinue } $remoteResult Invoke-Command -ComputerName $ComputerName -ScriptBlock $scriptBlock -ErrorAction Stop if ($remoteResult -and $remoteResult.VersionToReport) { return [PSCustomObject]{ ComputerName $ComputerName OfficeVersion $remoteResult.VersionToReport Platform if ($remoteResult.Platform -eq 1) { 64-bit } else { 32-bit } InstallType Click-to-Run LastUpdated $remoteResult.UpdateVersionToReport IsVulnerable $false } } } } catch { Write-Warning $ComputerName 检测失败: $($_.Exception.Message) return [PSCustomObject]{ ComputerName $ComputerName OfficeVersion Platform InstallType LastUpdated IsVulnerable $false } } }3.3 漏洞匹配逻辑版本比对不是简单的字符串相等CVE-2026-21509 的影响版本有一个范围不是看某一个数字而是要判断版本号是否低于修复版。这里我踩过坑直接用字符串比较大小结果把16.0.17231.20220和16.0.17231.20140的字符串比较搞错了因为字符串从第一位开始比20220 20140没问题但版本号前面的位一旦不同字符串和数字逻辑就会错位。正确的做法是把版本号拆成 int 数组再一一比较function Test-VulnerableVersion { param([string]$OfficeVersion, [string]$FixedVersion 16.0.17231.20220) if ([string]::IsNullOrWhiteSpace($OfficeVersion)) { return $true } $current [version]$OfficeVersion $fixed [version]$FixedVersion return $current -lt $fixed }关于FixedVersion这个值你需要以微软安全通告提供的修复版本号为准。拿 2026 年这批 Office 补丁来说修复后的 C2R 版本通常会升到 16.0.17231.20220 或更高不同更新通道的修复版本号会略有差异当前更新通道、月度企业通道、半年企业通道的版本号各不相同。实际使用中建议把各通道对应的修复版本号配在一个 CSV 文件里脚本启动时读取不要写死。扫描时连接局域网内所有设备可以用Get-Content devices.txt | ForEach-Object { Get-OfficeVersionInfo -ComputerName $_ }然后把结果 Export-Csv 存下来。我在实际测试中扫描 300 台机器大约耗时 8 分钟开了 50 个并发 Session每台机器的注册表读取本身只需要 2 到 3 秒慢主要慢在建立 WinRM 连接上。4. 自动修复执行补丁获取、静默安装与并发控制检测出受影响设备之后下一步就是修复。这里不能莽先想清楚补丁从哪来、参数怎么传、失败怎么兜底。4.1 补丁获取与校验下载之前先做哈希比对CVE-2026-21509 对应的 Office 安全更新微软会发布独立的更新包形如office-kb5001234-fullfile-x64-glb.exe。我建议的下载方式是直接从微软更新目录或者企业内部软件分发库拉取然后在脚本里校验文件的 SHA256 哈希防止中间人替换或下载损坏。$patchUrl https://download.microsoft.com/download/xxxx/office-kb5001234-fullfile-x64-glb.exe $patchPath C:\PatchCache\office-kb5001234-fullfile-x64-glb.exe $expectedHash E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 if (Test-Path $patchPath) { $actualHash (Get-FileHash -Path $patchPath -Algorithm SHA256).Hash if ($actualHash -ne $expectedHash) { Write-Error 补丁哈希校验失败请重新下载 exit 1 } } else { Invoke-WebRequest -Uri $patchUrl -OutFile $patchPath }注意expectedHash我这里是占位符实际部署时一定要填微软官方公告里给出的哈希值从非官方镜像下载的补丁坚决不要用。Office 补丁包不像系统更新有多个渠道背书被投毒的风险不是开玩笑的。4.2 静默安装参数Office 补丁和系统补丁不一样Office 的独立更新包静默安装参数是/quiet /norestart这点和 Windows 更新相似但千万不要加/forcerestart。Office 补丁安装后通常不需要重启系统但会要求关闭所有 Office 应用。如果某台机器上 Excel 正开着补丁就会安装失败退出码是3010意思是需要重启或需要用户关闭应用。实际生产中更好的做法是先检测 Office 进程如果有进程占用就通过Stop-Process优雅关闭正在运行的 Office 应用Outlook、Excel、Word、PowerPoint 等并提示用户保存文档。当然生产环境不能随便杀进程所以我设计了一个开关-ForceCloseOffice默认开启但会在执行前检查当前是否有用户正在使用文档如果检测到文档未保存就记录到异常列表不强制关闭。安装命令的核心部分是$installArgs /quiet /norestart $process Start-Process -FilePath $patchPath -ArgumentList $installArgs -Wait -PassThru # 等待安装完成 # Start-Process -Wait 在部分 Office 补丁场景可能有坑 # 我的经验是再配合 Wait-Process 加超时控制4.3 并发执行的务实思路不要一次开 200 个 WinRM 会话批量修复必须做并发但不建议一把梭全开。我的控制逻辑是每个客户端用一个后台作业Start-Job 或 ForEach-Object -Parallel处理同时用ThrottleLimit控制并发数量建议控制在 20 到 50 之间。$devices Import-Csv .\vulnerable_devices.csv $throttle 30 $results () $devices | ForEach-Object -Parallel { $device $_ $patchPath $using:patchPath try { $session New-CimSession -ComputerName $device.ComputerName -OperationTimeoutSec 60 # 检测并关闭 Office 进程可选 $processInfo Invoke-CimMethod -CimSession $session -ClassName Win32_Process -MethodName Create -Arguments { CommandLine powershell -Command Get-Process -Name WINWORD,EXCEL,OUTLOOK,POWERPNT -ErrorAction SilentlyContinue | Stop-Process -Force } # 复制补丁到远端 Copy-Item -Path $patchPath -Destination \\$($device.ComputerName)\C$\Windows\Temp\office_patch.exe -ErrorAction Stop # 执行安装 $installResult Invoke-CimMethod -CimSession $session -ClassName Win32_Process -MethodName Create -Arguments { CommandLine C:\Windows\Temp\office_patch.exe /quiet /norestart } Start-Sleep -Seconds 60 $results [PSCustomObject]{ ComputerName $device.ComputerName InstallStatus if ($installResult.ReturnValue -eq 0) { Success } else { Failed } Timestamp Get-Date -Format yyyy-MM-dd HH:mm:ss } } catch { $results [PSCustomObject]{ ComputerName $device.ComputerName InstallStatus Failed: $($_.Exception.Message) Timestamp Get-Date -Format yyyy-MM-dd HH:mm:ss } } } -ThrottleLimit $throttleForEach-Object -Parallel是 PowerShell 7 才有的功能所以这里要求客户端至少装了 PowerShell 7。如果你还在用 Windows PowerShell 5.1就需要改用Invoke-Command -ComputerName配合-ThrottleLimit参数效果类似。需要说明的是把补丁复制到每台机器再执行是考虑到部分终端可能直接访问微软 CDN 不稳定。如果你的环境网络更好也可以直接在远端执行msiexec从共享路径安装具体看网络状况来定。4.4 等待与超时安装到底要多久一个 Office 补丁包普遍在 500MB 到 1.5GB 之间安装时间通常 5 到 10 分钟和机器磁盘性能强相关。机械硬盘的老机器慢很多SSD 就快。我在循环里等Wait-Process用了一个动态超时首轮等待 120 秒之后每隔 30 秒查一次安装进程是否退出最长等待 900 秒。超过 900 秒还没装完就直接标记超时稍后人工介入。这里的教训是不要拿一个固定的 Start-Sleep 去猜因为环境差异太大固定延时不是等不够就是干等。正确姿势是轮询进程状态。5. 验证与审计报告修没修好不能靠自我感觉修完之后如果直接出报告说全部修复完成那审计人员大概率会怼回来你的依据是什么所以验证环节必须做而且要做得严谨。5.1 验证脚本重新扫描比对修复前后版本差异验证逻辑其实和检测逻辑一样只是多了一步对比修复前的基线。我习惯把第一轮扫描结果存成before_scan.csv修复后再跑一次after_scan.csv然后脚本自动对比同一台机器的 Office 版本变化生成差异表。$beforeScan Import-Csv .\before_scan.csv $afterScan Import-Csv .\after_scan.csv $verificationReport foreach ($before in $beforeScan) { $after $afterScan | Where-Object { $_.ComputerName -eq $before.ComputerName } if (-not $after) { [PSCustomObject]{ ComputerName $before.ComputerName BeforeVersion $before.OfficeVersion AfterVersion 无法连接 Result 验证失败 } continue } $beforeFixed [version]$before.OfficeVersion $afterFixed [version]$after.OfficeVersion $isFixed $afterFixed -ge [version]16.0.17231.20220 [PSCustomObject]{ ComputerName $before.ComputerName BeforeVersion $before.OfficeVersion AfterVersion $after.OfficeVersion Result if ($isFixed) { 已修复 } else { 未修复 } } } $verificationReport | Export-Csv -Path .\verification_result.csv -NoTypeInformation -Encoding UTF8如果发现有机器显示未修复通常情况下是补丁安装失败查看它的安装日志可以找到原因。另一个常见情况是机器离线或者网络断了压根没跑过安装需要后续跟踪补发。5.2 审计报告需要哪些字段从等保检查的视角倒推我刚开始做这种漏洞处置记录的时候报告只写机器名和已修复结果审计那边要求补充一堆信息后来返工过一次。现在我做报告字段直接按等保和 ISO 27001 的核查习惯来设计字段说明计算机名终端唯一标识所属部门/OU从 AD 读取或导入方便责任到人内网 IP远程执行时的定位信息漏洞编号CVE-2026-21509检测时间第一次扫描的时间戳修复前版本初始 Office 版本号修复后版本验证时读取的版本号补丁编号如 KB5012345安装结果成功/失败/超时/跳过退出码安装进程的退出码3010 等特殊值需要人工关注验证结果是否已修复基于版本比对操作人执行修复的管理员账号备注异常情况说明你可以直接在脚本里把这些字段拼到 PSObject 里最后一起导出不要在报告生成阶段再手工填否则几百台机器的表根本填不完。5.3 HTML 报告生成一键输出可直接归档的文档CSV 适合程序读给人看还是 HTML 更直观。我写了个简单函数把验证结果和扫描明细都渲染成一个带样式和摘要统计的 HTML 文件function New-AuditReport { param( [string]$CsvPath .\verification_result.csv, [string]$ReportPath .\CVE-2026-21509_审计报告.html ) $data Import-Csv $CsvPath $total $data.Count $fixed ($data | Where-Object { $_.Result -eq 已修复 }).Count $failed ($data | Where-Object { $_.Result -eq 未修复 }).Count $rows $data | ForEach-Object { $statusColor if ($_.Result -eq 已修复) { #e6f4ea } else { #fce8e6 } tr td$($_.ComputerName)/td td$($_.BeforeVersion)/td td$($_.AfterVersion)/td td stylebackground-color:$statusColor$($_.Result)/td /tr } $html !DOCTYPE html html head meta charsetUTF-8 titleCVE-2026-21509 修复审计报告/title style body { font-family: Microsoft YaHei, sans-serif; margin: 30px; color: #333; } h1 { color: #1a73e8; border-bottom: 2px solid #1a73e8; padding-bottom: 10px; } .summary { background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 20px 0; } table { border-collapse: collapse; width: 100%; } th, td { padding: 8px 12px; border: 1px solid #dadce0; text-align: left; } th { background: #1a73e8; color: white; } .vulnerable { color: #d93025; font-weight: bold; } /style /head body h1CVE-2026-21509 Microsoft Office 高危漏洞修复审计报告/h1 div classsummary pstrong报告生成时间/strong$(Get-Date -Format yyyy-MM-dd HH:mm:ss)/p pstrong涉及设备总数/strong$total/p pstrong已修复/strong$fixed/p pstrong未修复/异常/strong$failed/p /div table trth计算机名/thth修复前版本/thth修复后版本/thth结果/th/tr $($rows -join n) /table p stylemargin-top:20px;color:#666;本报告由 Windows PowerShell 自动化脚本生成数据来源于终端注册表版本扫描。/p /body /html $html | Set-Content -Path $ReportPath -Encoding UTF8 Write-Host 审计报告已生成: $ReportPath }这个 HTML 的表格样式很朴素但胜在信息完整、打开即用。如果你公司有统一的报告模板把 CSS 换成自己的就行。6. 几个容易踩的坑版本乱象、进程占用、WinRM 权限整个方案写完后我实际跑了几轮发现真正让人头疼的不是脚本逻辑而是各种环境差异导致的意外情况。下面这几个坑我建议你提前做预案。6.1 Office 版本号不可信的场景未完成更新的中间状态有个情况最容易被忽略某台机器的注册表VersionToReport已经变成修复后的版本但你打开 Office 的账户页面一看显示还是老版本。这是因为 Click-to-Run 的版本更新机制是异步的注册表里的值会先更新Office 应用的真正二进制文件可能还在后台替换。如果此时你用注册表版本号判定修复完成可能误报。我解决这个问题的办法是验证阶段除了读注册表版本号再检查 Office 安装目录下关键 DLL 的文件版本。比如C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE的文件版本会跟注册表同步更新且二进制文件版本比注册表更接近实际运行状态。先比注册表再比文件版本两者都达标才判定修复完成。6.2 进程占用导致补丁安装失败Outlook 杀进程要谨慎前面说过Office 补丁安装要求关闭所有 Office 进程。实操中最大的难点是 Outlook。企业用户上班第一件事就是打开 Outlook它常驻后台而且很多时候用户有重要邮件没保存草稿直接 Kill 进程是有风险的。我最终采用的处理策略是分三步走先尝试Outlook.exe /CleanReminders之类的命令也不靠谱直接发通知弹窗提醒用户 5 分钟内保存并关闭 Office 应用5 分钟后重试安装如果仍然被占用再从注册表Office\Outlook\Resiliency里看是否有未保存的邮件草稿确认没有未保存数据后才强制结束进程突出一个原则能不死磕就引导用户配合非必要不强制杀进程。6.3 WinRM 连接失败不是所有机器都开着 PSRemoting如果你的目标机器没启用 WinRMInvoke-Command会直接报错。处理方法有两个我更推荐第一个执行前用Test-WSMan探测端口 5985 是否可用不通的机器单独导出清单改用 PsExec 或登录脚本或者在组策略里提前放行 WinRM 服务并配置 TrustedHosts域名环境下一般默认开启 PowerShell 远程但如果你要打的是工作组机器这一步不做准备会卡死一大批。6.4 报告导出 UTF-8 乱码问题用 PowerShell 5.1 时Export-Csv默认编码是 ANSI本地编码生成的 CSV 用 Excel 打开中文没问题但如果用其他工具读或者导入 HTML 模板就会乱码。我在所有导出操作里都显式加上了-Encoding UTF8这是个小细节但能省很多麻烦。还有Set-Content写 HTML 时如果不指定 UTF-8默认的 UTF-16 会让浏览器直接显示乱码。这个我在初版脚本里踩过后来统一了编码参数。7. 这套方案上线之后的效果与继续优化的方向把脚本跑通那天下午我重新对整个终端池扫了一遍第一批检测出 218 台受影响机器自动修复成功 209 台剩下的 9 台有 3 台是机器关机、2 台网络隔离、4 台是进程占用未处理。第二轮补扫后全部修复完整个过程包含人工介入不超过 4 小时审计报告当天就归档完毕。通过这次处理我最大的体会有三点第一自动化修复的难点不在写命令而在补全边界情况。版本号比较逻辑、注册表重定向、进程占用、编码这些看似琐碎的问题任何一个没处理到位批量跑起来都会变成几千条报错糊一脸。第二留痕比修复本身更耗时但也更重要。你可能觉得修完了就行了但安全审计、等保检查、漏洞闭环管理都要求完整的处置记录。我建议从第一天开始就把所有检测和修复数据落盘不要到最后再补。第三脚本要有可配置项不要全写死在代码里。修复版本号、补丁路径、并发数、超时时间这些东西建议放到一个config.json或 CSV 里每次发布新漏洞时只改配置不改代码这个方案就能变成你处理 Office 漏洞事件的通用工具下次 CVE 出来你直接套壳就能用。最后再分享一个小技巧给脚本加个-ReportOnly参数只做扫描验证不实际修改用来在正式修复前模拟一遍流程确认逻辑没问题再开真刀真枪。我在测试环境就因为这个参数避开了不少低级错误比如补丁路径写错、版本号比较反了之类的问题。批量化操作这种事宁可慢一步也要稳一步。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门