敏感文件读写预警工具——基于Windows Defender勒索软件防护功能

Windows系统自带杀软,Windows Defender中,包含了很多不为人知的功能。我们今天用到的勒索软件防护[1](也叫受控文件夹访问,Controlled Folder Access),就是一个非常有意义的小功能。这个功能在Windows 10、Windows Server 2019全系列可用,但只有在系统里没有安装其他杀毒软件时,才能够看到这个选项。

总体来说,这个功能做的事情就是对用户指定和系统预置的几个目录[2]进行监控,只允许白名单里的程序修改其内容,其他程序读文件不受限制,写文件时就会报错,同时Windows Defender会对非白名单程序的修改行为进行告警,在通知中心弹一个窗。可以当一个低配版的火绒剑用 😉

本文不教大家怎么开启这个功能,这个官方文档[3]都写过了,再写没意思。本文的目的主要有两个。

  1. 更详细的告警信息——弹窗的时候就说明白,哪个进程访问了哪个目录。默认的弹窗只说“Windows阻止了一次访问”,不告诉我们哪个进程,不告诉我们对哪个目录的操作。如果要看到详情,就要点到Defender的界面,点点点好几回,再确认好几次UAC弹框,体验极差。而且,Windows Server中不提醒!不提醒!不提醒!
  2. 更详细的告警历史——一个列表就能看到,哪个进程之前访问过哪个目录。Defender的界面里,点开每一条详情就需要点一个UAC弹框,很安全,但是体验还是极差!而且,查看历史记录这个功能只有Windows 10里有,Windows Server的Defender界面中连阻止历史记录这个菜单都没有!
  3. 到这里我们可以得出一个结论,我就是那个把Windows Server 2019装在日常电脑上的蠢B……

本文实现这两个目标的方式,是通过在Windows系统事件中,截获关于Windows Defender“受控文件夹访问”部分的告警事件,再通过PowerShell脚本将事件详情推送到桌面上,这样恶意进程的操作我们就能够实时看到了。

完成效果如下图所示。我在WSL中操作主机用户的Documents目录,在其中创建测试文件,访问被拒绝,同时弹出了通知消息。

弹窗效果演示

为什么我会需要这么一个功能呢?因为我装了很多盗版游戏……

获取告警历史日志

开始我也不知道这功能的日志在哪儿,我找了一个组策略配置项的Excel官方文档[4],在里面搜索到受控文件夹访问的相关配置项,定位到它在组策略中的位置是 计算机配置/管理模板/Windows组件/Windows Defender防病毒程序/Windows Defender攻击防护/受控制文件夹的访问 。这里面 配置受控文件夹访问权限 配置项的帮助里面,记录了几种防护模式和相关日志的位置,Defender界面上是没有关于阻止模式和审计相关的设置的。

组策略项“配置受控文件夹访问权限”的帮助 >folded
针对不受信任的应用程序启用或禁用受控文件夹访问权限。你可以选择阻止、审核或允许不受信任的应用进行以下尝试:
- 修改或删除受保护文件夹内的文件,如“文档”文件夹
- 写入磁盘扇区

你也可以选择仅阻止或审核对磁盘扇区进行的写入,同时仍允许修改或删除受保护文件夹中的文件。

Windows Defender 防病毒会自动确定可以信任哪些应用程序。你可以在“配置允许的应用程序”GP 设置中添加其他受信任的应用程序。
默认的系统文件夹自动受保护,不过你可以在“配置受保护的文件夹”GP 设置中添加文件夹。

阻止:
将阻止以下操作:
- 不受信任的应用尝试修改或删除受保护文件夹中的文件
- 不受信任的应用尝试写入磁盘扇区
Windows 事件日志将在“应用程序和服务日志”> "Microsoft" > "Windows" > "Windows Defender" >“可操作”> "ID 1123" 下记录这些阻止操作。


已禁用:
以下操作将不会被阻止,并被允许运行:
- 不受信任的应用尝试修改或删除受保护文件夹中的文件
- 不受信任的应用尝试写入磁盘扇区
这些尝试将不会记录在 Windows 事件日志中。


