1. 程式人生 > >CVE-2017-8464 分析

CVE-2017-8464 分析

ane file details serve shell32 一個 導致 漏洞復現 加載dll

目錄

  • 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 分析