Wednesday, September 22, 2010

SeDebugPrivilege and Integrity Level

Windows Integrity mechanism was introduced in Vista/Win 2008. With this feature, operating system assigns so-called integrity level to process or thread. There are 5 integrity levels - Untrusted level (0x0000), Low integrity level (0x1000), Medium integrity level (0x2000), High integrity level (0x3000), System integrity level (0x4000). An interesting point is any process or thread with lower integrity level cannot access higher integrity level process or thread.

When an administrator runs an appliication with normal mode, its integrity level is Medium. If an administrator runs the application in elevated mode, its integrity level becomes High integrity level. Then can the elevated application access any process having System integrity level? The anwser is no. The attempt to access system integrity level process or thread will return Access Denied exception.

So if we cannot access any System integrity level process even if the current user is administrator, how can we solve this problem? We know there are various user scenarios that need to access system process.

There is a way. If SeDebugPrivilege is set in elevated process, the process/thread can access System integrity level process or thread. The below code shows one way of enabling (or disabling) SeDebugPrivilege in the access token.

BOOL EnableDebugPrivilege(BOOL bEnable)
{
HANDLE hToken = NULL;

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
return FALSE;

LUID luid;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid ))
return FALSE;

TOKEN_PRIVILEGES tokenPriv;
tokenPriv.PrivilegeCount = 1;
tokenPriv.Privileges[0].Luid = luid;
tokenPriv.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;

if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
return FALSE;

return TRUE;
}

Once the code is executed, we can check the SeDebugPrivilege
by running !token in the debugger.

0:011> !token –n
Privs:
00 0x000000005 SeIncreaseQuotaPrivilege Attributes -
01 0x000000007 SeTcbPrivilege Attributes -
02 0x000000008 SeSecurityPrivilege Attributes -
03 0x000000009 SeTakeOwnershipPrivilege Attributes -
04 0x00000000a SeLoadDriverPrivilege Attributes -
05 0x00000000b SeSystemProfilePrivilege Attributes -
06 0x00000000c SeSystemtimePrivilege Attributes -
07 0x00000000d SeProfileSingleProcessPrivilege Attributes -
08 0x00000000e SeIncreaseBasePriorityPrivilege Attributes -
09 0x00000000f SeCreatePagefilePrivilege Attributes -
10 0x000000011 SeBackupPrivilege Attributes -
11 0x000000012 SeRestorePrivilege Attributes -
12 0x000000013 SeShutdownPrivilege Attributes -
13 0x000000014 SeDebugPrivilege Attributes - Enabled
14 0x000000016 SeSystemEnvironmentPrivilege Attributes -
15 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
16 0x000000018 SeRemoteShutdownPrivilege Attributes -
17 0x000000019 SeUndockPrivilege Attributes -
18 0x00000001c SeManageVolumePrivilege Attributes -
19 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
20 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
21 0x000000021 SeIncreaseWorkingSetPrivilege Attributes -
22 0x000000022 SeTimeZonePrivilege Attributes -
23 0x000000023 SeCreateSymbolicLinkPrivilege Attributes -

Auth ID: 0:19c236
Impersonation Level: Impersonation
TokenType: Impersonation
Is restricted token: no.

One more thing. If a process sets debug privilege and calls some function in another process - through impersonation - the debug privilege can be propagated to called process. For instance, if a process with debug privilege calls a method in WMI process, the WMI thread will have the same privilege in its thread.

Wednesday, September 1, 2010

STA Reentrancy

I recently observed stack overflow issue due to STA reentrancy issue. The issue occurred when a slew of COM clients called the STA object concurrently and the STA object in question made outgoing cross-apartment (or cross-process) call. When STA COM call is made, the call is sent to a hidden window in STA COM and translated to window message. When making an out-of-apartment call from an STA apartment, STA COM spins a modal message pump while waiting for the call to return. Many calls that arrived at the message loop can now be dispatched, causing reentrance. Given a lot of calls keep entering to the STA object, the STA thread reached the max limit of the thread stack. Hence the stack overflow.

Looking at the debugger, I observed that many threads showed the same pattern as shown below.

