iT邦幫忙

2023 iThome 鐵人賽

DAY 28
0
Security

Windows Security 101系列 第 28

[Day28] Introduction to Logical Privilege Escalation on Windows (Part 5): COM

  • 分享至 

  • xImage
  •  

今天要來介紹的是 Component Object Model (COM),內容主要是以 James Forshaw 的演講筆記為主,再搭配一些相關的文章。

COM 在 Windows 的使用非常廣泛,像是 Office 內建的 Object Linking and Embedding (OLE) 和 IE 瀏覽器用到的 ActiveX 都是基於 COM。

James Forshaw 的 COM in Sixty Seconds 中提到 COM 的發明在一開始是為了支援 Application 間的 Interoperability,像是在 Word 中可以操作 Excel,即使兩個 Application 可能是以不同的程式語言實作,甚至是在不同的電腦。

Common ABI

Vtable 的實作,讓人可以透過傳遞指標來呼叫 method

|----------------|    |----------------|    |-----------------|
| Object Pointer | -> | Vtable Pointer | -> | Method1 Pointer |
|----------------|    |----------------|    |-----------------|
					  |      Data1     |    | Method2 Pointer |
					  |----------------|    |-----------------|

上圖對應到下方程式碼:

struct ObjectTable {
	void (*SetInt)(struct Object* This, int i);
	int (*GetInt)(struct Object* This);
};

struct Object {
	struct ObjectVTable* Vtbl;
	...
}

struct Object* obj;
obj-Vtbl->SetInt(obj, 1234);

Object 的開頭會是 Vtable 的指標,在使用 method 的時候都是從 Vtable 呼叫,並且第一個參數都是 Object 本身。

The Casting Problem

如果是在一個 Object 具有多個 interface 的狀況

struct Interface1 {
	virtual void A() = 0;
};
struct Interface2 {
	virtual void B() = 0;
};

class Object : public Interface1, public Interface2 {
	void A() {}
	void B() {}
};

Object 衍生了多個 interface

Interface1* intf1 = new Object;

將 Object cast 成 Interface1

Interface2* intf2 = (Interface2*)intf1;

再將 intf1 cast 成 Interface2 時,會發生非預期的問題。

因此 COM 提供了 IUnknown 作為所有 COM 的標準介面

DEFINE_GUID(IID_IUnknown, 00000000-0000-0000-C000-000000000046);
struct IUknown {
	HRESULT QueryInterface(GUID& iid, void** ppv);
	LONG AddRef();
	LONG Release();
}

所有的 COM 都是從 IUnknown 衍生。

Interface ID (IID) 是唯一的。

AddRef 和 Release 是用來管理 COM Object 的生命週期,而且因為 COM Object 的分配不是在 C++ heap,因此需要特別管理,避免 memory leak 等問題。

QueryInterface 是用來檢查 Object 是否支援某種 interface (IID),有支援的話才會返回 pointer,這樣就能解決 Casting Problem。

struct Interface1 : public IUknown {};
struct Interface2 : public IUknown {};

Interface2* intf2;
if (intf1->QueryInterface(IID_Interface2, (void**)&intf2) >= 0) {
	intf2->Release();
}

使用完 intf2 後執行 Release。

Class Registration

COM Object 需要在 registry HKEY_CLASSES_ROOT\CLSID\ 中註冊。每個 CLSID 就代表一種 COM Object。

InprocHandler32 路徑下的 DLL 就是 COM Object 的實作。

https://ithelp.ithome.com.tw/upload/images/20231012/20120098zlSumaKJyU.png

Class Factories

DEFINE_GUID(IID_ClassFactory, 00000001-0000-0000-C000-000000000046)

struct IClassFactory : public IUknown {
	HRESULT CreateInstance(
		IUknown *pUnkOuter,
		REFIID riid,
		void **ppvObject
	);
	HRESULT LockServer(BOOL fLock);
}

IClassFactory 提供 CreateInstance Method,讓使用者更方便建立多個物件。

CoGetClassObject

透過系統登錄中有 CLSID 的類別物件建立多個物件。透過 IClassFactory Interface 進行溝通。

HRESULT CoGetClassObject(
  [in]           REFCLSID rclsid,
  [in]           DWORD    dwClsContext,
  [in, optional] LPVOID   pvReserved,
  [in]           REFIID   riid,
  [out]          LPVOID   *ppv
);

rclsid 指定要存取的 CLSID。

dwClsContext 指定要使用的 server,以下是常見的幾種:

  • CLSCTX_INPROC_SERVER
  • CLSCTX_INPROC_HANDLER
  • CLSCTX_LOCAL_SERVER
  • CLSCTX_REMOTE_SERVER

CoCreateInstance*

在 Local 端,可以使用 CoCreateInstance。

而在 Remote 端可以使用 CoCreateInstanceEx。

HRESULT CoCreateInstanceEx(
  [in]      REFCLSID     Clsid,
  [in]      IUnknown     *punkOuter,
  [in]      DWORD        dwClsCtx,
  [in]      COSERVERINFO *pServerInfo,
  [in]      DWORD        dwCount,
  [in, out] MULTI_QI     *pResults
);

pServerInfo 指定 Remote 端的資訊。

In-Process Server

剛剛在 registry 中的 InProcServer32 的 server.dll 會有 exported function DLLGetClassObject 讓 process 載入。

下圖是 Client 使用 InProcServer32 的 COM Object 的過程

		                               |----------------|
							ˊ--------> | Class Instance |
						ˊ			   |----------------|
					ˊ					         ^
				ˊ							     |
|-------------|                        |----------------|
|   Client    | ---------------------> | Class Factory  |
|-------------|                        |----------------|
                ˋ                                ^
                    ˋ                            |
                        ˋ              |----------------|
						    ˋ--------> |   server.dll   |
						               |----------------|  

Client 先透過 DLLGetClassObject 取得 class object,在透過 Class Factory 取得 Class Instance,最後就可以使用該 Interface。

COM Apartment

在 single process 的環境中,以 COM Apartment 將每個 COM Instance 隔開,主要分為兩種狀況:

  • MultiThread Appartment (MTA)
    • 同一個 process 中,兩個不同的 thread 在同個 apartment,兩個 thread 都可以直接使用 COM Object
  • SingleThread Apartment (STA)
    • 同一個 process 中,兩個不同的 thread 在不同的 apartment,必須透過 marshalling 的方式,打包並傳遞參數

CoInitializeEx 可以用來設定 MultiThread Appartment。

另外,LocalServer32 的 cross processes 環境下,則會使用 DCOM。

下一篇,我將會介紹 DCOM。

References


上一篇
[Day27] Introduction to Logical Privilege Escalation on Windows (Part 4): RPC
下一篇
[Day29] Introduction to Logical Privilege Escalation on Windows (Part 6): DCOM
系列文
Windows Security 10130
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言