1. 程式人生 > >IE安全系列之——昨日黃花:IE中的ActiveX(I)

IE安全系列之——昨日黃花:IE中的ActiveX(I)

IX.1 ActiveX的歷史和簡單的介紹

說到ActiveX,則不得不提到COM(元件物件模型)。COM出現時要解決的問題是,“能否把軟體做到和硬體一樣”,硬體的理想情況是比如有人規定了USB的規範,(Server)提供一個USB介面之後,(Client)只要正確實現了USB就可以讓二者相聯絡,擴展出更多的功能(USB鍵鼠,U盤,攝像頭,等等)。這之中Server(集線器)並不需要知道另一頭Client(鍵盤?滑鼠?)具體做啥的,只是知道我(Server)提供給你(Client)一個符合USB規範的介面,你(Client)弄上個東西連線成功就能用,至於你(Client)如何實現就不是我(Server)的事情了。

最初COM的出現也是為了解決類似的問題,例如我在IE裡面如何開啟一個WORD文件?針對它的解決方案稱為OLE(物件連線與嵌入)。1993年微軟釋出了OLE2,在這(ole32.dll)中提供的API也被日後認為是COM API。有了這些支援之後,微軟用它創造了OLE控制元件,並最終發展成為了在IE中常見的ActiveX控制元件。如果你用過VB的話,在VB裡就能發現大量此類控制元件的身影,如內嵌的Microsoft Viso等等。

enter image description here

每個ActiveX控制元件都由一個GUID(全域性唯一識別符號)來標識自己,GUID和UUID(全球唯一識別符號)差不多,只不過後者是OSF(開源軟體基金會)維護,前者是微軟自己的實現。

GUID是一個128位的數字,理論上是極大概率時間空間唯一的(隨機數生成的空間有2122的大小,按照類似生日問題的演算法,如果正確生成的話,和其他人生成的偶然重複概率為1/261)。

GUID演算法包括1490年開始到現在的分鐘數(保證時間唯一性),一個偽隨機數,和MAC地址(保證空間唯一性,沒有網絡卡的話會使用另一個隨機常量),API CoCreateGuid可以生成GUID。GUID的一般形式類似於{AFEE063C-05BA-4248-A26E-168477F49734}這樣。

在IE裡或者WSF或者HTA等支援OBJECT標籤的地方,可以通過它來載入ActiveX,OBJECT標籤的具體用法請見參考資料(1)。

enter image description here

圖:iDefense ComRaider ActiveX Fuzzer生成的WSF測試指令碼使用了Object標籤來載入被測ActiveX控制元件

IX.2 IE中的ActiveX

預設情況下,要在IE瀏覽器中執行ActiveX外掛,ActiveX外掛必須同時擁有Safe For Scripting/Safe For Init標記才可以執行。在IE發現HTML網頁中有ActiveX控制元件時,IE會依次檢查如下動作來確定控制元件是否可以安全載入和運用於程式碼中。

(1) IE會先檢查ActiveX控制元件是否設定了killbit,如果設定了Killbit,IE將不會載入這個控制元件;

(2)IE會確定這個控制元件是否派生了IObjectSafety,如果有的話,IE會通過這個介面檢查是否有設定Safe for Scripting/Safe For Init,如果沒有派生IObjectSafety,IE會在登錄檔檢查{7DD95802-9882-11CF-9FA9-00AA006C42C4} (Safe for Initialization)和 {7DD95801-9882-11CF-9FA9-00AA006C42C4}(Safe For Scripting).兩項。

這裡的具體內容在之前的一篇文章http://drops.wooyun.org/papers/5673有介紹,所以不重複敘述了。為了後續實驗方便,我們可以使用環境自己先弄個ActiveX,這裡以Microsoft Visual C++ 2010為例建立一個ActiveX控制元件。

首先,如圖建立一個新工程,簡單起見,在後一個視窗直接完成即可。

enter image description here

生成的對外介面定義檔案(XXX.idl)檔案是和該控制元件資訊有關的一個重要檔案,許多資訊都可以在此看到。例如“類資訊”中uuid就是ActiveX控制元件待會兒在IE中載入時所要用到的Classid。

