Microsoft SharePoint Server 远程代码执行漏洞 (ToolShell) 完整技术分析
CVE-2025-53770 (代号: ToolShell) 是一个影响 Microsoft SharePoint Server 本地部署版本的严重远程代码执行漏洞。攻击者无需任何身份验证即可完全控制目标服务器,已在全球范围内被 APT 组织和勒索软件团伙积极利用。
一句话总结: 通过 Referer 头绕过身份验证,利用 ToolPane 页面的 ExcelDataSet 控件反序列化漏洞,实现远程代码执行。
技术要素:
身份验证绕过 (CVE-2025-49706)
不安全的反序列化 (CWE-502)
MachineKey 泄露
ViewState 伪造
| 项目 | 详情 |
|---|---|
| CVE 编号 | CVE-2025-53770 |
| CVSS 评分 | 9.8 (Critical) |
| 攻击复杂度 | Low (公开 PoC 可用) |
| 需要认证 | 否 |
| 全球暴露实例 | ~235,000 |
| 首次野外利用 | 2025-07-18 |
| 补丁发布 | 2025-07-19 |
SharePoint Server Subscription Edition (< 16.0.18526.20286)
SharePoint Server 2019 (< 16.0.10410.12000)
SharePoint Server 2016 Enterprise Edition (< 16.0.5461.1000)
注意: SharePoint Online (云端版本) 不受影响。
2025-05-16 Pwn2Own Berlin 2025 首次演示
↓
2025-07-08 Microsoft 发布初步补丁 (CVE-2025-49706/49704)
↓
2025-07-18 Eye Security 检测到首次野外利用
↓
2025-07-19 Microsoft 披露 CVE-2025-53770 (补丁绕过)
↓
2025-07-20 CISA 将其列入 KEV 目录
↓
2025-07-22 Microsoft 发布最终修复 (CVE-2025-53771)
↓
2025-07-24 全球 APT 组织开始大规模利用
CVE-2025-53770 实际上是三个漏洞的组合利用:
第1步: 身份验证绕过 (CVE-2025-49706)
└─> 通过 Referer: /_layouts/SignOut.aspx 绕过认证检查
第2步: 文件读取/反序列化 (CVE-2025-49704)
└─> 利用 ExcelDataSet 控件的 CompressedDataTable 属性
└─> 触发 BinaryFormatter 不安全反序列化
第3步: 持久化与提权 (CVE-2025-53770)
└─> 部署 webshell (spinstall0.aspx)
└─> 窃取 MachineKey
└─> 伪造 ViewState 实现稳定 RCE
SharePoint 的认证逻辑存在缺陷,在处理 Referer 头时使用了不安全的字符串匹配:
易受攻击的代码逻辑(简化版):
// 漏洞代码 (CVE-2025-49706)
if (request.UrlReferrer != null &&
request.UrlReferrer.PathAndQuery.EndsWith("SignOut.aspx",
StringComparison.OrdinalIgnoreCase))
{
skipAuthenticationCheck = true; // 错误!
}
攻击者只需在 HTTP 请求中设置:
Referer: https://target.com/_layouts/SignOut.aspx
即可绕过认证,访问本应受限的ToolPane.aspx页面。
第一次修复(CVE-2025-53770 仍可绕过):
// 不完善的修复
if (request.UrlReferrer.EndsWith("SignOut.aspx") &&
!request.Path.EndsWith("ToolPane.aspx"))
{
skipAuthenticationCheck = true;
}
// 绕过方法:
// POST /_layouts/15/ToolPane.aspx/ (末尾加斜杠)
// POST /_layouts/15/ToolPane%2easpx (URL 编码)
最终安全修复(CVE-2025-53771):
// 正确的修复: 路径规范化 + 白名单
string normalizedPath = NormalizePath(request.Path);
var allowedResources = new[] {
"/_layouts/15/init.js",
"/_layouts/15/core.js",
"/scriptresource.axd"
};
if (normalizedReferer.EndsWith("signout.aspx"))
{
skipAuthCheck = allowedResources.Any(r =>
normalizedPath.StartsWith(r, OrdinalIgnoreCase));
}
进入 ToolPane.aspx 后,攻击者利用ExcelDataSet控件的CompressedDataTable属性触发反序列化:
漏洞代码路径:
public DataTable DataTable
{
get
{
if (_dataTable == null && !string.IsNullOrEmpty(_compressedData))
{
// 步骤1: Base64 解码
byte[] compressed = Convert.FromBase64String(_compressedData);
// 步骤2: GZip 解压
using (var gz = new GZipStream(new MemoryStream(compressed),
CompressionMode.Decompress))
{
// 步骤3: 危险! 使用 BinaryFormatter 反序列化
var formatter = new BinaryFormatter();
// 没有 SerializationBinder,可以实例化任意类型!
object obj = formatter.Deserialize(gz);
_dataTable = obj as DataTable;
}
}
return _dataTable;
}
}
问题所在:
没有使用SerializationBinder限制可反序列化的类型
直接对用户控制的数据 (Base64+GZip) 进行反序列化
BinaryFormatter 会自动实例化并执行对象的构造函数/属性
攻击者使用ysoserial.net工具生成恶意对象链 (Gadget Chain):
TypeConfuseDelegate Gadget 原理:
// 构造恶意对象图
var delegate = (Comparison<string>)Delegate.CreateDelegate(
typeof(Comparison<string>),
typeof(Process).GetMethod("Start", new[] { typeof(string) })
);
var trigger = new SortedSet<string>(new DelegateComparer(delegate));
trigger.Add("powershell.exe -enc <Base64_Command>");
// 序列化这个对象
var formatter = new BinaryFormatter();
formatter.Serialize(stream, trigger);
// 当 SharePoint 反序列化时:
// 1. SortedSet 被还原
// 2. 自动调用 Comparer.Compare() 排序元素
// 3. Comparer 内部调用 delegate
// 4. delegate 指向 Process.Start
// 5. 执行 powershell.exe → RCE!
[1] 侦察
↓
[2] POST /_layouts/15/ToolPane.aspx
Referer: /_layouts/SignOut.aspx
Body: MSOtlPn_DWP=<WebPart_Markup>&CompressedDataTable=<Gadget>
↓
[3] 绕过认证 → 进入 ToolPane 页面
↓
[4] ExcelDataSet 控件实例化
↓
[5] CompressedDataTable 属性被设置
↓
[6] DataBind() → DataTable.get() → BinaryFormatter.Deserialize()
↓
[7] Gadget 触发 → Process.Start(powershell.exe)
↓
[8] 部署 webshell (spinstall0.aspx)
↓
[9] 读取 MachineKey (从 web.config)
↓
[10] 伪造 ViewState → 稳定 RCE
根据 Shodan 数据 (截至 2025-07-20):
| 国家/地区 | 暴露实例 |
|---|---|
| 美国 | 89,000 |
| 中国 | 31,000 |
| 英国 | 18,000 |
| 德国 | 15,000 |
| 日本 | 12,000 |
| 其他 | 70,000 |
| 总计 | ~235,000 |
政府部门: 23%
金融服务: 19%
医疗健康: 16%
制造业: 14%
教育机构: 12%
其他: 16%
APT 组织:
Violet Typhoon(中国): 针对政府和国防部门
Linen Typhoon: 供应链攻击
Storm-2603: 金融行业定向攻击
勒索软件团伙:
机会主义大规模扫描
加密 SharePoint 内容数据库
要求支付 10-50 BTC
脚本小子:
使用公开 PoC 自动化扫描
部署加密货币挖矿程序
建立僵尸网络
Snort IDS 规则:
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (
msg:"CVE-2025-53770 ToolShell Exploit Attempt";
flow:established,to_server;
content:"POST"; http_method;
content:"/_layouts/15/ToolPane.aspx"; http_uri;
content:"Referer"; http_header;
content:"SignOut.aspx"; http_header;
reference:cve,2025-53770;
classtype:web-application-attack;
sid:2025770; rev:1;
)
ModSecurity WAF 规则:
SecRule REQUEST_FILENAME "@rx /_layouts/15/ToolPane\.aspx" \
"id:7770001,\
phase:1,\
chain,\
deny,\
status:403,\
msg:'CVE-2025-53770: ToolPane access blocked'"
SecRule REQUEST_HEADERS:Referer "@rx SignOut\.aspx"
Sysmon 配置:
<Sysmon schemaversion="4.82">
<EventFiltering>
<!-- 检测 w3wp.exe 创建子进程 -->
<ProcessCreate onmatch="include">
<ParentImage condition="is">C:\Windows\System32\inetsrv\w3wp.exe</ParentImage>
<Image condition="is any">cmd.exe;powershell.exe</Image>
</ProcessCreate>
<!-- 检测 LAYOUTS 目录文件创建 -->
<FileCreate onmatch="include">
<TargetFilename condition="contains">TEMPLATE\LAYOUTS</TargetFilename>
<TargetFilename condition="end with">.aspx</TargetFilename>
</FileCreate>
</EventFiltering>
</Sysmon>
Splunk:
index=iis_logs sourcetype=iis
cs_uri_stem="/_layouts/15/ToolPane.aspx"
cs_method="POST"
| eval suspicious=if(like(cs_Referer, "%SignOut.aspx"), 1, 0)
| where suspicious=1
| stats count by src_ip, cs_uri_stem
| where count > 3
Elasticsearch:
{
"query": {
"bool": {
"must": [
{ "term": { "http.request.method": "POST" } },
{ "wildcard": { "url.path": "*ToolPane.aspx*" } },
{ "wildcard": { "http.request.referrer": "*SignOut.aspx*" } }
]
}
}
}
PowerShell 脚本:
# 扫描可疑 webshell
$layoutsPath = "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\*\TEMPLATE\LAYOUTS"
Get-ChildItem $layoutsPath -Filter "*.aspx" -Recurse |
Where-Object {
$_.CreationTimeUtc -gt (Get-Date).AddDays(-7) -and
$_.Length -lt 10KB
} | ForEach-Object {
$content = Get-Content $_.FullName -Raw
if ($content -match 'Request\[|eval\(|Process\.Start') {
Write-Host "[!] 可疑 webshell: $($_.FullName)" -ForegroundColor Red
}
}
# 检测可疑进程
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-Sysmon/Operational'
ID = 1
StartTime = (Get-Date).AddDays(-7)
} | Where-Object {
$xml = [xml]$_.ToXml()
$parent = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'ParentImage'}).'#text'
$parent -match 'w3wp\.exe'
} | Select TimeCreated,
@{N='Image';E={($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'Image'}).'#text'}},
@{N='CommandLine';E={($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'CommandLine'}).'#text'}}
1. 部署官方补丁
| SharePoint 版本 | KB 编号 | 下载链接 |
|---|---|---|
| Subscription Edition | KB5002768 | Microsoft Update Catalog |
| 2019 | KB5002754 | Microsoft Update Catalog |
| 2016 | KB5002760 | Microsoft Update Catalog |
自动化部署脚本:
# 安装补丁
$patchPath = "C:\Patches\sharepoint-kb5002754.msp"
Start-Process msiexec.exe -ArgumentList "/update `"$patchPath`" /passive /norestart" -Wait
# 运行 SharePoint 配置向导
& "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\BIN\PSConfig.exe" `
-cmd upgrade -inplace b2b -wait -force
2. 轮换 MachineKey
# 生成新密钥
$newValidationKey = -join ((48..57) + (65..70) | Get-Random -Count 128 | % {[char]$_})
$newDecryptionKey = -join ((48..57) + (65..70) | Get-Random -Count 48 | % {[char]$_})
# 更新所有 web.config
Add-PSSnapin Microsoft.SharePoint.PowerShell
Get-SPWebApplication | ForEach-Object {
$configPath = Join-Path $_.IisSettings[0].Path.FullName "web.config"
[xml]$config = Get-Content $configPath
$mk = $config.configuration.'system.web'.machineKey
$mk.validationKey = $newValidationKey
$mk.decryptionKey = $newDecryptionKey
$config.Save($configPath)
}
# 重启 IIS
iisreset /noforce
3. 启用 AMSI
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\AMSI\Providers\{2781761E-28E0-4109-99FE-B9D127C57AFE}" `
-Name "ScanLevel" -Value 2 -Type DWord # 2 = Full scan
NGINX 反向代理配置:
server {
listen 443 ssl http2;
server_name sharepoint.company.com;
# CVE-2025-53770 防护
location ~ ^/_layouts/15/ToolPane\.aspx {
# 检查 Referer
if ($http_referer ~* "SignOut\.aspx") {
return 403 "CVE-2025-53770 Protection: Access blocked";
}
# 限制 POST 大小
if ($http_content_length > 102400) {
return 413;
}
# 要求认证 Cookie
if ($http_cookie !~* "FedAuth|rtFa") {
return 401;
}
proxy_pass https://backend-sharepoint;
}
}
防火墙规则(PowerShell):
# 仅允许来自反向代理的连接
New-NetFirewallRule -DisplayName "SharePoint - Allow from Proxy Only" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 80,443 `
-RemoteAddress 10.0.1.10 ` # 反向代理 IP
-Action Allow
# 阻断 w3wp.exe 的出站连接 (防止 C2 通信)
New-NetFirewallRule -DisplayName "Block w3wp Outbound" `
-Direction Outbound `
-Program "C:\Windows\System32\inetsrv\w3wp.exe" `
-Action Block `
-Enabled True
禁用危险 WebPart:
Add-PSSnapin Microsoft.SharePoint.PowerShell
$dangerousWebParts = @(
"Microsoft.PerformancePoint.Scorecards.Client.ExcelDataSet",
"Microsoft.Office.Excel.WebUI.ExcelWebRenderer"
)
foreach ($wp in $dangerousWebParts) {
Get-SPWebPartPack -Identity $wp -ErrorAction SilentlyContinue |
Uninstall-SPWebPartPack -Confirm:$false
}
强化 web.config:
<configuration>
<system.web>
<!-- 启用 ViewState MAC 验证 -->
<pages enableViewStateMac="true"
viewStateEncryptionMode="Always" />
<!-- 禁用调试模式 -->
<compilation debug="false" />
<!-- 自定义错误页面 -->
<customErrors mode="On" defaultRedirect="~/Error.aspx" />
</system.web>
<system.webServer>
<!-- 安全响应头 -->
<httpProtocol>
<customHeaders>
<add name="X-Content-Type-Options" value="nosniff" />
<add name="X-Frame-Options" value="SAMEORIGIN" />
<add name="X-XSS-Protection" value="1; mode=block" />
<add name="Content-Security-Policy" value="default-src 'self'" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
文件 IOC:
C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\*\TEMPLATE\LAYOUTS\spinstall*.aspx
SHA256:92bb4ddb98eeaf...(已知 webshell 哈希)
进程 IOC:
进程链:w3wp.exe→cmd.exe→powershell.exe
命令行包含:-EncodedCommand或-enc
网络 IOC:
POST 请求到/_layouts/15/ToolPane.aspx
Referer 包含SignOut.aspx
w3wp.exe 发起出站 HTTP/HTTPS 连接
注册表 IOC:
修改HKLM\SOFTWARE\Microsoft\AMSI\Providers\{...}\ScanLevel为 0 (尝试禁用 AMSI)
阶段1: 遏制 (0-1小时)
# 1. 立即隔离受感染服务器
New-NetFirewallRule -DisplayName "Emergency Isolation" `
-Direction Inbound -Action Block -Enabled True
# 2. 停止 SharePoint 服务
Stop-Service SPTimerV4, SPAdminV4, W3SVC
# 3. 创建内存转储 (取证)
Get-Process w3wp | ForEach-Object {
& "procdump64.exe" -ma $_.Id "C:\Forensics\w3wp_$($_.Id).dmp"
}
阶段2: 根除 (1-4小时)
# 1. 删除 webshell
$suspiciousFiles = Get-ChildItem "C:\...\LAYOUTS" -Filter "sp*.aspx"
foreach ($file in $suspiciousFiles) {
Move-Item $file "C:\Quarantine\$($file.Name)" -Force
}
# 2. 轮换 MachineKey (见上文)
# 3. 重置所有管理员密码
# (手动操作)
# 4. 检查持久化机制
Get-WmiObject Win32_Service | Where-Object {$_.PathName -match "powershell|cmd"}
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
阶段3: 恢复 (4-24小时)
# 1. 部署补丁
# (见上文)
# 2. 从备份还原被篡改的文件
# Restore-SPFarm -Directory "C:\Backups\SharePoint_20250707"
# 3. 重启服务
Start-Service SPTimerV4, SPAdminV4
iisreset
# 4. 验证系统完整性
Test-SPContentDatabase -Name "WSS_Content" -WebApplication "http://sp"
阶段4: 事后分析 (1-7天)
完整日志分析 (IIS、Windows 事件、Sysmon)
确定初始入侵时间和攻击者身份
评估数据泄露范围
生成事件报告
更新安全策略和检测规则
向量字符串: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
基础指标:
攻击向量 (AV:N) - Network (远程网络)
攻击复杂度 (AC:L) - Low (无需特殊条件)
所需权限 (PR:N) - None (无需认证)
用户交互 (UI:N) - None (无需用户参与)
影响范围 (S:U) - Unchanged (不影响其他组件)
影响指标:
机密性 (C:H) - High (完全数据泄露)
完整性 (I:H) - High (任意数据修改)
可用性 (A:H) - High (服务完全拒绝)
基础评分: 9.8 (CRITICAL)
金融行业:
客户数据泄露 (GLBA 违规)
监管罚款: 高达 $10M
声誉损失: 客户流失 15-30%
总损失估算: $20M - $50M
医疗行业:
患者隐私泄露 (HIPAA 违规)
监管罚款: 高达 $1.5M/年
诉讼风险
总损失估算: $5M - $30M
政府部门:
国家安全数据泄露
外交影响
损失无法量化
制造业:
知识产权窃取
供应链中断
总损失估算: $2M - $15M
避免不安全的反序列化:
// 错误: 使用 BinaryFormatter
var formatter = new BinaryFormatter();
object obj = formatter.Deserialize(stream); // 危险!
// 正确: 使用 JSON + 强类型
[DataContract]
public sealed class SafeDataDto
{
[DataMember] public string[][] Rows { get; set; }
}
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver(), // 禁用多态
MaxDepth = 32
};
var obj = JsonSerializer.Deserialize<SafeDataDto>(stream, options);
如果必须使用 BinaryFormatter:
// 添加 SerializationBinder
public sealed class SafeBinder : SerializationBinder
{
private static readonly HashSet<string> AllowedTypes = new HashSet<string>
{
"System.Data.DataTable, System.Data",
"System.Data.DataSet, System.Data"
};
public override Type BindToType(string assemblyName, string typeName)
{
string fullName = $"{typeName}, {assemblyName}";
if (!AllowedTypes.Contains(fullName))
{
throw new SecurityException($"Type not allowed: {fullName}");
}
return Type.GetType(fullName, throwOnError: true);
}
}
// 使用
var formatter = new BinaryFormatter { Binder = new SafeBinder() };
object obj = formatter.Deserialize(stream);
1. 定期安全更新
订阅 Microsoft 安全公告
每月部署安全补丁
维护补丁部署记录
2. 最小权限原则
SharePoint 服务账户不应是域管理员
应用程序池使用专用账户
数据库连接使用最小权限
3. 网络分段
SharePoint 服务器位于内网
通过反向代理对外发布
限制 SharePoint 到 SQL Server 的连接
4. 监控与告警
集中日志收集 (SIEM)
实时告警 (异常进程、文件创建、网络连接)
定期威胁狩猎
5. 备份策略
3-2-1 备份规则 (3 份副本、2 种介质、1 份离线)
每日增量备份
每周完整备份
定期恢复测试
6. 应急演练
每季度进行桌面推演
每年进行实战演练
更新应急响应手册
高危漏洞: CVSS 9.8,已在野利用,影响全球 ~235,000 个实例
无需认证: 攻击者可远程利用,无需任何凭据
链式利用: 结合认证绕过、反序列化、密钥窃取三个漏洞
持久化强: 通过 webshell 和 ViewState 伪造实现稳定控制
检测困难: 利用合法功能,流量特征不明显
今天: 部署官方补丁 (KB5002754/68/60)
今天: 轮换 MachineKey
今天: 启用 AMSI Full Mode
今天: 部署 WAF 规则
本周: 执行威胁狩猎,检查 IOC
本周: 部署 IDS/SIEM 检测规则
本月: 网络隔离和分段
本月: 应急响应演练
持续: 监控和日志分析
技术债务清理: 逐步迁移到 SharePoint Online 或替换遗留代码
零信任架构: 实施严格的身份验证和授权
DevSecOps: 将安全融入开发生命周期
安全培训: 提升开发和运维团队的安全意识
免责声明: 本文仅供安全研究和防御目的,所有代码和技术细节应在授权环境中使用。请遵守当地法律法规。