[TOC]shell
之前大一聽網絡安全宣講會的時候,聽他們講那個震網事件,說插一個U盤就能夠在用戶不知情的狀況下執行任意代碼,以前一直都沒有搞懂究竟是怎麼作到的,因此如今就來嘗試來分析分析。這裏要分析的cve-2017-8464是一個LNK文件漏洞,控制面板快捷方式加載CPL的時候存在缺陷致使能夠加載指定DLL,從而執行任意代碼。windows
編寫一個測試DLL,爲了看到效果,就彈一個MessageBox吧。api
#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盤根目錄下。安全
使用winhex建立並編輯一個以下圖數據的LNK文件(後綴爲.lnk)。並將其放在任意的地方(我放的是桌面)網絡
ok,如今刷新一下桌面,wow!,會彈超屢次的對話框。app
建議去看看微軟官方資料:less
紅色: 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
大機率是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"逆向分析"
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函數,傳入的參數分別是:
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
不知道,你是否好奇爲啥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的做用。
加載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]的話,把這個函數調通就好了。下圖是相關校驗代碼
那U盤怎麼利用這個呢?U盤插入電腦盤符可能有多少種狀況呢?26種,那就構造26個惡意lnk文件:
A:\a.dll B:\a.dll 以此類推。(a.dll放入U盤根目錄)。