enter image description here

由於我們只是演示,因此我們簡單的新增一個函式foo(): 在類檢視的介面處點選右鍵,選擇新增->方法

enter image description here

然後,新增一個void foo(),填寫之後點選完成:

enter image description here

這時VS會在多個地方新增這個函式的資訊,不過簡單起見,回到剛才的idl檔案中我們就可以看到添加了個新函式

enter image description here

在MSVC中直接F12過去:

enter image description here

這個位置就是新加函式的位置,為了方便演示,我們在裡面隨便彈一個對話方塊好了:

::MessageBox(0, L"hello wooyun", L"blast", MB_OK);

然後,我們右鍵選擇工程,編譯一個Release版的ocx控制元件:

enter image description here

之後,使用regsvr32 testActivex.ocx。

enter image description here

此時,該ActiveX就已經被註冊了,我們建立一個測試用的htm檔案吧,

<HTML>    
<OBJECT ID="test" WIDTH=300 HEIGHT=300 classid="CLSID:2D9835F7-9534-46C2-AE7A-C75098AA6105">  
</OBJECT>  
</HTML>  

CLSID請換成你的idl裡面的那個uuid。由於我們的ActiveX控制元件並未設定Safe For Scripting和Safe For Init,所以在本地域開啟時IE做了這個提示,點選是之後,就可以看到外掛執行起來了。

enter image description here

enter image description here

此時,我們也可以測試呼叫我們加入的foo()函式,在SCRIPT標籤中呼叫test.foo(),即可呼叫成功,可見IE中彈出了一個對話方塊:

enter image description here

要實現Safe For Scripting、Init,請這麼做: 標頭檔案(例如我的工程是testActivexCtrl.h)中加入#include <objsafe.h>,然後將 DECLARE_INTERFACE_MAP()

BEGIN_INTERFACE_PART(ObjSafe, IObjectSafety)   


    STDMETHOD_(HRESULT,   GetInterfaceSafetyOptions)   (     
    /*   [in]   */   REFIID   riid,   
    /*   [out]   */   DWORD   __RPC_FAR   *pdwSupportedOptions,   
    /*   [out]   */   DWORD   __RPC_FAR   *pdwEnabledOptions   
    );  

    STDMETHOD_(HRESULT,   SetInterfaceSafetyOptions)   (     
        /*   [in]   */   REFIID   riid,   
        /*   [in]   */   DWORD   dwOptionSetMask,   
        /*   [in]   */   DWORD   dwEnabledOptions   
        );  

END_INTERFACE_PART(ObjSafe);

加入class CtestActiveXCtrl : public COleControl塊中。

在對應CPP檔案的最後加入下列程式碼,記得將CtestActiveXCtrl換成你自己的類名。

BEGIN_INTERFACE_MAP(CtestActiveXCtrl,COleControl) 
  INTERFACE_PART(CtestActiveXCtrl,IID_IObjectSafety,ObjSafe) 
END_INTERFACE_MAP()  

ULONG FAR EXPORT CtestActiveXCtrl::XObjSafe::AddRef() 
{ 
  METHOD_PROLOGUE(CtestActiveXCtrl,ObjSafe) 
    return pThis->ExternalAddRef(); 
}  

ULONG FAR EXPORT CtestActiveXCtrl::XObjSafe::Release() 
{ 
  METHOD_PROLOGUE(CtestActiveXCtrl,ObjSafe) 
    return pThis->ExternalRelease(); 
}  

HRESULT FAR EXPORT CtestActiveXCtrl::XObjSafe::QueryInterface(REFIID iid,void FAR* FAR* ppvObj) 
{ 
  METHOD_PROLOGUE(CtestActiveXCtrl,ObjSafe) 
    return (HRESULT)pThis->ExternalQueryInterface(&iid,ppvObj); 
}  

const DWORD dwSupportedBits = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA; 
const DWORD dwNotSupportedBits=~dwSupportedBits;  

