blast · 2016/06/21 13:18
0x00 簡介
漏洞作者EduardoBraun Prado在今年早期發現了WMP的.MCL檔案又存在一個可以導致遠端程式碼執行的漏洞。為什麼要說又呢,因為這個東西實在是“不爭氣”,同一個地方出現了多次繞過導致遠端程式碼執行的問題。
0x01 歷史A――MS15-100
2015年的MS15-100(CVE-2015-2509,Aaron Luo, Kenney Lu, and Ziv Chang of TrendMicro)漏洞便是MCL“噩夢”的開始,研究者發現只要在MCL檔案中指定:
<application run="c:\windows\system32\cmd.exe"></application>
複製程式碼
便可以讓WMP執行cmd.exe,程式碼層面看,由於MCL是一個XML檔案,WMP載入時只是簡單的解析了該XML,便直接執行了“run”中指定的內容。看起來相當令人無語。
0x02 歷史B――MS15-134
緊隨著MS15-100中微軟將run的內容做了過濾之後,MS15-134(CVE-2015-6127,Francisco Falcon of CoreSecurity)又誕生了。這回作者利用的倒不是run這個屬性,而是另一個url屬性。
通過application的url屬性指定一個檔案後,WMP會在自己的WebBrowser中載入這個URL。但是研究者發現,WMP的exe並不在Local Machine Lockdown的列表(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\InternetExplorer\MAIN\FeatureControl\FEATURE_LOCALMACHINE_LOCKDOWN
)中,因此在WMP的WebBrowser中開啟的本地頁面中的指令碼將會自動執行。
但是總歸不能分發兩個檔案到使用者電腦中吧,於是作者想到了更“優雅”的方式:在application標籤中加入指令碼。因為WMP的XMLParser把不認識的APPLICATION的子元素全部忽略,如果攻擊者構造攻擊檔案a.mlc:
#!html
<application url="a.mlc">
<script>alert(1)</script>
</application>
複製程式碼
注意url指向自身,這樣這個檔案又會被當作HTML檔案載入,由於WebBrowser(IE)處理HTML的寬鬆的特性――不認識的標籤會被忽略,只去渲染認識標籤的含義。就導致WebBrowser中直接渲染並在file域下執行了檔案包含的指令碼。接下來能幹什麼事情想必也不用細說了。
0x03 主角――MS16-059
在微軟攔截的七七八八的時候,又有人發現了新玩法:
<application run="file:///\\127.0.0.1\c$\programdata\cpl.lnk"/>
複製程式碼
因為微軟對UNC作出了攔截,攻擊者使用了file:///
來繞過過濾。同時,微軟也對傳遞的檔案字尾進行了攔截,因此作者又傳入了lnk(快捷方式)檔案進行了繞過。
作者在lnk中指定了呼叫一個CPL檔案,CPL其實也是一個實現了特定介面的DLL檔案,因此作者成功地繞過了MCL的攔截,執行了任意程式碼。
藉助於UNC,作者仍然只需要傳入一個MCL檔案給使用者即可,其他的所有惡意程式碼完全依靠遠端拉取。
0x04 MCL是如何被處理的?(原版・14年版本)
因為虛擬機器打補丁實在是太慢了,所以這裡就直接用14年的DLL來演示了,看一個原始風貌。
MCL檔案如何處理?開啟登錄檔,檢視MCL檔案相關關聯,可知是由ehshell.exe開啟的:HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.mcl\OpenWithList\ehshell.exe
。
觀察ehshell.exe的程式碼可以發現,ehshell.exe簡單地載入了ehshell.dll,之後所有邏輯都是再ehshell.dll去做的。
從呼叫棧也可以得出這個結論(雙擊mcl檔案之後的棧):
#!bash
0:025> bp kernel32!CreateFileW
0:025> g
Breakpoint 0 hit
kernel32!CreateFileW:
00000000`779b1b88 ff2522b90700 jmp qword ptr [kernel32!_imp_CreateFileW (00000000`77a2d4b0)] ds:00000000`77a2d4b0={KERNELBASE!CreateFileW (000007fe`fd944600)}
0:000> dqs esp
00000000`0021e108 00000000`779a0dad kernel32!CreateFileWImplementation+0x7d
00000000`0021e110 00000000`80000000
00000000`0021e118 00000000`00000000
00000000`0021e120 00000000`0000006c
00000000`0021e128 00000000`0039d2e0
00000000`0021e130 00000000`00000003
00000000`0021e138 00000000`00100000
00000000`0021e140 00000000`00000000
00000000`0021e148 00000000`00000000
00000000`0021e150 00000000`00440042
00000000`0021e158 00000000`03a7ed88
00000000`0021e160 00000000`00100000
00000000`0021e168 000007fe`f116bec7 mscorwks!DoNDirectCall__PatchGetThreadCall+0x7b
00000000`0021e170 00000000`00000000
00000000`0021e178 00000000`0021e1d0
00000000`0021e180 00000000`00000003
0:000> du 00000000`03a7ed88
00000000`03a7ed88 "C:\Users\BlastTS\Desktop\test.mc"
00000000`03a7edc8 "l"
複製程式碼
可以看到LaunchMediaCenter之後,ehshell基本就啥都不幹了。
#!bash
# Child-SP RetAddr : Args to Child : Call Site
00 00000000`0021e108 00000000`779a0dad : 00000000`80000000 00000000`00000000 00000000`0000006c 00000000`0039d2e0 : kernel32!CreateFileW
01 00000000`0021e110 000007fe`f116bec7 : 00000000`00000000 00000000`0021e1d0 00000000`00000003 000007fe`f0331830 : kernel32!CreateFileWImplementation+0x7d
*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v2.0.50727_64\mscorlib\c3beeeb6432f004b419859ea007087f1\mscorlib.ni.dll
02 00000000`0021e170 000007fe`f0207f71 : 000007fe`f0331830 000007fe`eff69210 00000000`03a7ee70 000007fe`f0287aea : mscorwks!DoNDirectCall__PatchGetThreadCall+0x7b
03 00000000`0021e230 000007fe`f0207e2e : 00000000`03a7edd8 00000000`03a7edd8 00000000`03a7ee00 000007fe`f0288e86 : mscorlib_ni+0x317f71
……………………………………………………
17 00000000`0021f3b0 000007ff`00250222 : 00000000`02681eb0 00000000`02681f30 00000000`0021f4a0 00000000`0021f060 : 0x7ff`00252817
18 00000000`0021f5b0 000007fe`f116a21a : 40ca06f5`582e02ef bdb2bd08`2c45789a 00000000`00000000 00000000`00000000 : 0x7ff`00250222
19 00000000`0021f5e0 00000001`3f7e161b : 00000000`01e10020 00000000`003bcfa8 00000000`0021f6a0 00000000`00000000 : mscorwks!UMThunkStubAMD64+0x7a
1a 00000000`0021f670 00000001`3f7e153a : 00000000`00312462 00000000`00000000 00000000`003283c0 00000000`01e10020 : ehshell!LaunchMediaCenter+0x14e
1b 00000000`0021f6c0 00000001`3f7e148d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ehshell!wWinMain+0xc3
1c 00000000`0021f920 00000000`779a59ed : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ehshell!InitializeCOMSecurity+0x382
1d 00000000`0021f9e0 00000000`77adb371 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
1e 00000000`0021fa10 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d
複製程式碼
由於MediaCenter以及ehshell.dll都是C# DLL,因此WinDbg看就顯得比較捉急了,我們將其反編譯,生成原始碼直接看:
工程十分龐大,在漫長的生成過程後,搜尋關鍵字可以看到這樣的邏輯:在載入MCL之後,首先程式解析.MCL檔案(通過System.XML),並在InternalExtensibilityAppInfo中為包含有Run屬性的MCL建立一個特殊的“AppEntryPoint”。
#!csharp
namespace MediaCenter.Extensibility
{
……
public ExtensibilityEntryPointInfo AddExternalAppEntryPoint(string strRun,string strTitle, string strDescription, string strCategory, object objContext,string strNowPlayingDirective)
{
IDictionarydictEntryPoint = CreateBaseEntryPointDict(Guid.NewGuid(), strTitle,strDescription, strCategory, objContext, strNowPlayingDirective);
dictEntryPoint[Run] = strRun;
return this.AddEntryPoint(dictEntryPoint);
}
複製程式碼
在處理AppEntryPoint時,程式啥校驗都不做,直接Process.Start啟動程式。當然,這是最初的版本,因此沒有任何彈框或判斷。
#!csharp
namespace MediaCenter.Extensibility
{
………………
internal classExtensibilityExternalAppEntryPointInfo : ExtensibilityEntryPointInfo
{
………………
internal override LaunchResult Launch(ref object objState)
{
………………
Process process = null;
try
{
process = Process.Start(base._strRun);
}
catch (Exception exception)
………………
複製程式碼
0x05 新版如何過濾?(16年6月補丁後版本)
簡單粗暴,所有包含Run/Url的全部彈窗二次確認。這也是微軟對前幾個嚴重問題所做的非常暴力的“折中”處理。
0x06 十年以上的漏洞
而有趣的是,這個漏洞看起來感覺有十年以上。MCL檔案最初就是為了在WMP中執行一個程式用的,這有點像HTA檔案,在查詢相關資訊的時候,我發現了06年(肯定還有更早的)論壇帖子就有關於MCL如何啟動程式的討論了。非常有意思的是,當時的人們並不覺得這個功能是一個安全漏洞,而只是一個功能而已,但是到了2015年第一次被人報告開始,微軟就給了這個問題最高的安全評級――但是這個原本是正常的“功能設計”。可見安全的概念在十年間轉換了很多。
例如下面的程式碼是曾經一個帖子“如何在Xbox 360上執行Zsnes”的解決程式碼,和MS15-100的利用程式碼一模一樣,發帖時間是2012年。
#!html
<application
name = "Zsnes"
SharedViewport = ""
NowPlayingDirective = ""
run = "C:/Zsnes/Zsnes.exe">
<capabilitiesrequired
directx="True"
audio="False"
video="False"
intensiverendering="True"
console="False"
cdburning="False" />
</application>
複製程式碼