I feel like I'm missing something obvious but I've been stuck on this for hours. I am attempting to call SetTokenInformation
on a duplicate primary token of the current process, but it fails with 0x5 (Access denied)
.
I'm trying to create a child process with no privileges. Then I want to adjust the integrity level of the token to low.
Flow of execution in the minimal example:
We enable every single privilege. All of them.
We open the current process with PROCESS_ALL_ACCESS, and open the process token with TOKEN_ALL_ACCESS
We get a duplicate token for our current process.
We attempt to change the mandatory integrity level of the token, and we get
Access denied
.
Please note this is for a minimal example, I know the above would be very bad practice in production code. We are simply eliminating the possibility of permission issues.
Privileges:
Output:
What could I be missing here? I have tried everything I can think of:
Debugged it to verify the method being called is correct and the arguments on the stack are all valid
Verified arguments and return values for every function in the program
Tried every process and token access/duplication flag
Minimum 2 hours of Googling for random info with "StackOverflow ... token" (this one is a joke, but I really did)
etc.
So I figure it has to be a problem with the SID itself. But I have been able to find nothing on the internet about how this SID is constructed.
MinimalMain.cpp:
// TokenMinimalExample.cpp : This file contains the 'main' function. Program execution begins and ends there.
#include <windows.h>
#include <stdio.h>
#include "token_example.h"
int main()
{
// Enable every privilege under the sun.
if (!EnableEveryPrivilegeUnderTheSun())
{
printf("Failed to enable every privilege under the sun: %d\n", GetLastError());
return 1;
}
// Get a duplicate token for the current process
HANDLE hToken = GetCurrentProcessPrimaryToken();
if (hToken == INVALID_HANDLE_VALUE)
{
printf("Failed to get a duplicate token for the current process: %d\n", GetLastError());
return 1;
}
// Create a mandatory low integrity SID
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_MANDATORY_LABEL_AUTHORITY;
PSID pSid = NULL;
// Can be low, med, high, system, they're all 0x5
if(!AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_MANDATORY_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSid))
{
printf("Failed to create a mandatory low integrity SID: %d\n", GetLastError());
system("PAUSE");
return 1;
}
// Change the integrity level of the duplicate token
TOKEN_MANDATORY_LABEL tml;
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = pSid;
if (!SetTokenInformation(hToken, TokenIntegrityLevel, &tml, sizeof(tml)))
{
// Access denied ??????
printf("Failed to set token information: %d\n", ::GetLastError());
system("PAUSE");
return 1;
}
system("PAUSE");
return 0;
}
token_example.h:
#pragma once
#include <Windows.h>
#include <stdio.h>
// Helper function to adjust our processes current privileges
bool SetThreadProcessPrivilege(LPCWSTR PrivilegeName, bool Enable)
{
HANDLE Token;
TOKEN_PRIVILEGES TokenPrivs;
LUID TempLuid;
bool Result;
if (!LookupPrivilegeValueW(NULL, PrivilegeName, &TempLuid)) return false;
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &Token))
{
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &Token)) return false;
}
TokenPrivs.PrivilegeCount = 1;
TokenPrivs.Privileges[0].Luid = TempLuid;
TokenPrivs.Privileges[0].Attributes = (Enable ? SE_PRIVILEGE_ENABLED : 0);
Result = (AdjustTokenPrivileges(Token, FALSE, &TokenPrivs, 0, NULL, NULL) && ::GetLastError() == ERROR_SUCCESS);
// Even if AdjustTokenPrivileges returns TRUE, it may not have succeeded
// check last error top confirm
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf(" Unable to set privilege: %S Error: %d \n", PrivilegeName, GetLastError());
CloseHandle(Token);
return FALSE;
}
CloseHandle(Token);
return Result;
}
// Test method, trying to figure out if I was missing any privileges
bool EnableEveryPrivilegeUnderTheSun()
{
if (!SetThreadProcessPrivilege(L"SeBackupPrivilege", true))
{
printf("Failed to enable SeBackupPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeRestorePrivilege", true))
{
printf("Failed to enable SeRestorePrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeIncreaseQuotaPrivilege", true))
{
printf("Failed to enable SeIncreaseQuotaPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeAssignPrimaryTokenPrivilege", true))
{
printf("Failed to enable SeAssignPrimaryTokenPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeTcbPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeDebugPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeSecurityPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeSystemtimePrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeShutdownPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeSystemEnvironmentPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeTakeOwnershipPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeLoadDriverPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeManageVolumePrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
return true;
}
// Helper function to get a duplicate primary token for the current process
HANDLE GetCurrentProcessPrimaryToken()
{
HANDLE tempproc;
HANDLE realtoken, duplicatetoken;
DWORD accessmode = TOKEN_ALL_ACCESS;
// Enable SeDebugPrivilege.
SetThreadProcessPrivilege(L"SeDebugPrivilege", true);
// Open a handle to the process.
tempproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
if (tempproc == NULL)
{
printf("OpenProcess failed with error %d\n", GetLastError());
return INVALID_HANDLE_VALUE;
}
if (!OpenProcessToken(tempproc, accessmode, &realtoken))
{
printf("OpenProcessToken failed with error %d\n", GetLastError());
CloseHandle(tempproc);
return INVALID_HANDLE_VALUE;
}
CloseHandle(tempproc);
SECURITY_ATTRIBUTES secattr = { 0 };
secattr.nLength = sizeof(secattr);
secattr.bInheritHandle = FALSE;
secattr.lpSecurityDescriptor = NULL;
if (!DuplicateTokenEx(realtoken, MAXIMUM_ALLOWED, &secattr, SecurityImpersonation, TokenPrimary, &duplicatetoken))
{
printf("DuplicateTokenEx failed with error %d\n", GetLastError());
CloseHandle(realtoken);
return INVALID_HANDLE_VALUE;
}
CloseHandle(realtoken);
return duplicatetoken;
}
CodePudding user response:
Not sure why you cannot assign the system IL if you are running as System but the RID you are really looking for is SECURITY_MANDATORY_LOW_RID:
HANDLE hToken, hPrimTok = 0;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &hToken))
{
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, 0, SecurityAnonymous, TokenPrimary, &hPrimTok)) hPrimTok = 0;
CloseHandle(hToken);
}
if (hPrimTok)
{
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_MANDATORY_LABEL_AUTHORITY;
TOKEN_MANDATORY_LABEL tml;
tml.Label.Attributes = SE_GROUP_INTEGRITY;
if (AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_MANDATORY_LOW_RID, 0, 0, 0, 0, 0, 0, 0, &tml.Label.Sid))
{
if (!SetTokenInformation(hPrimTok, TokenIntegrityLevel, &tml, sizeof(tml)))
printf("SetTokenInformation failed, %u\n", GetLastError());
else
{
STARTUPINFOA si = { sizeof (STARTUPINFO) };
PROCESS_INFORMATION pi;
if (CreateProcessAsUserA(hPrimTok, 0, "Notepad.exe", 0, 0, FALSE, 0, 0, 0, &si, &pi))
{
// CloseHandle *2
}
}
FreeSid(tml.Label.Sid);
}
CloseHandle(hPrimTok);
}
Please note that you should also call CreateRestrictedToken
to really restrict the new process. Disable all groups and privileges you don't need.