HRESULT STDMETHODCALLTYPE   
  CtestActiveXCtrl::XObjSafe::GetInterfaceSafetyOptions(   
  /*[in]*/ REFIID riid, 
  /*[out]*/ DWORD __RPC_FAR *pdwSupportedOptions, 
  /*[out]*/ DWORD __RPC_FAR *pdwEnabledOptions) 
{ 
  METHOD_PROLOGUE(CtestActiveXCtrl,ObjSafe) 
    HRESULT retval = ResultFromScode(S_OK); 

  IUnknown FAR* punkInterface; 
  retval = pThis->ExternalQueryInterface(&riid, (void **)&punkInterface); 
  if(retval != E_NOINTERFACE) 
  { 
    punkInterface->Release(); 
  } 

  *pdwSupportedOptions=*pdwEnabledOptions=dwSupportedBits; 
  return retval; 
}  

HRESULT STDMETHODCALLTYPE   
  CtestActiveXCtrl::XObjSafe::SetInterfaceSafetyOptions(   
  /*[in]*/ REFIID riid, 
  /*[in]*/ DWORD dwOptionSetMask, 
  /*[in]*/ DWORD dwEnabledOptions) 
{ 
  METHOD_PROLOGUE(CtestActiveXCtrl, ObjSafe) 

   IUnknown FAR* punkInterface; 
  pThis->ExternalQueryInterface(&riid, (void **)&punkInterface); 
  if(punkInterface)
  { 
    punkInterface->Release(); 
  } 
  else 
  { 
    return ResultFromScode(E_NOINTERFACE); 
  }  

  if(dwOptionSetMask & dwNotSupportedBits)  
  {   
    return ResultFromScode(E_FAIL); 
  }  

  dwEnabledOptions&=dwSupportedBits; 
  if((dwOptionSetMask&dwEnabledOptions)!=dwOptionSetMask) 
  { 
    return ResultFromScode(E_FAIL); 
  } 

  return ResultFromScode(S_OK); 
}

重新編譯,再次開啟IE時,這個控制元件就會直接載入起來而不會再彈出對話方塊了。

IX.3 Windows 10中ActiveX外掛變成了什麼樣

另一個大家比較關注的可能是Windows 10裡面的ActiveX情況,有一個好訊息是Microsoft Edge已經不支援ActiveX了,不過Windows 10同樣自帶的IE11則依然和Win7、Win8的IE11一樣,繼續支援ActiveX。

在Windows 10中讓我們做同樣的事情,

enter image description here

首先,Win10提供了IE和Edge瀏覽器,選擇使用Edge瀏覽器開啟時,頁面中並未載入該ActiveX控制元件:

enter image description here

可以看到test物件事實上只是一個CObjectElement而已,它並沒有載入ActiveX控制元件。使用工具檢視Edge的記憶體也可得出同樣結論——這個OCX並沒載入到記憶體。

enter image description here

enter image description here

再換用IE開啟這個頁面,在Win10自帶的IE11中,我們看到外掛正常運行了。

enter image description here

另外,在原先的IE中,Flash外掛也是靠ActiveX的形式活著的。在Edge中應該也考慮到了這個問題,Edge自帶一套Flash,所以使用者不需要額外去網站上下載安裝,這套Flash的形式是什麼呢?我們可以簡單看一下:

enter image description here

首先我們可以看到FLASH的載入程式碼依然和之前一樣,採用Object的方式載入:

enter image description here

不過之前的經歷我們知道Object在Edge中是不能像之前一樣簡單載入ActiveX的,那這裡是什麼呢?參考程序列表可以發現,Edge在執行到包含有Flash的頁面時,會啟動一個FlashUtil_ActiveX.exe 程序,這個程序名字十分奇怪,在仔細檢視Edge的程序模組後,我們可以發現,Edge其實還是保留著載入ActiveX控制元件的能力的,只不過控制在一個比較小的範圍內而已:

enter image description here

enter image description here

本章節只是介紹了ActiveX的歷史和一個示例ActiveX的編譯方法,而並沒有介紹有關Activex安全的部分,關於Activex漏洞的挖掘等內容將在後續章節介紹。

參考資料

(1] http://www.w3school.com.cn/tags/tag_object.asp

(2] http://www.ffri.jp/assets/files/monthly_research/MR201503_Windows_10_Technical_Preview_Security_Overview_ENG.pdf