穿透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加入驱动保护过滤白名单即可。

相关推荐
lucky67073 小时前
Windows 上彻底卸载 Node.js
windows·node.js
编程小白20264 小时前
从 C++ 基础到效率翻倍:Qt 开发环境搭建与Windows 神级快捷键指南
开发语言·c++·windows·qt·学习
凯子坚持 c5 小时前
CANN 性能剖析实战:从原始事件到交互式火焰图
windows·microsoft
开开心心就好6 小时前
发票合并打印工具,多页布局设置实时预览
linux·运维·服务器·windows·pdf·harmonyos·1024程序员节
獨枭6 小时前
PyCharm 跑通 SAM 全流程实战
windows
仙剑魔尊重楼7 小时前
音乐制作电子软件FL Studio2025.2.4.5242中文版新功能介绍
windows·音频·录屏·音乐·fl studio
PHP小志7 小时前
Windows 服务器怎么修改密码和用户名?账户被系统锁定如何解锁
windows
专注VB编程开发20年9 小时前
vb.net datatable新增数据时改用数组缓存
java·linux·windows
仙剑魔尊重楼9 小时前
专业音乐制作软件fl Studio 2025.2.4.5242中文版新功能
windows·音乐·fl studio
rjc_lihui10 小时前
Windows 运程共享linux系统的方法
windows