46 Id: 2444.1a18 Suspend: 1 Teb: 000007ff`fff5e000 Unfrozen
Child-SP RetAddr Call Site
00000000`03c3dce8 00000000`76f3c0b0 ntdll!ZwWaitForSingleObject+0xa
00000000`03c3dcf0 000007fe`fdca86b2 kernel32!WaitForSingleObjectEx+0x9c
00000000`03c3ddb0 000007fe`fddc9d80 ole32!GetToSTA+0x8a
00000000`03c3de00 000007fe`fddc9375 ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+0x100
00000000`03c3de50 000007fe`fdc9f436 ole32!CRpcChannelBuffer::SendReceive2+0xf1
00000000`03c3e010 000007fe`fdc9f398 ole32!CAptRpcChnl::SendReceive+0x52
00000000`03c3e0d0 000007fe`fd7d603a ole32!CCtxComChnl::SendReceive+0x6c
00000000`03c3e180 000007fe`fd7cef90 RPCRT4!NdrProxySendReceive+0x4a
00000000`03c3e1b0 000007fe`fd7d6157 RPCRT4!NdrpClientCall3+0x246
00000000`03c3e400 000007fe`fd728772 RPCRT4!ObjectStublessClient+0xa7
00000000`03c3e770 000007fe`f99a80e8 RPCRT4!ObjectStubless+0x42
00000000`03c3e7c0 00000000`ffb329cc FastProx!CWbemSvcWrapper::XWbemServices::ExecQueryAsync+0xd4
00000000`03c3e830 00000000`ffb265ba wmiprvse!CServerObject_StaThread::ExecQueryAsync+0xd4
00000000`03c3e8a0 00000000`ffb268a8 wmiprvse!CInterceptor_IWbemSyncProvider::Helper_ExecQueryAsync+0x54a
00000000`03c3e950 000007fe`fd735ec5 wmiprvse!CInterceptor_IWbemSyncProvider::ExecQueryAsync+0x138
00000000`03c3e9f0 000007fe`fd711f46 RPCRT4!Invoke+0x65
00000000`03c3ea60 000007fe`fd7d5cae RPCRT4!NdrStubCall2+0x348
00000000`03c3f040 000007fe`f998412d RPCRT4!CStdStubBuffer_Invoke+0x66
00000000`03c3f070 000007fe`fddc89b9 FastProx!CBaseStublet::Invoke+0x19
00000000`03c3f0a0 000007fe`fddc892b ole32!SyncStubInvoke+0x5d
00000000`03c3f110 000007fe`fdc9d633 ole32!StubInvoke+0xdf
00000000`03c3f1b0 000007fe`fddc87c6 ole32!CCtxComChnl::ContextInvoke+0x19f
00000000`03c3f330 000007fe`fddc855f ole32!AppInvoke+0xc2
00000000`03c3f3a0 000007fe`fddc7314 ole32!ComInvokeWithLockAndIPID+0x407
00000000`03c3f520 000007fe`fd7368d4 ole32!ThreadInvoke+0x1f0
00000000`03c3f5d0 000007fe`fd7369f0 RPCRT4!DispatchToStubInCNoAvrf+0x14
00000000`03c3f600 000007fe`fd70b042 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x100
00000000`03c3f6f0 000007fe`fd70afbb RPCRT4!RPC_INTERFACE::DispatchToStub+0x62
00000000`03c3f730 000007fe`fd70af4a RPCRT4!RPC_INTERFACE::DispatchToStubWithObject+0x5b
00000000`03c3f7b0 000007fe`fd737080 RPCRT4!LRPC_SCALL::DispatchRequest+0x436
00000000`03c3f820 000007fe`fd7362bb RPCRT4!LRPC_SCALL::HandleRequest+0x200
00000000`03c3f940 000007fe`fd735e1a RPCRT4!LRPC_ADDRESS::ProcessIO+0x44a
00000000`03c3fa60 000007fe`fd717769 RPCRT4!LOADABLE_TRANSPORT::ProcessIOEvents+0x24a
00000000`03c3fb10 000007fe`fd717714 RPCRT4!ProcessIOEventsWrapper+0x9
00000000`03c3fb40 000007fe`fd7177a4 RPCRT4!BaseCachedThreadRoutine+0x94
00000000`03c3fb80 00000000`76f2be3d RPCRT4!ThreadStartRoutine+0x24
00000000`03c3fbb0 00000000`77136a51 kernel32!BaseThreadInitThunk+0xd
00000000`03c3fbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x1d

