概述:本文是基于伪装PEB进行越权复制文件相关学习研究进行的拓展文章,主要记录通过COM组件越权复制文件。
[toc]
本篇博客参考的文章较多,主要描述和记录一下伪装PEB进行越权复制的操作。
伪装PEB进行越权复制
伪装PEB
主要操作就是修改当前进程的 ImagePathName
、 CommandLine
、 FullDllName
、BaseDllName
这几个变量,通过修改成为与系统可信的进程一样的内容进而实现越权操作。3gstudent 提供了Demo,可以去Github看下相关逻辑,这里不做过多解释。
-
_RTL_USER_PROCESS_PARAMETERS.ImagePathName
-
_RTL_USER_PROCESS_PARAMETERS.CommandLine(可选)
-
_LDR_DATA_TABLE_ENTRY.FullDllName
-
_LDR_DATA_TABLE_ENTRY.BaseDllName
源代码路径:https://github.com/3gstudent/Use-COM-objects-to-bypass-UAC/blob/master/MasqueradePEB.cpp
原理:COM组件通过 Process Status API (PSAPI) 读取进程PEB结构中的Commandline来识别它们正在运行的进程。如果将进程的 Path 改成可信文件(如explorer.exe),就能够欺骗PSAPI,调用COM组件IFileOperation实现越权复制
PSAPI:进程状态API,主要提供用于检索一下信息的函数集:
COM调用堆栈如下所示
如下所示为启动一个uac进程到该进程发起rpc的调用堆栈,须知当前进程的提权是通过 RPC 调用到 Appinfo 服务的。流程如下:
- uac 通过 RPC 请求到 AppInfo 服务
- APPInfo 通过
AiLaunchProcess
接口创建 consent.exe
进程,调用时会传入一个结构体地址,调用命令行为 consent.exe ppid 结构体长度 结构体地址
- consent.exe 会根据用户操作将结果写入结构体中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| [0x0] RPCRT4!LRPC_BASE_CCALL::DoAsyncSend+0x21c 0xbddbc4 0x76538061 [0x1] RPCRT4!LRPC_CCALL::AsyncSend+0x61 0xbddc48 0x7653796e [0x2] RPCRT4!I_RpcSend+0x7e 0xbddc64 0x75dda34a [0x3] combase!CAsyncCall::RpcSendRequest+0xaf 0xbddc8c 0x75e59faa [0x4] combase!ThreadSendReceive+0xa4f 0xbdddd0 0x75dd77f8 [0x5] combase!CSyncClientCall::SwitchAptAndDispatchCall+0xadb 0xbdddd0 0x75dd77f8 [0x6] combase!CSyncClientCall::SendReceive2+0xbca 0xbdddd0 0x75dd77f8 [0x7] combase!SyncClientCallRetryContext::SendReceiveWithRetry+0x29 0xbddfa8 0x75e5c0a7 [0x8] combase!CSyncClientCall::SendReceiveInRetryContext+0x29 0xbddfa8 0x75e5c0a7 [0x9] combase!ClassicSTAThreadSendReceive+0x98 0xbddfa8 0x75e5c0a7 [0xa] combase!CSyncClientCall::SendReceive+0x2a7 0xbde06c 0x75de0468 [0xb] combase!CClientChannel::SendReceive+0x79 0xbde258 0x76516b23 [0xc] combase!NdrExtpProxySendReceive+0xc8 0xbde258 0x76516b23 [0xd] RPCRT4!NdrClientCall2+0x9e3 0xbde280 0x75eaeaa0 [0xe] combase!ObjectStublessClient+0x70 0xbde6d0 0x75ea6a3f [0xf] combase!ObjectStubless+0xf 0xbde6f0 0x75e113f5 [0x10] combase!CRpcResolver::DelegateActivationToSCM+0x30e 0xbde700 0x75e8c69c [0x11] combase!CRpcResolver::CreateInstance+0x14 0xbde80c 0x75e12d54 [0x12] combase!CClientContextActivator::CreateInstance+0x144 0xbde828 0x75e124d4 [0x13] combase!ActivationPropertiesIn::DelegateCreateInstance+0xc4 0xbdea88 0x75e3a762 [0x14] combase!ICoCreateInstanceEx+0xc12 0xbdead4 0x75e399d1 [0x15] combase!CComActivator::DoCreateInstance+0x231 0xbdedd8 0x75f4bec1 [0x16] combase!CComActivator::StandardCreateInstance+0x81 0xbdeecc 0x75ba8686 [0x17] ole32!CLUAMoniker::CreateInstance+0x126 0xbdf73c 0x63e8f20f [0x18] comsvcs!CNewMoniker::BindToObject+0x12f 0xbdf77c 0x75b869cd [0x19] ole32!CCompositeMoniker::BindToObject+0x19d 0xbdf7f8 0x75b84f9e [0x1a] ole32!CoGetObject+0xbe 0xbdf82c 0x74e70e88 [0x1b] windows_storage!CoCreateInstanceAsAdmin+0xb2 0xbdf878 0x74e7e364 [0x1c] windows_storage!CFileOperation::_CreateElevatedCopyengine+0x43 0xbdfb80 0x74e8293a [0x1d] windows_storage!CFileOperation::_RunElevatedOperation+0x4d 0xbdfbf4 0x74ce1761 [0x1e] windows_storage!CFileOperation::_ProcessLUAOperations+0x118056 0xbdfc28 0x74bc878a [0x1f] windows_storage!CFileOperation::PrepareAndDoOperations+0x238 0xbdfc7c 0x74bc2274 [0x20] windows_storage!CFileOperation::PerformOperations+0xd4 0xbdfcec 0xa03f55 [0x21] MasqueradePEBtoCopyfile!wmain+0x365 0xbdfd1c 0xa047fe [0x22] MasqueradePEBtoCopyfile!__scrt_wide_environment_policy::initialize_environment+0x2e 0xbdfe80 0xa04667 [0x23] MasqueradePEBtoCopyfile!__crt_char_traits<wchar_t>::tcscpy_s<wchar_t * &,unsigned int,wchar_t const * const &>+0x1d7 0xbdfe94 0xa044fd [0x24] MasqueradePEBtoCopyfile!__crt_char_traits<wchar_t>::tcscpy_s<wchar_t * &,unsigned int,wchar_t const * const &>+0x6d 0xbdfef0 0xa04878 [0x25] MasqueradePEBtoCopyfile!wmainCRTStartup+0x8 0xbdfef8 0x76cdfcc9 [0x26] KERNEL32!BaseThreadInitThunk+0x19 0xbdff00 0x77607c6e [0x27] ntdll!__RtlUserThreadStart+0x2f 0xbdff10 0x77607c3e [0x28] ntdll!_RtlUserThreadStart+0x1b 0xbdff6c 0x0
|
CoGetObject
的创建管理员对象
到达 AppInfo 后, 调用堆栈如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| [0x0] ntdll!NtCreateUserProcess 0xdf231fc698 0x7ff9da72b473 [0x1] KERNELBASE!CreateProcessInternalW+0xfe3 0xdf231fc6a0 0x7ff9da728a03 [0x2] KERNELBASE!CreateProcessAsUserW+0x63 0xdf231fdc70 0x7ff9daa4de30 [0x3] KERNEL32!CreateProcessAsUserWStub+0x60 0xdf231fdce0 0x7ff9d4a4526b [0x4] appinfo!AiLaunchProcess+0x8eb 0xdf231fdd50 0x7ff9d4a4789d [0x5] appinfo!AiLaunchConsentUI+0x51d 0xdf231fec40 0x7ff9d4a471e3 [0x6] appinfo!AiCheckLUA+0x343 0xdf231fee60 0x7ff9d4a62ff1 [0x7] appinfo!AipGetTokenForService+0x245 0xdf231ff040 0x7ff9d4a639b6 [0x8] appinfo!RAiGetTokenForCOM+0x206 0xdf231ff160 0x7ff9db4ab4b3 [0x9] RPCRT4!Invoke+0x73 0xdf231ff220 0x7ff9db50c5ea [0xa] RPCRT4!Ndr64AsyncServerWorker+0x39a 0xdf231ff2d0 0x7ff9db489188 [0xb] RPCRT4!DispatchToStubInCNoAvrf+0x18 0xdf231ff3e0 0x7ff9db46a3a6 [0xc] RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1a6 0xdf231ff430 0x7ff9db469fd6 [0xd] RPCRT4!RPC_INTERFACE::DispatchToStubWithObject+0x186 0xdf231ff510 0x7ff9db47730f [0xe] RPCRT4!LRPC_SCALL::DispatchRequest+0x16f 0xdf231ff5b0 0x7ff9db4768c8 [0xf] RPCRT4!LRPC_SCALL::HandleRequest+0x7f8 0xdf231ff680 0x7ff9db475eb1 [0x10] RPCRT4!LRPC_ADDRESS::HandleRequest+0x341 0xdf231ff790 0x7ff9db47591e [0x11] RPCRT4!LRPC_ADDRESS::ProcessIO+0x89e 0xdf231ff830 0x7ff9db47a032 [0x12] RPCRT4!LrpcIoComplete+0xc2 0xdf231ff970 0x7ff9dc990330 [0x13] ntdll!TppAlpcpExecuteCallback+0x260 0xdf231ffa10 0x7ff9dc9c2f86 [0x14] ntdll!TppWorkerThread+0x456 0xdf231ffa90 0x7ff9daa47344 [0x15] KERNEL32!BaseThreadInitThunk+0x14 0xdf231ffd90 0x7ff9dc9c26b1 [0x16] ntdll!RtlUserThreadStart+0x21 0xdf231ffdc0 0x0
|
越权原理
基于以上描述,我们大概了解了启动一个UAC进程的流程和过程。那么,到底是在哪一步判断中进程的UAC鉴权操作。
处理逻辑:AIS(appinfo.dll) 服务:处理提升请求
主要看一下 AipGetTokenForService
函数。以下 2 种情况可能不会弹 UAC 对话框,会自动提升至管理员权限(越权也是基于白名单):
UAC 流程
简单说明一下UAC的请求流程。
- 程序配置为自动提升
如果程序中配置了 autoElevate 为 true,会尝试自动提升
1. 先判断是否限制自动提权策略
2. 判断是否设置了 autoElevate
- 白名单
判断要执行的程序是否属于白名单,在白名单之内就调用 AipIsValidAutoApprovalEXE 函数检查程序签名等信息,如果不在就基本结束这个函数了
依靠上述两个判断还是不够的,在 appinfo 中还有如下所示的几个列表。
-
g_lpExcludedWindowsDirs
-
g_lpIncludedWindowsDirs
-
g_lpIncludedSystemDirs
-
g_lpIncludedPFDirs
consent进程执行逻辑
在进行上述判断之后会创建 consent 进程,传递父进程id以及内存地址
- 写入consent进程参数
- 拉起consent进程
Buffer 就是 consent 进程的 commandline。
- 继续分析 consent 进程的执行逻辑。调用后,首先对命令行进行了解析。
- 随后读取了传入的结构体
- 提权操作结束后,将用户操作结果写回给 appinfo 进程直接中。
弹窗逻辑
但是中间弹窗的过程被省略了,这里可以调试分析一下。主要就是通过查看COM组件有没有自提升权限进而判断是否需要弹窗,查看 CuiIsCOMClassAutoApprovable
即可。
越权逻辑
越权主要还是基于以下两点,流程上和 consent.exe 是不涉及的。
-
各类UAC白名单程序的DLL劫持(Dll Hijack)
-
各类提升权限的COM接口利用(Elevated COM interface)
可利用的COM接口有哪些,可以在 HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\UAC\\COMAutoApprovalList
注册表下查看值为 键值项为1 的项为可以自提升的。
本文伪装PEB所示的代码就是通过白名单 g_lpIncludedSystemDirs
实现的。检查的堆栈为:
appinfo!RAiGetTokenForCOM -> appinfo!AipGetTokenForService -> appinfo!AiCheckSecureApplicationDirectory -> appinfo!AipCheckSecureWindowsDirectory 或者 AipCheckSecurePFDirectory(取决于传递的路径)
修改为 C:\windows\explorer.exe
之后,其相当于从白名单启动的进程,因此可以绕过consent,直接获取权限。
文件启动路径
获取程序运行路径的堆栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| [0x0] combase!<lambda_7438a189e512c84b56128b16ff6946f3>::operator()+0x9d 0x71d5d8 0x75070f45 [0x1] combase!CRpcResolver::GetConnection+0x70 0x71db48 0x75051915 [0x2] combase!CoInitializeSecurity+0xe5 0x71db78 0x750ed384 [0x3] combase!InitializeSecurity+0x4f 0x71ddec 0x7509196b [0x4] combase!CComApartment::InitRemoting+0x8d 0x71de20 0x750b1ce2 [0x5] combase!CComApartment::StartServer+0x60 0x71de64 0x7506dd3d [0x6] combase!InitChannelIfNecessary+0xb2 0x71de64 0x7506dd3d [0x7] combase!CGIPTable::RegisterInterfaceInGlobalHlp+0x4d 0x71de80 0x7506dce5 [0x8] combase!CGIPTable::RegisterInterfaceInGlobal+0x15 0x71dec0 0x7453b91b [0x9] windows_storage!CFreeThreadedItemContainer::Initialize+0x8b 0x71ded8 0x7453b1b0 [0xa] windows_storage!CFSPropertyStoreFactory_CreateInstance+0x360 0x71df0c 0x7451ab6b [0xb] windows_storage!CFSFolder::_BindToChild+0x10e 0x71e15c 0x7454e000 [0xc] windows_storage!CFSFolder::_Bind+0xa10 0x71e928 0x7454d279 [0xd] windows_storage!CFSFolder::BindToObject+0x4c9 0x71ec3c 0x74562745 [0xe] windows_storage!CShellItem::BindToHandler+0x525 0x71ef1c 0x7455e92e [0xf] windows_storage!CShellItem::_GetPropertyStoreWorker+0x2ee 0x71f1ec 0x7457313d [0x10] windows_storage!CShellItem::GetPropertyStoreForKeys+0x11d 0x71f260 0x745a1977 [0x11] windows_storage!CShellItem::GetString+0x57 0x71f4e8 0x745a18e3 [0x12] windows_storage!IShellItem_GetFileName+0x5b 0x71f530 0x745a0934 [0x13] windows_storage!CPendingOperation::AddToTree+0x114 0x71f55c 0x745a07fb [0x14] windows_storage!CCopyTree::InitializeFromPendingList+0x9a 0x71f7b0 0x7459224f [0x15] windows_storage!CFileOperation::PerformOperations+0xaf 0x71f7d4 0x543f55 [0x16] MasqueradePEBtoCopyfile!wmain+0x365 0x71f818 0x5447fe [0x17] MasqueradePEBtoCopyfile!__scrt_wide_environment_policy::initialize_environment+0x2e 0x71f97c 0x544667 [0x18] MasqueradePEBtoCopyfile!__crt_char_traits<wchar_t>::tcscpy_s<wchar_t * &,unsigned int,wchar_t const * const &>+0x1d7 0x71f990 0x5444fd [0x19] MasqueradePEBtoCopyfile!__crt_char_traits<wchar_t>::tcscpy_s<wchar_t * &,unsigned int,wchar_t const * const &>+0x6d 0x71f9ec 0x544878 [0x1a] MasqueradePEBtoCopyfile!wmainCRTStartup+0x8 0x71f9f4 0x7612fcc9 [0x1b] KERNEL32!BaseThreadInitThunk+0x19 0x71f9fc 0x76fd7c6e [0x1c] ntdll!__RtlUserThreadStart+0x2f 0x71fa0c 0x76fd7c3e [0x1d] ntdll!_RtlUserThreadStart+0x1b 0x71fa68 0x0
|
获取dll的堆栈
1 2
| [0x0] ntdll!LdrGetDllFullName 0x71d974 0x76de5a56 [0x1] KERNELBASE!GetModuleFileNameW+0x46 0x71d978 0x750ff6ee
|