审核模式:
以下操作将不会被阻止,并被允许运行:
- 不受信任的应用尝试修改或删除受保护文件夹中的文件
- 不受信任的应用尝试写入磁盘扇区
Windows 事件日志将在“应用程序和服务日志”> "Microsoft" > "Windows" > "Windows Defender" >“可操作”> "ID 1124" 下面记录这些尝试。


仅阻止磁盘修改:
将阻止以下操作:
- 不受信任的应用尝试写入磁盘扇区
Windows 事件日志将在“应用程序和服务日志”> "Microsoft" > "Windows" > "Windows Defender" >“可操作”> "ID 1123" 下面记录这些尝试。

以下操作将不会被阻止,并被允许运行:
- 不受信任的应用尝试修改或删除受保护文件夹中的文件
这些尝试将不会记录在 Windows 事件日志中。


仅审核磁盘修改:
以下操作将不会被阻止,并被允许运行:
- 不受信任的应用尝试写入磁盘扇区
- 不受信任的应用尝试修改或删除受保护文件夹中的文件
只有尝试写入受保护的磁盘扇区的操作才会记录到 Windows 事件日志中(在“应用程序和服务日志”> "Microsoft" > "Windows" > "Windows Defender" >“可操作”> "ID 1124" 下)。
将不会记录尝试修改或删除受保护文件夹中的文件的操作。

未配置:
同“已禁用”。

从这里我们可以看到,受控文件夹访问的相关告警日志位于系统日志查看器中的 “应用程序和服务日志”> “Microsoft” > “Windows” > “Windows Defender” >“Operational”> “ID 1123” 和 1124 中。其中1123是防护模式为“阻止”时,阻止成功的日志,1124是防护模式为“仅审计”时,记录但没有实际阻止的警告日志。

除此之外,实际使用过程中发现,ID 1127也是此功能的警告日志,在非白名单程序写入磁盘扇区的时候会触发。

知道日志的具体位置以后,我们就可以在日志查看器中按图索骥查看受控文件夹访问功能的日志了。

为了方便我们以后快速的检索这些日志,我们可以将日志过滤器保存为一个自定义视图。按下图操作,将事件来源设置为 Windows Defender,包含事件ID 1123,1124,1127 即可。

事件查看器配置

现在我们看历史记录就很方便了。

设置告警弹窗

获取到事件日志之后,我们可以使用任务计划程序创建一个任务,以指定事件(在这里是Windows Defender的告警)作为触发器,运行一个命令行操作。这样Defender发现未知程序的时候,我们能够第一时间收到通知。

PowerShell通知脚本

我们先来想想怎么写这个脚本。为了保证最好的用户体验,我希望这个脚本能达到这几点:

  1. Native的体验,以悬浮提示(toast notification)的形式弹出,能在Windows通知中心中查看。有一个通知程序[5]能在桌面上弹出一个messagebox,这样的方式就很丑。
  2. 通知内容中包含每次告警的详情,什么进程访问了什么目录。而不是每次弹窗都一样的。
  3. 不要弹出命令行窗口的黑框。即使是一闪而过,也不优雅,还让人很慌。

命令行参数应该是这样:

"C:\path\to\notifier.ps1" -User $(User) -ProcessName $(ProcessName) -Path $(Path)

弹出通知的效果就是文章开头截图所示的样子。

参考[6]了一下StackOverflow,实现了下面的脚本,能够以传入的参数[7]弹出一个toast通知消息。

notifier.ps1 >folded
param (
[string]$User = $(Get-WMIObject -class Win32_ComputerSystem | Select-Object -ExpandProperty username),
[string]$ProcessName,
[string]$Path
)