When checking the GetToSTA, I found the COM call was made to STA thread where WMI component did reside. I learned the client created a lot of multiple async threads and sent OLE requests simultaneously. All those COM calls were entered into STA thread message loop.

Looking at the STA thread, there is repeating pattern of the below red part. That is, the incoming call is processed by the STA thread -> STA thread calls another component and wait for reply -> STA thread peeks message queue to see if there is any other incoming message. If any, the STA thread processes the message. This is STA reentrancy and it is a COM feature.

…..
00000000`013da270 000007fe`fdca80c9 ole32!ComInvoke+0x85
00000000`013da2a0 000007fe`fdca7eae ole32!ThreadDispatch+0x29
00000000`013da2d0 00000000`7705d53e ole32!ThreadWndProc+0xaa
00000000`013da350 00000000`7705d7c6 USER32!UserCallWinProcCheckWow+0x1ad
00000000`013da410 000007fe`fdd31433 USER32!DispatchMessageWorker+0x389
00000000`013da490 000007fe`fdcb11e0 ole32!CCliModalLoop::PeekRPCAndDDEMessage+0x73
00000000`013da500 000007fe`fdc7b093 ole32!CCliModalLoop::BlockFn+0x36100
00000000`013da540 000007fe`fddc7689 ole32!ModalLoop+0x6f
…..
00000000`013dc360 000007fe`f4d50263 framedyn!Provider::CreateInstanceEnum+0x34
….
00000000`013dd5d0 000007fe`fdca80c9 ole32!ComInvoke+0x85
00000000`013dd600 000007fe`fdca7eae ole32!ThreadDispatch+0x29
00000000`013dd630 00000000`7705d53e ole32!ThreadWndProc+0xaa
00000000`013dd6b0 00000000`7705d7c6 USER32!UserCallWinProcCheckWow+0x1ad
00000000`013dd770 000007fe`fdd31433 USER32!DispatchMessageWorker+0x389
00000000`013dd7f0 000007fe`fdcb11e0 ole32!CCliModalLoop::PeekRPCAndDDEMessage+0x73
00000000`013dd860 000007fe`fdc7b093 ole32!CCliModalLoop::BlockFn+0x36100
00000000`013dd8a0 000007fe`fddb4cb0 ole32!ModalLoop+0x6f
00000000`013dd8f0 000007fe`fddcb946 ole32!SwitchSTA+0x20
00000000`013dd920 000007fe`fddc9375 ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+0x24a6
00000000`013dd970 000007fe`fdc7b3be ole32!CRpcChannelBuffer::SendReceive2+0xf1
….
00000000`013de310 000007fe`f4d50d1a FastProx!CWbemSvcWrapper::XWbemServices::GetObjectW+0x95
00000000`013de370 000007fe`f4d527d5 framedyn!Provider::GetClassObjectInterface+0xda
00000000`013de420 000007fe`fd735ec5 framedyn!CWbemProviderGlue::ExecQueryAsync+0x2ad
….
00000000`013df620 000007fe`fdca80c9 ole32!ComInvoke+0x85
00000000`013df650 000007fe`fdca7eae ole32!ThreadDispatch+0x29
00000000`013df680 00000000`7705d53e ole32!ThreadWndProc+0xaa
00000000`013df700 00000000`7705d7c6 USER32!UserCallWinProcCheckWow+0x1ad
00000000`013df7c0 00000000`ffb133f3 USER32!DispatchMessageWorker+0x389
00000000`013df840 00000000`ffb12eb8 wmiprvse!WmiThread<unsigned long>::ThreadWait+0x11b
00000000`013dfac0 00000000`ffb11aa8 wmiprvse!WmiThread<unsigned long>::ThreadDispatch+0xf4
00000000`013dfb20 00000000`76f2be3d wmiprvse!WmiThread<unsigned long>::ThreadProc+0x30
00000000`013dfb50 00000000`77136a51 kernel32!BaseThreadInitThunk+0xd
00000000`013dfb80 00000000`00000000 ntdll!RtlUserThreadStart+0x1d