ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

第四章 启动技术---突破SESSION 0隔离创建用户进程

2021-09-12 18:34:01  阅读:343  来源: 互联网

标签:令牌 bRet FALSE SERVICE --- SESSION ServiceStatus NULL 第四章


一、突破SESSION 0隔离创建用户进程

在Windows XP、Windows Server 2003,以及更老版本的Windows操作系统中,服务和应用程序使用相同的会话(SESSION)来运行,而这个会话是由第一个登录到控制台的用户来启动的,该会话就称为SESSION 0。将服务和用户应用程序一起在SESSION 0中运行会导致安全风险,因为服务会使用提升后的权限来运行,而用户应用程序使用用户特权(大部分都是非管理员用户)运行,这会使得恶意软件把某个服务作为攻击目标,通过“劫持”该服务以达到提升自己权限级别的目的。
从Windows VISTA开始,只有服务可以托管到SESSION 0中,用户应用程序和服务之间会进行隔离,并需要运行在用户登录系统时创建的后续会话中。如第一个登录用户创建Session 1,第二个登录用户创建Session 2,以此类推。
使用不同会话运行的实体(应用程序或服务)如果不将自己明确标注为全局命名空间,并提供相应的访问控制设置,那么将无法互相发送消息,共享UI元素或共享内核对象。

二、API

1、WTSGetActiveConsoleSessionId函数
检索控制台会话的标识符Session Id。控制台会话时当前连接到物理控制台的会话。
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-wtsgetactiveconsolesessionid
2、WTSQueryUserToken函数
获取由Session Id指定的登录用户的主访问令牌。要想成功调用此功能,则调用应用程序必须在本地系统账户的上下文中运行,并具有SE_TCB_NAME特权。
https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsqueryusertoken
3、DuplicateTokenEx函数
创建一个新的访问令牌,它与现有令牌重复。此功能可以创建主令牌或模拟令牌。
https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-duplicatetokenex
4、CreateEnvironmentBlock函数
检索指定用户的环境变量,然后可以将此块传递给CreateProcessAsUser函数。
https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-createenvironmentblock
5、CreateProcessAsUser函数
创建一个新进程及主进程,新进程在指定令牌表示的用户安全的上下文中运行。
https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera

三、实现原理

由于SESSION 0的隔离,使得在系统服务进程内不能直接调用CreateProcess等函数创建进程,而只能通过CreateProcessAsUser函数来创建。这样,创建的进程才会显示UI界面,与用户进行交互。
在SESSION 0中创建用户桌面进程具体的实现流程如下所示。
首先,调用WTSGetActiveConsoleSessionId函数来获取当前程序的会话ID,即Session Id。调用该函数不需要任何参数,直接返回Session Id。根据Session Id继续调用WTSQueryUserToken函数来检索用户令牌,并获取对应的用户令牌句柄。在不需要使用用户令牌句柄时,可以调用CloseHandle函数来释放句柄。
其次,使用DuplicateTokenEx函数创建一个新令牌,并复制上面获取的用户令牌。设置新令牌的访问权限为MAXIMUM_ALLOWED,这表示获取所有令牌权限。新访问令牌的模拟级别为SecurityIdentification,而且令牌类型为TokenPrimary,这表示新令牌是可以在CreateProcessAsUser函数中使用的主令牌。
最后,根据新令牌调用CreateEnvironmentBlock函数创建一个环境块,用来传递给CreateProcessAsUser使用。在不需要使用进程环境块时,可以通过调用DestroyEnvironmentBlock函数进行释放。获取环境块后,就可以调用CreateProcessAsUser来创建用户桌面进程。CreateProcessAsUser函数的用法以及参数含义与CreateProcess函数的用法和参数含义类似。新令牌句柄作为用户主令牌的句柄,指定创建进程的路径,设置优先级和创建标志,设置STARTUPINFO结构信息,获取PROCESS_INFORMATION结构信息。
经过上述操作后,就完成了用户桌面进程的创建。但是,上述方法创建的用户桌面进程并没有继承服务程序的系统权限,只具有普通权限。要想创建一个有系统权限的子进程,这可以通过设置进程访问令牌的安全描述符来实现,具体的实现步骤在此就不详细介绍了。

四、编码实现

创建服务进程代码:001.exe(以管理员身份运行创建服务)
ServiceOperate.h

#ifndef _SERVICE_OPERATE_H_
#define _SERVICE_OPERATE_H_

#include <Windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
// 0 加载服务    1 启动服务    2 停止服务    3 删除服务
BOOL SystemServiceOperate(char* lpszDriverPath, int iOperateType);
#endif

ServiceLoader.cpp

// ServiceLoader.cpp : 定义控制台应用程序的入口点。
//
#include "ServiceOperate.h"
#include<stdio.h>
#include<tchar.h>

