穿透Session 0隔离

1、前言

在 Windows XP 和 Windows Server 2003 之前,用户和服务会共享同一个会话,而这个会话是由第一个登录到控制台的用户来启动的,该会话就称为Session 0。

而从Windows Vista 开始,Windows 采取了会话隔离的措施,确保系统进程和服务与普通用户会话隔离,即只有系统服务和后台进程可以运行在Session 0中。

由于不同的Session之间工作站互相独立,无法直接从一个Session中创建属于另一个Session的GUI进程,因此就需要下面要讲述的Session穿透技术。

2、核心技术

1. 进程创建

Windows提供了多个可以以指定用户上下文创建新进程的API函数,包括CreateProcessAsUserCreateProcessWithLogon CreateProcessWithTokenW

其中使用CreateProcessWithLogon 必须要持有明文的用户名和密码,而CreateProcessWithLogon 函数不支持XP系统,因此选择CreateProcessAsUser

c 复制代码
BOOL CreateProcessAsUserW(
  [in, optional]      HANDLE                hToken,
  [in, optional]      LPCWSTR               lpApplicationName,
  [in, out, optional] LPWSTR                lpCommandLine,
  [in, optional]      LPSECURITY_ATTRIBUTES lpProcessAttributes,
  [in, optional]      LPSECURITY_ATTRIBUTES lpThreadAttributes,
  [in]                BOOL                  bInheritHandles,
  [in]                DWORD                 dwCreationFlags,
  [in, optional]      LPVOID                lpEnvironment,
  [in, optional]      LPCWSTR               lpCurrentDirectory,
  [in]                LPSTARTUPINFOW        lpStartupInfo,
  [out]               LPPROCESS_INFORMATION lpProcessInformation
);
2. 环境变量

由于不同用户的环境变量存在差异,因此创建进程时需要考虑是否需要指定用户的环境变量还是继承当前用户。如果需要可以使用CreateEnvironmentBlock检索指定用户的环境变量。

c 复制代码
BOOL CreateEnvironmentBlock(
  [out]          LPVOID *lpEnvironment,
  [in, optional] HANDLE hToken,
  [in]           BOOL   bInherit
);
3. 用户令牌获取

想要通过CreateProcessAsUser创建用户进程,最关键的一步就在于获取用户的令牌。而比较常用的获取用户令牌的方式有两种:

  1. 通过WTSQueryUserToken获取指定Session的令牌
c 复制代码
BOOL WTSQueryUserToken(
  [in]  ULONG   SessionId,
  [out] PHANDLE phToken
);
  1. 通过SetTokenInformation替换自身的令牌的Session
c 复制代码
BOOL SetTokenInformation(
  [in] HANDLE                  TokenHandle,
  [in] TOKEN_INFORMATION_CLASS TokenInformationClass,
  [in] LPVOID                  TokenInformation,
  [in] DWORD                   TokenInformationLength
);
4. 获取活动Session

虽然Windows提供了WTSGetActiveConsoleSessionId函数可以直接获取当前活动会话,但实际使用中远程桌面访问的过程中,获取的活动会话与实际不符。

因此,更多情况下会使用WTSEnumerateSessionsW枚举所有的会话,找到活动的会话ID。

c 复制代码
BOOL WTSEnumerateSessionsW(
  [in]  HANDLE             hServer,
  [in]  DWORD              Reserved,
  [in]  DWORD              Version,
  [out] PWTS_SESSION_INFOW *ppSessionInfo,
  [out] DWORD              *pCount
);

3 具体实现

c 复制代码
DWORD wts_session_count;
PWTS_SESSION_INFO wts_session;
// 枚举当前系统所有的会话
if (!WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wts_session, &wts_session_count)) {
	return false;
}
DWORD active_session_id = -1;
for (auto i = 0ul; i < wts_session_count; i++) {
    // 找到活动的会话ID
	if (wts_session[i].State == WTSActive) {
		active_session_id = wts_session[i].SessionId;
		break;
	}
}
// 释放资源
WTSFreeMemory(wts_session);

HANDLE htoken = nullptr;
LPVOID env = nullptr;
do {
    // 查询用户令牌
	HANDLE user_token;
	if (!WTSQueryUserToken(active_session_id, &user_token)) {
		break;
	}
    
	TOKEN_ELEVATION_TYPE token_type;
	DWORD token_size;
    // 获取令牌类型
	if (!GetTokenInformation(user_token, TokenElevationType, (VOID*)&token_type, sizeof(token_type), &token_size)) {
		CloseHandle(user_token);
		break;
	}
    // 判断令牌类型
	if (TokenElevationTypeLimited == token_type) {
        // 从限制令牌获取提权令牌
		if (!GetTokenInformation(user_token, TokenLinkedToken, (VOID*)&htoken, sizeof(htoken), &token_size)) {
			CloseHandle(user_token);
			break;
		}
		CloseHandle(user_token);
	}
	else {
		htoken = user_token;
	}
    
	PROCESS_INFORMATION pi;
	STARTUPINFO si;
	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
	ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	// 创建用户环境
	if (!CreateEnvironmentBlock(&env, htoken, FALSE)) {
		break;
	}
    // 创建指定用户进程
	if (!CreateProcessAsUser(htoken, NULL, (LPWSTR)command,
		NULL, NULL, FALSE, CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, env, NULL, &si, &pi)) {
		break;
	}
} while (false);
// 释放资源
if (env) {
    DestroyEnvironmentBlock(env);
}
if (htoken) {
    CloseHandle(htoken);
}

4、后话

这里简单描述一下笔者在开发安全产品的过程中,遇到的一些问题。

  • WTSQueryUserToken失败,错误代码ERROR_ACCESS_DENIEDWin7
  • CreateProcessAsUser失败,错误代码ERROR_FILE_NOT_FOUNDWin - XP

以上两个问题的本质上都是因为执行系统函数的过程中将具体的实现委托给第三方进程,而第三方进程在又反过来访问当前进程,导致第三方进程被驱动拦截。

因此,将第三方进程lsm.exewinlogon.exe加入驱动保护过滤白名单即可。

相关推荐
office大师姐1 小时前
迈向云数据领域的第一步:Microsoft Azure DP-900认证指南
大数据·windows·microsoft·微软·azure
java-王森4 小时前
List中多个数据相同,保留最新日期数据
linux·windows·list
sukalot5 小时前
Windows 图形显示驱动开发-WDDM 2.4功能-GPU 半虚拟化(八)
windows
水淹萌龙10 小时前
firewall-cmd添加访问规则
服务器·网络·windows
星释10 小时前
Windows Server 2025 使用 IIS 搭建 ASP.NET 3.5 网站
windows·asp.net
zhou周大哥11 小时前
轮循取值算法数据库
linux·windows·microsoft
好看资源平台11 小时前
SQL Server安装进度卡在 57%:Windows Update 服务异常
windows
上海合宙LuatOS1 天前
从零开始:使用Luatools工具高效烧录Air780EPM核心板项目的完整指南
开发语言·人工智能·windows·单片机·嵌入式硬件·物联网·php
Tee xm1 天前
清晰易懂的Cursor实现AI编程从安装到实战TodoList开发
windows·ai编程
Tee xm1 天前
清晰易懂的Trae实现为AI编程从安装到实战开发ToDoList
windows·macos·ai编程