Closed bencikpeter closed 6 years ago
Our service is started as LocalSystem
, according to MSDN this should already give it the SE_CREATE_TOKEN_NAME
privilege. It might just be a matter of enabling it: https://docs.microsoft.com/en-us/windows/desktop/secauthz/enabling-and-disabling-privileges-in-c--
I am sorry to say that, but this is not right. I´ve already battled this problem when implementing the token manipulation library and came across this MSDN post and I am not sure if it is outdated or are we accessing a restricted LocalSystem
accout but the privilege is not there. My personal working theory is that whatever system routine is starting our service, it is running CreateRestrictedToken on token we then get.
I´ve explicitly checked for the privilege by starting command line in LocalSystem
context.
Output from my system is down below and as you can see, there is no SeCreateTokenPrivilege
present.
C:\Windows\system32>whoami
nt authority\system
C:\Windows\system32>whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
========================================= ================================================================== ========
SeAssignPrimaryTokenPrivilege Replace a process level token Disabled
SeLockMemoryPrivilege Lock pages in memory Enabled
SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled
SeTcbPrivilege Act as part of the operating system Enabled
SeSecurityPrivilege Manage auditing and security log Disabled
SeTakeOwnershipPrivilege Take ownership of files or other objects Disabled
SeLoadDriverPrivilege Load and unload device drivers Disabled
SeSystemProfilePrivilege Profile system performance Enabled
SeSystemtimePrivilege Change the system time Disabled
SeProfileSingleProcessPrivilege Profile single process Enabled
SeIncreaseBasePriorityPrivilege Increase scheduling priority Enabled
SeCreatePagefilePrivilege Create a pagefile Enabled
SeCreatePermanentPrivilege Create permanent shared objects Enabled
SeBackupPrivilege Back up files and directories Disabled
SeRestorePrivilege Restore files and directories Disabled
SeShutdownPrivilege Shut down the system Disabled
SeDebugPrivilege Debug programs Enabled
SeAuditPrivilege Generate security audits Enabled
SeSystemEnvironmentPrivilege Modify firmware environment values Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeUndockPrivilege Remove computer from docking station Disabled
SeManageVolumePrivilege Perform volume maintenance tasks Disabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled
SeTimeZonePrivilege Change the time zone Enabled
SeCreateSymbolicLinkPrivilege Create symbolic links Enabled
SeDelegateSessionUserImpersonatePrivilege Obtain an impersonation token for another user in the same session Enabled
You can perform this check on your computer using PSTools, maybe my system is just messed up.
Startup a command line as an administrator, navigate to PSTools folder and run this, tu start command line as NT AUTHORITY\SYSTEM
psexec.exe -i -s %SystemRoot%\system32\cmd.exe
and then to display privileges available, run
whoami /priv
To the matter of enabling, I am already using this code inside the library to enable and disable that privilege,, but it returns The token does not have the specified privilege
message when callig inside CageManager
so enabling is definitely not the problem here.
bool setPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid)) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError());
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
bool changeTokenCreationPrivilege(bool privilegeStatus) {
//keeping this for future debug purposes
//will be important if ever implementing token capture for a token having SE_CREATE_TOKEN_NAME included
/*
DWORD bufferSize = 0;
GetUserName(NULL, &bufferSize);
LPTSTR pUserName = (LPTSTR) new BYTE[bufferSize * sizeof(TCHAR)];
GetUserName(pUserName, &bufferSize);
wprintf(L"User account accessed: %s\n", pUserName);
delete[](BYTE*) pUserName;
*/
HANDLE currentProcessHandle;
HANDLE userTokenHandle;
currentProcessHandle = GetCurrentProcess();
if (!OpenProcessToken(currentProcessHandle, TOKEN_ALL_ACCESS, &userTokenHandle)) {
wprintf(L"Error getting token for privilege escalation\n");
return false;
}
return setPrivilege(userTokenHandle, SE_CREATE_TOKEN_NAME, privilegeStatus);
CloseHandle(userTokenHandle);
}
My temporary solution while developing TokenLibrary
was to manually assign that privilege to the "Administrators" group, but this is massively irresponsible.
I am sorry to say that, but this is not right. I´ve already battled this problem when implementing the token manipulation library and came across this MSDN post and I am not sure if it is outdated or are we accessing a restricted LocalSystem accout but the privilege is not there. My personal working theory is that whatever system routine is starting our service, it is running CreateRestrictedToken on token we then get.
Didn't mean to step on your toes, I was just curious about the privileges and found the mentioned MSDN article. Had a look at the service privileges and yeah, it's a shame the article is not accurate :/
Just out of curiosity: We do have the SeTcbPrivilege
, so we can act as the system. Does this not allow us to just grant ourselves the SeCreateTokenPrivilege
? (I know you would most likely already done this if it was possible, but I'm just wondering why this would not work 😅).
Didn't mean to step on your toes, I was just curious about the privileges and found the mentioned MSDN article. Had a look at the service privileges and yeah, it's a shame the article is not accurate :/
Don´t worry, you didn´t step on anything 😉 If I sounded offended in a previous post, it was not intenended to be that 🙂
We do have the SeTcbPrivilege, so we can act as the system. Does this not allow us to just grant ourselves the SeCreateTokenPrivilege?
Here it starts to be funnny. With having SeTcbPrivilege
privilege indeed comes the ability to manipulate tokens and access rights. The catch is, that for individual tokens, WINAPI provides (at least to my knowlege) only functions to remove or restrict privileges. Adding privileges is possible, but only for whole accounts, not individual tokens. The only way to assign new privileges to a token I know about, is to construct a token from scratch using NtCreateToken
but that needs SeCreateTokenPrivilege
to work. And here we eat our own tail, because the privilege we are trying to grant ourselves is exactly SeCreateTokenPrivilege
🐍
What we have privileges for, is to get tokens of other processes. So if we find a process having SeCreateTokenPrivilege
we can just start CageManager
with that token and voilaa 🎉
Here it starts to be funnny. With having
SeTcbPrivilege
privilege indeed comes the ability to manipulate tokens and access rights. The catch is, that for individual tokens, WINAPI provides (at least to my knowlege) only functions to remove or restrict privileges.
That's too bad. 😞
What we have privileges for, is to get tokens of other processes. So if we find a process having
SeCreateTokenPrivilege
we can just startCageManager
with that token and voilaa 🎉
Hijacking other processes' tokens just feels hackish, but what can we do. 😆 I guess this just comes with the unusual nature of what we are trying to achieve. We can probably be confident that there exists at least one (system) process with an appropriate token?
Cheers for the detailed explanation! 🥂
Hijacking other processes' tokens just feels hackish, but what can we do. 😆
It indeed is hackish 😅 Actually it was not my idea... I discussed this issue with Langweg a month or two ago. What he told me was:
To enable the SeCreateTokenPrivilege, from the service executed as LocalSystem I had to locate a process that possessed a token that had this privilege, get a handle to that process, get a handle to its token, adjust the token privileges (from disabled to enabled), and then use the modified token in a call to ImpersonateLoggedOnUser(), providing the handle to the modified token as the parameter.
The code is contained in the function
SDAcquireTCBPrivilege
in fileSDModifiedTokens.pas
. To find a process from which I could re-use its token, I used the functionSDFindPIDWithPrivilege
in fileSDInfoProcesses.pas
.
I did not have time to look into those functions yet (and nor am I very entusiastic to dig around in delphi code 😅 ), but it seems that this approach was successfuly realized in the previous delpi run of the project.
We can probably be confident that there exists at least one (system) process with an appropriate token?
From previously referenced (incorrect) MSDN article it seems like that at least Service Control Manager should be possessing such a privilege... I will have a look at that soon and keep y´all updated 😎
Alternative approach that might work:
@langweg Thank you for the suggestion. In the meantime I´ve already implemented your previous suggestion with aquiring a token of a different process that already has SeCreateTokenPrivilege
and it seems to work pretty well so far. And it was not as difficult to implement as I´ve originaly thought.
You can see the implementation at #71. Maybe you can give me some thoughts on the implementation?
Implemented in #71.
In order for token manipulation to work, the CageManager must be started with a token that has a SeCreateTokenPrivilege
Possible solution is to (from the CageService) locate a process in a system that posseses a desired privilege, get a handle on it, then capture its token and start CageManager with such token