int _tmain(int argc, _TCHAR* argv[])
{
	BOOL bRet = FALSE;
	char szExePath[] = "C:\\C C++\\winhack\\session0\\001\\x64\\Release\\002.exe";

	// 加载服务
	bRet = SystemServiceOperate(szExePath, 0);
	if (bRet)
	{
		printf("INSTALL OK.\n");
	}
	else
	{
		printf("INSTALL ERROR.\n");
	}
	// 启动服务
	bRet = SystemServiceOperate(szExePath, 1);
	if (bRet)
	{
		printf("START OK.\n");
	}
	else
	{
		printf("START ERROR.\n");
	}


	system("pause");

	// 停止服务
	bRet = SystemServiceOperate(szExePath, 2);
	if (bRet)
	{
		printf("STOP OK.\n");
	}
	else
	{
		printf("STOP ERROR.\n");
	}
	// 卸载服务
	bRet = SystemServiceOperate(szExePath, 3);
	if (bRet)
	{
		printf("UNINSTALL OK.\n");
	}
	else
	{
		printf("UNINSTALL ERROR.\n");
	}

	return 0;
}

ServiceOperate.cpp

#include "ServiceOperate.h"
#include <stdio.h>
#include<tchar.h>


void ShowError(char* lpszText)
{
	char szErr[MAX_PATH] = { 0 };
	::wsprintf(szErr, "%s Error!\nError Code Is:%d\n", lpszText, ::GetLastError());
#ifdef _DEBUG
	::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);
#endif
}


// 0 加载服务    1 启动服务    2 停止服务    3 删除服务
BOOL SystemServiceOperate(char* lpszExePath, int iOperateType)
{
	BOOL bRet = TRUE;
	char szName[MAX_PATH] = { 0 };

	lstrcpy(szName, lpszExePath);
	// 过滤掉文件目录,获取文件名
	PathStripPath(szName);

	SC_HANDLE shOSCM = NULL, shCS = NULL;
	SERVICE_STATUS ss;
	DWORD dwErrorCode = 0;
	BOOL bSuccess = FALSE;
	// 打开服务控制管理器数据库
	shOSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (!shOSCM)
	{
		ShowError("OpenSCManager");
		return FALSE;
	}

	if (0 != iOperateType)
	{
		// 打开一个已经存在的服务
		shCS = OpenService(shOSCM, szName, SERVICE_ALL_ACCESS);
		if (!shCS)
		{
			ShowError("OpenService");
			::CloseServiceHandle(shOSCM);
			shOSCM = NULL;
			return FALSE;
		}
	}

	switch (iOperateType)
	{
	case 0:
	{
		// 创建服务
		// SERVICE_AUTO_START   随系统自动启动
		// SERVICE_DEMAND_START 手动启动
		shCS = ::CreateService(shOSCM, szName, szName,
			SERVICE_ALL_ACCESS,
			SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
			SERVICE_AUTO_START,
			SERVICE_ERROR_NORMAL,
			lpszExePath, NULL, NULL, NULL, NULL, NULL);
		if (!shCS)
		{
			ShowError("CreateService");
			bRet = FALSE;
		}
		break;
	}
	case 1:
	{
		// 启动服务
		if (!::StartService(shCS, 0, NULL))
		{
			ShowError("StartService");
			bRet = FALSE;
		}
		break;
	}
	case 2:
	{
		// 停止服务
		if (!::ControlService(shCS, SERVICE_CONTROL_STOP, &ss))
		{
			ShowError("ControlService");
			bRet = FALSE;
		}
		break;
	}
	case 3:
	{
		// 删除服务
		if (!::DeleteService(shCS))
		{
			ShowError("DeleteService");
			bRet = FALSE;
		}
		break;
	}
	default:
		break;
	}
	// 关闭句柄
	if (shCS)
	{
		::CloseServiceHandle(shCS);
		shCS = NULL;
	}
	if (shOSCM)
	{
		::CloseServiceHandle(shOSCM);
		shOSCM = NULL;
	}

	return bRet;
}

创建用户的进程002.exe
CreateProcessAsUser_Test.cpp

#include <Windows.h>
#include <UserEnv.h>
#include <WtsApi32.h>
#include <tchar.h>
#pragma comment(lib, "UserEnv.lib")
#pragma comment(lib, "WtsApi32.lib")


// 服务入口函数以及处理回调函数
void __stdcall ServiceMain(DWORD dwArgc, char* lpszArgv);
void __stdcall ServiceCtrlHandle(DWORD dwOperateCode);
void DoTask();
// 显示消息对话框
void ShowMessage(TCHAR* lpszMessage, TCHAR* lpszTitle);
// 创建用户进程
BOOL CreateUserProcess(char* lpszFileName);

// 全局变量
char g_szServiceName[MAX_PATH] = "002.exe";    // 服务名称 
SERVICE_STATUS g_ServiceStatus = { 0 };
SERVICE_STATUS_HANDLE g_ServiceStatusHandle = { 0 };


