CVE-2017-8464 分析
目錄
- CVE-2017-8464(stuxnet 3.0) 分析
- 0xFF 前言
- 0x00 分析工具
- 0x01 漏洞復現
- 1)、生成一個DLL用於測試
- 2)、構造一個惡意的lnk二進制文件
- 3)、RUA!
- 0x02 POC細節
- 0x03 分析
- 1)、bp LoadLibraryW
- 2)、Parse 惡意lnk文件的過程
- 3)、關於那個3
- 4)、關於讀取指定dll路徑的過程
- 0x04 總結
- 0x05 參考
CVE-2017-8464(stuxnet 3.0) 分析
0xFF 前言
? 以前大一聽網絡安全宣講會的時候,聽他們講那個震網事件,說插一個U盤就可以在用戶不知情的情況下執行任意代碼,之前一直都沒有搞懂到底是怎麽做到的,所以現在就來嘗試來分析分析。這裏要分析的cve-2017-8464是一個LNK文件漏洞,控制面板快捷方式加載CPL的時候存在缺陷導致可以加載指定DLL,從而執行任意代碼。
0x00 分析工具
- IDA
- WinHex
- WINDBG
- windows 7 sp1 x86 (no patch)
0x01 漏洞復現
1)、生成一個DLL用於測試
編寫一個測試DLL,為了看到效果,就彈一個MessageBox吧。
#include<Windows.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { if(dwReason == DLL_PROCESS_ATTACH) MessageBox(0, TEXT("Called me!"), TEXT("Cap"), 0); return TRUE; }
將生成的DLL名字改為a.dll,並將其放在漏洞機中的C盤根目錄下。
2)、構造一個惡意的lnk二進制文件
使用winhex創建並編輯一個如下圖數據的LNK文件(後綴為.lnk)。並將其放在任意的地方(我放的是桌面)
3)、RUA!
? ok,現在刷新一下桌面,wow!,會彈超多次的對話框。
0x02 POC細節
建議去看看微軟官方資料:
https://msdn.microsoft.com/en-us/library/dd871305.aspx
紅色: HeaderSize ,必須為0x4c
藍色: LinkCLSID ,必須為00021401-0000-0000-C000
黃色: LinkFlags , 為1 表示HasLinkTargetIDList
橙色:ItemID[0]
藍色:ItemID[1]
紅色:ItemID[2]
綠色:TerminalID,A 16-bits unsigned integer,this value must be zero
黑色:IDListSize , 0x6E = sizeof(IDListSize)+sizeof(ItemID[0])+sizeof(Item[1])+sizeof(Item[2])
ps: ItemID[0]和ItemID[1]的具體含義可以去看看參考2那篇分析文章
紅色:BlockSize
黃色:BlockSignature,這裏是0xA0000005的話表面這塊是SpecialFolderDataBlock
藍色:SpecialFolderID,3 代表的是CSIDL_CONTROLS
綠色:Offset,Specifies the location of the ItemID of the first child segment of the IDList specified by SpecialFolderID. This value is the offset, in bytes, into the link target IDList(從IDListSize後面開始的偏移)
紫色:TERMINAL_BLOCK,This value must be less than 0x00000004
0x03 分析
1)、bp LoadLibraryW
? 大概率是explorer.exe解析的,所以用windbg雙機調試,下條件斷點。(為了便於調試,請先將那個a.dll中調用MessageBox那行註釋掉,重新編譯生成新的a.dll放於漏洞機的C盤根目錄)。
先使用
!process 0 0
找到 explorer.exe 的EPROCESS 假設為 8781f510斷到指定進程環境下
.process -i -r -p 8781f510
使用條件斷點:
bp Kernel32!LoadLibraryW "$<C:\\windbgScript\\sp.txt"
sp.txt文件內容(絕對路徑:C:\windbgScript\sp.txt)
as /mu ${/v:dllname} poi(esp+4)
.if ($sicmp( "${dllname}", "C:\a.dll")=0) {.echo ${dllname}} .else {gc}
如果下不了斷點請先使用這個命令再加載下符號: !sym noisy;.reload /user *
,再檢查下絕對路徑是否是使用的\\(雙斜線),windbg符號路徑是否已經設置。
下好斷點後,鍵入
g
命令運行,漏洞機中刷新桌面.windbg 斷下:顯示如下內容:
kd> .if ($sicmp( "${dllname}", "C:\a.dll")=0) {.echo ${dllname}} .else {gc}
C:\a.dll
kernel32!LoadLibraryW:
001b:76653c01 8bff mov edi,edi好,查看棧回溯
kd> k
ChildEBP RetAddr
07edccc8 767b73ed kernel32!LoadLibraryW
07edcf24 769d259f SHELL32!CPL_LoadCPLModule+0x169
07edd5c8 769d26e6 SHELL32!CControlPanelFolder::_GetPidlFromAppletId+0x19c
07edd5f4 76767b0b SHELL32!CControlPanelFolder::ParseDisplayName+0x49
07edd678 7676f21f SHELL32!CRegFolder::ParseDisplayName+0x93
07edd6ec 7677083d SHELL32!ReparseRelativeIDList+0x137
07edd730 76770885 SHELL32!TranslateAliasWithEvent+0xa6
07edd748 7673e916 SHELL32!TranslateAlias+0x15
07edd774 7673e6ab SHELL32!CShellLink::_DecodeSpecialFolder+0xf9
07edea38 766fca50 SHELL32!CShellLink::_LoadFromStream+0x39f
07edec68 766fc9bf SHELL32!CShellLink::_LoadFromFile+0x90
07edec78 766fc914 SHELL32!CShellLink::Load+0x32
.....看到SHELL32!CShellLink::_LoadFromFile,猜測是從這個函數開始解析lnk文件的。使用
lm v m shell32
命令查看得到shell32.dll的在漏洞機中的路徑:
kd> lm v m shell32
start end module name
....
Image path: C:\Windows\system32\SHELL32.dll
....ok,找到shell32.dll 並把它拖進IDA pro中,開始我們的F5"逆向分析"
2)、Parse 惡意lnk文件的過程
CShellLink::_LoadFromStream
在CShellLink::_LoadFromStream中,首先會讀取惡意lnk文件頭4個字節,判斷是不是等於0x4C。接著讀取惡意lnk文件頭+0x4處(讀取文件流會造成文件指針移動,剛剛已經讀了4個了,所以下一次開始讀取時文件指針偏移加4)繼續讀取惡意lnk文件ShellLinkHeader結構的剩下0x48個字節的數據並放到this->offset_0n244處(這個this就是CShellLink這個對象的實例),接著判斷LinkCLSID(16 bytes)是否等於00021401-0000-0000-C000-000000000046.
? 之後取LinkFlags 檢查有哪些結構在ShellLinkHeader後面存在,首先檢查的是HasLinkTargetIDList ,在我們構造這個POC中,是成立的。(pCShellLink+244+16 就是LinkFlags,因為this->offset_0n244處存的是ShellLinkHeader結構剩下的的0x48字節,LinkCLSID後面緊跟著的就是LinkFlags字段,16是LinkCLSID的大小)。下圖中的v6會等於0,所以會調用CShellLink::_LoadIDList,通過逆向(F5)這個函數得知做的功能是分配一塊內存加載lnk文件的LINKTARGET_IDLIST段(去掉這個段的前2個字節,即ItemID[0]+ItemID[1]+ItemID[2])到this->0n188處。這個函數執行後,當前文件流指針就指向了LINKTARGET_IDLIST的後面,對於我們這個POC來說也就是EXTRA_DATA段了。
? 接著從文件流讀取EXTRA_DATA到this->offset_0n228處。
? 下面那個是DWORD*的this+47,所以要乘以4即this->offset_0n188,即判斷是不是存在LinkTargetIDList.IDList,我們這個POC是存在的,所以調用CShellLink::_DecodeSpecialFolder(this);
CShellLink::_DecodeSpecialFolder(CShellLink *this)
首先調用SHFindDataBlock查找this->offset_0n228處(EXTRA_DATA)是否有KnownFolderDataBlock(它的BlockSignature 是0xA000000B),顯然我們的POC中沒有構造這個Block。所以27行的v2會返回0,接著會執行第41行,調用SHFindDataBlock查找this->offset_0n228處(EXTRA_DATA)是否有SpecialFolderDataBlock(它的BlockSignature是0xA0000005),而我們的POC中的確存在這個Block,所以SHFindDataBlock返回指向SpecialFolderDataBlock的指針。
接著取pSpecialFolderDataBlock->SpecialFolderID(偏移為8)作為SHCloneSpecialIDList函數的第二個參數,根據MSDN上關於SHCloneSpecialIDList的介紹,它是用來獲取一個KnownFolder的ITEMIDLIST結構,我們poc中構造的是3,
Retrieves a pointer to the ITEMIDLIST structure that specifies a special folder.
在vs中查看得知3對應的是Control Panel,所以這個函數返回的是一個指向Control Panel的ITEMIDList結構的指針(這個是個關鍵,詳細的請看後面我單獨會寫)。
接著會調用TranslateAlias函數,傳入的參數分別是:
- this->offset_0n188,即ItemID[0],ItemID[1],ItemID[2]
- v11 是指向一塊存放 ItemID[0]和ItemID[1]的內存
- 控制面板的ITEMIDLIST,用於之後綁定
TranslateAlias
TranslateAliasWithEvent
SHBindToObject函數MSDN上是有介紹的:
Retrieves and binds to a specified object by using the Shell namespace IShellFolder::BindToObject method.
所以要觸發到後面調用CControlPanelFolder::的例程,那麽一定要獲取到ControlPanel的IDList
ReparseRelativeIDList
58行的調用也是個關鍵,是調用這個將poc中IDList[2]中的"C:\a.dll" 加載到CControlPanelFolder::s_dsaTemporaryAppId裏面的,之後LoadLibraryW的那個參數正是從這個CControlPanelFolder::_dsaTemp裏面獲取的。這個函數裏面做了些什麽我後面會單獨出來講。
CRegFolder::ParseDisplayName
CControlPanelFolder::ParseDisplayName
CControlPanelFolder::_GetPidlFromAppletId
首先調用的是35行那句,調用後從一個
下面來看看CControlPanelFolder::_GetAppletPathForTemporaryAppId發生了啥:
CPL_LoadCPLModule
觸發調用指定Dll
3)、關於那個3
不知道,你是否好奇為啥SpecailFolderID一定要指定為3?那個3到底是啥意思呢?我開始的時候理解的是就是指定讀取TargetIDList的第3個IDList,即IDList[2]。其實不是。
ok,讓我們看看當SpecialFolderBlock.SpecialFolderID為3的時候,SHCloneSpecialIDList的返回值是什麽。
我在上面Parse過程中分析到這個地方的時候說過,SHCloneSpecialIDList根據傳入的csidl,獲取指定文件夾的IDList,為了驗證這裏獲取到的的確是控制面板的IDList。我們可以打開控制面板,任意選擇一個CPL創建一個快捷方式,我這裏創建了一個管理工具的快捷方式。用winhex打開它,哇IDList[0]和IDList[1]和上圖中返回的一模一樣。
好,下面我們把我們構造的那個lnk文件的SpecialFolderID改為一個非3的數字,這裏我們改成1,刷新桌面,並未觸發調用a.dll。
還是在SHCloneSpecialIDList處下斷,下面我們來試試將SHCloneSpecialIDList返回的IDList數據強行改成CSIDL_CONTROLS(3)的數據,看看會不會有奇跡出現。
嘿嘿,觸發了。(為啥還要eq eax+20 0呢?因為我發現只改那0x20個字節的數據為CSIDL_CONTROLS的返回數據不會觸發加載a.dll,還需要再多改8個字節才行)
註意,我上面不是改的SHCloneSpecialIDList的參數,樣本裏面指定的還是是1,那SHCloneSpecialIDList的第二個參數就是1,我改的是它返回的數據,改成控制面板裏面的項的IDList[0]和IDList[1]就行了。所以間接證明了那個3的作用。
4)、關於讀取指定dll路徑的過程
加載dll路徑到CControlPanelFolder::s_dsaTemporaryAppId那個裏面是在ReparseRelativeIDList調用DisplayNameOfAsString函數做的。
流程如下:
CControlPanelFolder::GetModule
CControlPanelFolder::GetModuleMapped
CControlPanelFolder::GetExecName
CControlPanelFolder::_GetFullCPLName
CControlPanelFolder::_GetTemporaryAppIdForApplet
CControlPanelFolder::GetDetailsEx
GetStringProperty
CControlPanelFolder::GetDisplayNameOf
CRegFolder::GetDisplayNameOf
DisplayNameOfAsString //開始處
所以我們poc中的字符串必須要為寬字符字串,且必須要從偏移24字節出開始寫。
下面再來看看是如何校驗IDList[2]是否合法的。
但是其實上面那個函數只是CControlPanelFolder::_IsValid的子過程,真正校驗包含dll路徑的IDList[2]是否合法的函數其實是CControlPanelFolder::_IsValid。所以要構造惡意的IDList[2]的話,把這個函數調通就行了。下圖是相關校驗代碼
0x04 總結
? 那U盤怎麽利用這個呢?U盤插入電腦盤符可能有多少種情況呢?26種,那就構造26個惡意lnk文件:
A:\a.dll B:\a.dll 以此類推。(a.dll放入U盤根目錄)。
0x05 參考
https://msdn.microsoft.com/en-us/library/dd871305.aspx
https://paper.tuisec.win/detail/bb5e0d987cf23cc
CVE-2017-8464 分析