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

相关推荐
百事牛科技3 分钟前
PPT只读怎么取消?四种情况分别处理
windows·powerpoint
熊明才19 小时前
WSL2 网络突然瘫痪(Network is unreachable / 没有 eth0)最短修复指南(2026年9月20日亲测可用)
linux·windows·wsl2
xbzb1 天前
Windows 启动故障修复知识整理:蓝屏 / 无限重启 / 盘符 X / 无 PE 盘
windows
我命由我123451 天前
Mac 操作系统 - 一些使用记录
运维·windows·学习·系统安全·运维开发·mac·学习方法
kakakahahahaha2 天前
Windows Steam 游戏存档在哪里?定位和恢复排查流程
windows·其他·电脑·笔记本电脑·内容运营·软件需求
Xy-unu2 天前
Windows 开启外接显示器 HDR 后外接显示器无信号、无法扩展模式故障记录
windows
troy1282 天前
Codex 安全盲区:代码漏洞生成实测
windows·python·ci/cd·pycharm·django·github·fastapi
其实防守也摸鱼2 天前
常见安全架构中 Shiro 的认证确认机制解析
运维·服务器·数据库·windows·github
五_谷_丰_登2 天前
windows批处理脚本(bat)问题解决方案大全——持续更新
windows·bat
YCOSA20252 天前
迎双节 雨晨 Windows 11 IoT 企业版 LTSC 26H1 轻装 28000.2956
windows·物联网