int _tmain(int argc, _TCHAR* argv[])
{
	// 注册服务入口函数
	SERVICE_TABLE_ENTRY stDispatchTable[] = { { g_szServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain }, { NULL, NULL } };
	::StartServiceCtrlDispatcher(stDispatchTable);

	return 0;
}


void __stdcall ServiceMain(DWORD dwArgc, char* lpszArgv)
{
	g_ServiceStatus.dwServiceType = SERVICE_WIN32;
	g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
	g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
	g_ServiceStatus.dwWin32ExitCode = 0;
	g_ServiceStatus.dwServiceSpecificExitCode = 0;
	g_ServiceStatus.dwCheckPoint = 0;
	g_ServiceStatus.dwWaitHint = 0;

	g_ServiceStatusHandle = ::RegisterServiceCtrlHandler(g_szServiceName, ServiceCtrlHandle);

	g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
	g_ServiceStatus.dwCheckPoint = 0;
	g_ServiceStatus.dwWaitHint = 0;
	::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);

	// 自己程序实现部分代码放在这里
	DoTask();
}


void __stdcall ServiceCtrlHandle(DWORD dwOperateCode)
{
	switch (dwOperateCode)
	{
	case SERVICE_CONTROL_PAUSE:
	{
		// 暂停
		g_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
		break;
	}
	case SERVICE_CONTROL_CONTINUE:
	{
		// 继续
		g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
		break;
	}
	case SERVICE_CONTROL_STOP:
	{
		// 停止
		g_ServiceStatus.dwWin32ExitCode = 0;
		g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
		g_ServiceStatus.dwCheckPoint = 0;
		g_ServiceStatus.dwWaitHint = 0;
		::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
		break;
	}
	case SERVICE_CONTROL_INTERROGATE:
	{
		// 询问
		break;
	}
	default:
		break;
	}
}


void DoTask()
{
	// 自己程序实现部分代码放在这里
	// 显示对话框
	ShowMessage("Hi Demon·Gan\nThis Is From Session 0 Service!\n", "HELLO");
	// 创建用户桌面进程
	CreateUserProcess("C:\\C C++\\winhack\\createmeutex\\x64\\Release\\createmeutex.exe");
}


void ShowMessage(TCHAR* lpszMessage, TCHAR* lpszTitle)
{
	// 获取当前的Session ID
	DWORD dwSessionId = ::WTSGetActiveConsoleSessionId();
	// 显示消息对话框
	DWORD dwResponse = 0;
	::WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, dwSessionId,
		lpszTitle, (1 + ::lstrlen(lpszTitle)),
		lpszMessage, (1 + ::lstrlen(lpszMessage)),
		0, 0, &dwResponse, FALSE);
}


// 突破SESSION 0隔离创建用户进程
BOOL CreateUserProcess(char* lpszFileName)
{
	BOOL bRet = TRUE;
	DWORD dwSessionID = 0;
	HANDLE hToken = NULL;
	HANDLE hDuplicatedToken = NULL;
	LPVOID lpEnvironment = NULL;
	STARTUPINFO si = { 0 };
	PROCESS_INFORMATION pi = { 0 };
	si.cb = sizeof(si);

	do
	{
		// 获得当前Session ID
		dwSessionID = ::WTSGetActiveConsoleSessionId();

		// 获得当前Session的用户令牌
		if (FALSE == ::WTSQueryUserToken(dwSessionID, &hToken))
		{
			ShowMessage("WTSQueryUserToken", "ERROR");
			bRet = FALSE;
			break;
		}

		// 复制令牌
		if (FALSE == ::DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL,
			SecurityIdentification, TokenPrimary, &hDuplicatedToken))
		{
			ShowMessage("DuplicateTokenEx", "ERROR");
			bRet = FALSE;
			break;
		}

		// 创建用户Session环境
		if (FALSE == ::CreateEnvironmentBlock(&lpEnvironment,
			hDuplicatedToken, FALSE))
		{
			ShowMessage("CreateEnvironmentBlock", "ERROR");
			bRet = FALSE;
			break;
		}

		// 在复制的用户Session下执行应用程序,创建进程
		if (FALSE == ::CreateProcessAsUser(hDuplicatedToken,
			lpszFileName, NULL, NULL, NULL, FALSE,
			NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
			lpEnvironment, NULL, &si, &pi))
		{
			ShowMessage("CreateProcessAsUser", "ERROR");
			bRet = FALSE;
			break;
		}

	} while (FALSE);
	// 关闭句柄, 释放资源
	if (lpEnvironment)
	{
		::DestroyEnvironmentBlock(lpEnvironment);
	}
	if (hDuplicatedToken)
	{
		::CloseHandle(hDuplicatedToken);
	}
	if (hToken)
	{
		::CloseHandle(hToken);
	}
	return bRet;
}

标签:令牌,bRet,FALSE,SERVICE,---,SESSION,ServiceStatus,NULL,第四章
来源: https://www.cnblogs.com/sakura521/p/15256986.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有