前言
事件1
2017年7月25日,Adobe 终于官宣了 Flash 的产品寿命结束计划:
2020年12月31日,终止 Flash Player 的开发支持工作;
2021年1月12日,Flash Player 将不再支持播放 Flash 内容;
同时,该公司“强烈建议”所有用户立即在系统中卸载 Flash Player。
事件2
事件3
家里的网站也被这个flash屏蔽给搞坏了。
Flash服务了这么多年,可以说覆盖率相当大,此次停用,等同于埋了一个后门,定时触发“后门攻击”,不同于安全漏洞,它是有意为之。造成的影响不亚于一次供应链攻击。
很好奇,它是如何实现的呢?
分析
分析目的
Adobe在pepflashplayer.dll里如何实现对过期使用的封禁?从安全对抗的角度,这个限制策略是否可以绕过?
分析对象
名称 | 版本 | md5 |
---|---|---|
pepflashplayer.dll | 32.0.0.465 | AF6D91D849B5AD7BF71CE730EF1AC0E0 |
分析环境
Win10
Chrome 87.0.4280.141
分析工具
BurpSuit Pro
QQAnalyzer
WinDbg Preview
IDA
010 Editor
分析过程
1、寻找突破口
Adobe在其之前发布的几版Flash软件中埋下了逻辑后门,会在2021年1月12日触发,导致代码在此日期之后拒绝渲染任何更多内容。
这颗逻辑后门是否就在flash.dll里呢?
我们当前的dll位置:C:\Users\vinegar\AppData\Local\Google\Chrome\User Data\PepperFlash\32.0.0.465\pepflashplayer.dll
下载一个老版本v26.0.0.131,替换该pepflashplayer.dll,重启chrome,flash渲染正常,可以断定逻辑后门确实在该dll中。
过期使用封禁的策略是怎样的呢?
猜测两个可能,一个是联网状态,获取服务端限制策略,使本地逻辑开关关闭,另一个是获取本地日期,与限制日期比较。
情况一 联网抓包
针对http/https请求,burpsuit未发现可疑请求
针对TCP/UDP请求,QQAnalyzer也未发现可疑请求
也可能看漏了。。。
情况二 日期比较
这种情况,又分4种
(1) 当前日期来自本地,限制日期来自服务端
(2) 当前日期来自服务端,限制日期来自本地
(3) 当前日期来自本地,限制日期来自本地
(4) 当前日期来自服务端,限制日期来自服务端[这种类似情况一,但是直接下发一个开关就好了,没必要从server拿两个日期到client比较]
抓包没有发现,直接修改本地时间到2021.1.12之前,重启浏览器,加载成功。
可以确定,flash获取了本地时间,进行比较。
2、定位系统时间获取函数
Win中可以获取系统时间的方式很多,如何确定目标DLL是如何获取的呢?
可以参考导入函数表
IDA加载pepflashplayer.dll,查看导入函数,time关键字过滤
这里关注Get相关函数,有了时间获取函数,可以批量下断,动态调试跟踪逻辑了。
3、定位时间校验逻辑
我们知道chrome是多进程运行,包括浏览器,渲染器和插件程序
哪一个才是我们要调试的进程呢?
知识补充: Adobe Flash Player ActiveX:适用于7、Vista、XP系统IE内核浏览器zhi、本地视频、游戏客户端 NPAPI:适用于FireFox(火狐)、Safari(苹果)、Opera (欧dao朋,12.17版以下) PPAPI:适用于Chromium浏览器、Opera (欧朋,15.00版以上)
我们用的是chrome浏览器,所以目标锁定在上图第三个进程16736,--type=ppapi
WinDbg Preview 附加,lm查看加载模块,果然,我们的分析对象就在这里
从实际效果看,Attach方式调试已经晚了,校验逻辑已经执行完毕
如何在时间校验逻辑触发前,就断下来呢?
需要 Launch方式加载chrome.exe,并且调试子进程
进入我们需要调试的目标子进程
8:071> | 0 id: 448c create name: chrome.exe 1 id: 3e30 child name: chrome.exe 2 id: 326c child name: chrome.exe 3 id: 1f84 child name: chrome.exe 4 id: a60 child name: chrome.exe 5 id: 46d8 child name: chrome.exe 6 id: 45d0 child name: chrome.exe 7 id: 3580 child name: chrome.exe . 8 id: 3a24 child name: chrome.exe 8:071> | 8 s ntdll!LdrpDoDebuggerBreak+0x30: 00007fff`6b610fcc cc int 3 8:071> |. . 8 id: 3a24 child name: chrome.exe
设置模块加载断点
8:071> sxe ld:pepflashplayer 8:071> g ModLoad: 00007fff`689c0000 00007fff`68b58000 C:\Windows\System32\gdi32full.dll ModLoad: 0000016e`9a1d0000 0000016e`9a364000 C:\Windows\System32\USER32.dll ModLoad: 00007fff`6b400000 00007fff`6b42e000 C:\Windows\System32\IMM32.DLL ModLoad: 00007fff`67e00000 00007fff`67e0c000 C:\Windows\SYSTEM32\CRYPTBASE.DLL ModLoad: 00007fff`13b50000 00007fff`15ad4000 C:\Users\vinegar\AppData\Local\Google\Chrome\User Data\PepperFlash\32.0.0.465\pepflashplayer.dll 0000016e`99051734 c3 ret
批量设置获取时间函数断点
8:071> bm KERNEL32!GetSystemTime* 1: 00007fff`69f5ec90 @!"KERNEL32!GetSystemTimeAdjustmentStub" 2: 00007fff`69f5a840 @!"KERNEL32!GetSystemTimeStub" 3: 00007fff`69f56a70 @!"KERNEL32!GetSystemTimeAsFileTimeStub" 4: 00007fff`69f5e470 @!"KERNEL32!GetSystemTimesStub" 5: 00007fff`69f625c0 @!"KERNEL32!GetSystemTimePreciseAsFileTime" 8:071> bl 1 e Disable Clear 00007fff`69f5ec90 0001 (0001) 8:**** KERNEL32!GetSystemTimeAdjustmentStub 2 e Disable Clear 00007fff`69f5a840 0001 (0001) 8:**** KERNEL32!GetSystemTimeStub 3 e Disable Clear 00007fff`69f56a70 0001 (0001) 8:**** KERNEL32!GetSystemTimeAsFileTimeStub 4 e Disable Clear 00007fff`69f5e470 0001 (0001) 8:**** KERNEL32!GetSystemTimesStub 5 e Disable Clear 00007fff`69f625c0 0001 (0001) 8:**** KERNEL32!GetSystemTimePreciseAsFileTime
截获的时间函数调用很多,如何确定哪一个才是我们要找的呢?
这里有个笨办法,还好被调用的也就8,9次。
每次时间函数断下,修改系统时间,gu放行,再改回系统时间,g放行,查看页面效果,确认哪次修改,会使flash正常工作。
操作发现,GetSystemTimeAsFileTimeStub函数断下后,修改系统时间,可使flash正常工作。但该函数的上层调用位于chrome.dll中,并未在pepflashplayer.dll中。
难道check逻辑是在chrome.dll中?这与我们上面的验证逻辑不符,替换pepflashplayer.dll为老版本,可以正常渲染,难道chrome.dll能判断版本?
继续追溯上层调用发现,
Breakpoint 3 hit KERNEL32!GetSystemTimeAsFileTimeStub: 00007fff`69f56a70 48ff2571210600 jmp qword ptr [KERNEL32!_imp_GetSystemTimeAsFileTime (00007fff`69fb8be8)] ds:00007fff`69fb8be8={KERNELBASE!GetSystemTimeAsFileTime (00007fff`685bd100)} 8:071> gu chrome!ChromeMain+0x867: 00007ffe`f05e5817 4c89f0 mov rax,r14 8:071> gu chrome!ChromeMain+0x6b3: 00007ffe`f05e5663 4889f0 mov rax,rsi 8:071> gu chrome!RelaunchChromeBrowserWithNewCommandLineIfNeeded+0xae2056: 00007ffe`f492cea6 488b0e mov rcx,qword ptr [rsi] ds:00000038`4bffdcf0=002f176961bc531f 8:071> gu pepflashplayer!IAEModule_IAEKernel_UnloadModule+0x8863a3: 00007fff`14f86f03 f20f5905a5ea1800 mulsd xmm0,mmword ptr [pepflashplayer!curl_share_strerror+0x134ee0 (00007fff`151159b0)] ds:00007fff`151159b0=408f400000000000 8:071> gu pepflashplayer!PPP_ShutdownBroker+0x19ef45: 00007fff`13db35d5 0f28f0 movaps xmm6,xmm0
原来chrome.dll中的上层调用,来自pepflashplayer.dll。
所以是flash.dll从chrome.dll中获取时间,到自己内部做判断。奇怪,它自己读时间不简单嘛?
4、时间校验逻辑分析
基址信息
00007ff6`14d70000 00007ff6`14fa0000 chrome_exe 00007ffe`f05e0000 00007ffe`f9a5c000 chrome 00007fff`13b50000 00007fff`15ad4000 pepflashplayer
GetSystemTimeAsFileTimeStub函数的上层调用函数偏移计算:
内存中chrome.dll的获取时间函数地址:00007ffef05e5817 内存中chrome.dll的基址:00007ffef05e0000 IDA中chrome.dll的基址:180000000 IDA中chrome.dll的获取时间函数地址:00007ffef05e5817-00007ffef05e0000+180000000=180005817
所以GetSystemTimeAsFileTimeStub函数的上层调用位于chrome.dll的sub_180005680函数
继续追溯,上层调用位于pepflashplayer.dll的sub_181436EF0函数
上层调用位于pepflashplayer.dll的sub_180262FD0函数
比对参数就是qword_1815F8468,数值写死
分析总结
程序流程
时间校验流程
思考
时间校验逻辑中的对比数据,硬编码在dll中,直接修改该数据足够大,是否能绕过该check呢?
POC测试
1、定位pepflashplayer.dll中的时间戳硬编码位置: 40 46 3E 6F 77 42
2、修改该时间戳硬编码,足够大:FF FF FF FF FF 42
3、重启chrome,poc生效,成功
结论
Adobe Flash Player的过期封禁策略,是通过pepflashplayer.dll调用chrome.dll获取系统时间戳,与本地硬编码的时间戳做对比,实现过期无法渲染的逻辑。
有三种方式可以绕过上述校验逻辑:
1、hook上述check函数,修改读取到的系统时间戳;
2、时间戳对比结果值存储于寄存器al,修改该寄存器的值;
3、修改pepflashplayer.dll中的时间戳硬编码,flash未对dll做防篡改校验;