[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

$objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$objNotifyIcon.Icon = [System.Drawing.SystemIcons]::Information
$objNotifyIcon.BalloonTipIcon = "Warning"
# 标题最多显示32个字
$objNotifyIcon.BalloonTipTitle = "检测到受限文件夹访问"
# 内容最多64个字,120个英文字符

function ConcatPath($Path, $MaxLength) {
$filename = $Path.Substring($Path.LastIndexOf("\"))
$remain = $MaxLength - $filename.Length - 5
if ($Path.Length -le $MaxLength) {
return $Path
}
elseif ($filename -gt $MaxLength) {
return "$($filename.Substring($filename.Length-3))..."
}
elseif ($remain -gt 0) {
return "$($Path.Substring(0, $remain)) [..] $filename"
}
else {
return "[..]/$filename"
}
}

if ($ProcessName.Length + $Path.Length -gt 100) {

$ProcessName = ConcatPath "$ProcessName" 30
$Path = ConcatPath "$Path" 40
}

$objNotifyIcon.BalloonTipText = "进程: $ProcessName,访问路径: $Path"
$objNotifyIcon.Visible = $True
$objNotifyIcon.ShowBalloonTip(30)

实际测试中,一条toast消息的标题最多32个中文字符,内容最多能放64个字或120个英文字符,而且里面包含文件路径的时候,还会在一些莫名其妙的地方换行浪费很多空间,因此额外加了一个函数来剪裁一下最终输出的文件名,效果还不错。

隐藏脚本运行的命令行窗口

这个小脚本还有一个缺点,因为powershell.exe这个二进制文件的类型是命令行程序,因此操作系统在启动它时,必然会弹出一个黑框框。即使加上参数 PowerShell.exe -WindowStyle Hidden 也会有一个黑框框一闪而过,很膈应。这个问题在PowerShell的Github上也有讨论[8]。翻了一遍大概有两个解决办法。

一个是用Windows Script Host wscript.exe 运行一个VBS脚本[8:1]作为中转,从VBS里调用PowerShell。另一个方案[9]是编译一个C#的小程序作为中转调用PowerShell。思路都是用一个非命令行程序作为中转,再用系统API静默启动PowerShell。

这里我选择方案一,写一个VBS脚本。因为方案二需要把一个未签名的程序放到system32目录下(作者的设定是和powershell.exe放在一起),既不安全也不好定制。

这个脚本也是参考上面issue里面的讨论[8:2]写的,但原作者写的版本没办法向脚本传入参数,因此我改进了一下。VB这语言是真垃圾,处理字符串很麻烦,搜doc又没有,只能找example,编程靠脑补,debug靠报错信息,报错信息还不给原因,就告诉我xx行错了,简直是垃圾中的垃圾。

powershell.vbs
Set args = CreateObject("System.Collections.ArrayList")
For Each oItem In Wscript.Arguments: args.Add oItem: Next

CreateObject("Wscript.Shell").Run("powershell -windowstyle hidden -File """ & Join(args.ToArray, """ """) & """"),0

上面的文件保存后,我们就可以用这个脚本无窗口调用PowerShell脚本了。

wscript.exe "C:\path\to\powershell.vbs" "C:\path\to\notifier.ps1" -User $(User) -ProcessName $(ProcessName) -Path $(Path)</Arguments>

仔细一想,这个操作还是很牛逼的。用在渗透中,可以执行一些用户无感知的操作,隐蔽且功能强大,算是一个很棒的技巧啦。

配置事件监控

接下来我们就要创建事件监听器了,我们通过任务计划触发刚刚写的一堆脚本,整个流程就打通了。

在任务计划程序里按下图的配置创建一个新的任务,触发器中的筛选器选择 Windows Defender 作为事件源,事件ID和刚才创建事件视图时一样就好, 1123,1124,1127 ,上面的事件级别我忘了要不要配置了,保险起见我全勾上了。

创建任务

触发器配置好后,不要急着点确定(点了确定系统也会报错),在“操作”选项卡里添加一个启动程序操作,命令是 C:\Windows\system32\wscript.exe ,参数和上一节所述相似,记得改成实际的脚本路径,后面的 $(User) 几个部分不要修改。

wscript参数
"C:\path\to\powershell.vbs" "C:\path\to\notifier.ps1" -User $(User) -ProcessName $(ProcessName) -Path $(Path)

向导完成之后,我们还需要一些手动配置。右键导出刚刚创建的任务,存成XML。在XML中的 EventTrigger 标签中增加一个 ValueQueries 子标签[10][11],编辑后的配置文件就像这样。

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<!-- ... -->
</RegistrationInfo>
<Triggers>
<EventTrigger>
<Enabled>true</Enabled>
<Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="Microsoft-Windows-Windows Defender/Operational"&gt;&lt;Select Path="Microsoft-Windows-Windows Defender/Operational"&gt;*[System[Provider[@Name='Microsoft-Windows-Windows Defender'] and (Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5) and (EventID=1123 or EventID=1124 or EventID=1127)]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription>
<ValueQueries>
<Value name="Path">Event/EventData/Data[@Name="Path"]</Value>
<Value name="ProcessName">Event/EventData/Data[@Name="Process Name"]</Value>
<Value name="User">Event/EventData/Data[@Name="User"]</Value>
</ValueQueries>
</EventTrigger>
</Triggers>
<!-- ... -->
</Task>

编辑完成后,在任务计划程序中删除刚刚创建的任务并将这个任务导入。这个修改的目的是将Event的数据提取出来,存成事件的一个参数,这样事件触发操作的命令行中就可以用类似 $(Path) 的语法引用这个参数,实现将Event的数据传递给命令行。

成果

这一套流程最后能达成的效果其实就是文章开头图中显示的那样。任何未授权的文件修改或磁盘扇区读写最终都会弹出一个警告。

弹窗效果演示

Defender监视的文件夹可以自定义,可以通过图形界面(Windows安全中心)、PowerShell命令 Add-MpPreference -ControlledFolderAccessAllowedApplications 和组策略三种方式[3:1]。组策略中的配置位于 计算机配置/管理模板/Windows组件/Windows Defender防病毒程序/Windows Defender攻击防护/受控制文件夹的访问/配置受保护的文件夹 。如果读者在上文的配置中,在组策略里修改了防护模式(修改了 配置受控文件夹访问权限 配置项),那受保护的文件夹列表就只能在组策略中设置了。比较麻烦。

结语

除了勒索软件防护之外,Windows Defender还包括很多其他看起来非常安全的功能,比如二进制大佬的克星——Exploit Protection,基于TPM和安全启动的系统防篡改——Windows Defender System Guard,基于硬件虚拟化的内核隔离、Credential Guard(缓解mimikatz类攻击),还有一堆针对企业用户的防护策略类功能。仔细想想就会发现,其实根本用不到别的杀毒软件嘛!

参考文档


  1. Defender介绍官方文档: 防止勒索软件和威胁对文件进行加密和更改 | Microsoft Docs ↩︎

  2. 系统预置的保护目录包括个人目录(%USERPROFILE%)和公共用户(%PUBLIC%)下的文档、图片、音乐、视频、桌面目录。这些目录无法从保护名单中删除。 ↩︎

  3. 受控文件夹访问功能配置官方文档: 在 Windows 10 中打开受保护的文件夹功能 | Microsoft Docs ↩︎ ↩︎

  4. 可以搜索的组策略Excel文档: Download Group Policy Settings Reference for Windows and Windows Server from Official Microsoft Download Center ↩︎

  5. 另一种弹出通知的方法:How can I send a notification to a Windows 10 computer from the command line - Super User ↩︎

  6. 参考别人的PowerShell推送通知脚本: How to keep powershell notification in action center - Stack Overflow ↩︎

  7. PowerShell参数的定义方式,比bash和python优雅多了。PowerShell Parameters - PowerShell - SS64.com ↩︎

  8. Github 讨论:Powershell -WindowStyle Hidden still shows a window briefly · Issue #3028 · PowerShell/PowerShell ,我的comment: issuecomment-583834582 ,参考别人的VBS中转脚本: issuecomment-522375489 ↩︎ ↩︎ ↩︎

  9. 一个小程序,假装自己是一个GUI程序,调用powershell: SeidChr/RunHiddenConsole: Created executable can be renamed to powershellw.exe or pwshw.exe …… ↩︎

  10. 任务计划高级配置官方文档:EventTrigger.ValueQueries property - Win32 apps | Microsoft Docs ↩︎

  11. 如何将事件参数传入计划任务的命令行: Scheduled Task - Trigerred by Event - Getting data into the scripts as parameters…- Question ↩︎

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×