CVE-2017-8464 分析

[TOC]shell

CVE-2017-8464(stuxnet 3.0) 分析

0xFF 前言

​ 之前大一聽網絡安全宣講會的時候,聽他們講那個震網事件,說插一個U盤就能夠在用戶不知情的狀況下執行任意代碼,以前一直都沒有搞懂究竟是怎麼作到的,因此如今就來嘗試來分析分析。這裏要分析的cve-2017-8464是一個LNK文件漏洞,控制面板快捷方式加載CPL的時候存在缺陷致使能夠加載指定DLL,從而執行任意代碼。windows

0x00 分析工具

  • IDA
  • WinHex
  • WINDBG
  • windows 7 sp1 x86 (no patch)

0x01 漏洞復現

1)、生成一個DLL用於測試

編寫一個測試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盤根目錄下。安全

2)、構造一個惡意的lnk二進制文件

使用winhex建立並編輯一個以下圖數據的LNK文件(後綴爲.lnk)。並將其放在任意的地方(我放的是桌面)網絡

3)、RUA!

​ ok,如今刷新一下桌面,wow!,會彈超屢次的對話框。app

0x02 POC細節

建議去看看微軟官方資料:less

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

相關文章
相關標籤/搜索