gpt4 book ai didi

子进程中的 C++ windows LocalSystem 模拟失败

转载 作者:行者123 更新时间:2023-11-28 01:44:44 36 4
gpt4 key购买 nike

试图解决它,但到目前为止所有的努力都是徒劳的。工作流程如下

作为 LocalSystem 运行的 Windows 服务使用 CreateProcessAsUser(...) 创建子服务带有当前登录用户的 token 。

const auto session = WTSGetActiveConsoleSessionId();
auto result = WTSQueryUserToken(session, &token);

HANDLE primary;
result = DuplicateTokenEx(token,
TOKEN_QUERY_SOURCE | TOKEN_ALL_ACCESS | TOKEN_IMPERSONATE |
TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ADJUST_PRIVILEGES,
nullptr, SecurityImpersonation, TokenPrimary, &primary);

const auto args = std::to_string(reinterpret_cast<long>(access));
CreateProcessAsUser(primary,
const_cast<LPSTR>(command.c_str()), // module name
const_cast<LPSTR>(args.c_str()), // Command line
nullptr, // Process handle not inheritable
nullptr, // Thread handle not inheritable
TRUE, // Set handle inheritance to TRUE
0, // No creation flags
nullptr, // Use parent's environment block
nullptr, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi); // Pointer to PROCESS_INFORMATION structure

子进程在用户工作站\桌面启动,主线程捕获用户 I/O 事件。子进程模拟如下
 void impersonate() {
const auto args = GetCommandLine();
const auto system_token = reinterpret_cast<HANDLE>(std::stol(args, nullptr));

if (SetThreadToken(nullptr, system_token) == TRUE) {
auto result = OpenThreadToken(GetCurrentThread(),
TOKEN_QUERY | TOKEN_QUERY_SOURCE, TRUE, &token);
if (result == TRUE)
{
DWORD dwSize = 0;

if (!GetTokenInformation(token, TokenStatistics, NULL, 0, &dwSize)) {
const auto dwResult = GetLastError();

if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
cout << "GetTokenInformation Error: " << dwResult;

} else {
// Allocate the buffer.
PTOKEN_STATISTICS statistics =
(PTOKEN_STATISTICS)GlobalAlloc(GPTR, dwSize);

// Call GetTokenInformation again to get the group information.
if (!GetTokenInformation(token, TokenStatistics, statistics, dwSize,
&dwSize)) {
cout << "GetTokenInformation Error: " << error;
} else {
const auto level = statistics->ImpersonationLevel;
std::string str;

switch (level) {
case SecurityAnonymous:
str = R"(anonymous)";
break;
case SecurityIdentification:
str = R"(identification)";
break;
case SecurityImpersonation:
str = R"(impersonation)";
break;
case SecurityDelegation:
str = R"(delegation)";
break;
}

// This outputs identification.
cout << "impersonation level : " << str;
}
}
}
}

void thread_main()
{
impersonate();

// if impersonation is successful, file opening fails otherwise not.
const auto file = CreateFile(R"(C:\foo.txt)", // name of the write
GENERIC_WRITE, // open for writing
0, // do not share
NULL, // default security
CREATE_NEW, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template

if (file == INVALID_HANDLE_VALUE) {

} else {
// Rest of code;
}
}

尽管当前用户是管理员并添加了“身份验证后模拟客户端”,但它仍然报告“安全标识”。

问:是否需要将其提升为安全模拟?
谢谢,

最佳答案

我如何理解你下一步做什么 - 你将 LocalSystem token 从服务复制到子进程(通过继承句柄)并在命令行中传递它的句柄值。那么您调用 SetThreadToken .

但是 SetThreadToken 的文档是错误的和不完整的。

这里只说token必须有TOKEN_IMPERSONATE访问权限。
没有说线程句柄访问权限 - 它必须有 THREAD_SET_THREAD_TOKEN
但主要:

When using the SetThreadToken function to impersonate, you must have the impersonate privileges and make sure that the SetThreadToken function succeeds



下是什么意思你必须有 ?通常这意味着调用线程(或在线程没有 token 的情况下调用线程所属的进程)必须在 token 中具有模拟权限。

但这是错误的,不是真的。您(调用线程)拥有的特权 - 无关紧要。目标(不是调用!)线程所属的进程(即使目标线程有 token )必须有 SeImpersonatePrivilege特权或具有与模拟 token 相同的登录 session ID,否则.. 不,函数不会失败,并且返回成功,但它会默默替换 SECURITY_IMPERSONATION_LEVEL token 中的成员 SecurityIdentification (查看 WRK-v1.2\base\ntos\ps\security.c PsImpersonateClient 函数 - 从 SeTokenCanImpersonate 开始(在 WRK-v1.2\base\ntos\se\token.c 中实现 - 在这里并检查 TOKEN_HAS_IMPERSONATE_PRIVILEGE 和 LogonSessionId),如果失败 ( STATUS_PRIVILEGE_NOT_HELD) 返回 SeTokenCanImpersonate - PsImpersonateClient 函数集 ImpersonationLevel = SecurityIdentification ;
所以即使你调用 SetThreadToken来自子进程线程的服务(具有模拟特权) - 如果子进程没有模拟特权,则调用“失败”。反之亦然 - 如果您说将自己的线程句柄(具有 THREAD_SET_THREAD_TOKEN 访问权限)传递给没有模拟权限的受限进程 - 他可以成功调用 SetThreadToken对于 您的 线程 - 模拟级别不会重置为 SecurityIdentification
在您的情况下,因为子进程没有 SeImpersonatePrivilege (通常它只存在于提升的进程中,但如果用户使用 LOGON32_LOGON_INTERACTIVE 进入系统 - 甚至“管理员”也确实具有受限 token (因此他们不是真正的管理员))并且具有不同的 session ID(比较本地系统 token session ID) - 在 SetThreadToken 之后你的帖子有 SecurityIdentification模仿级别。结果是任何系统调用,其中安全检查(例如打开文件或注册表项)将失败并出现错误 ERROR_BAD_IMPERSONATION_LEVEL .

解决方案如何? 如果用户有管理员权限 - 你需要创建 高架 用户 session 中的子进程(如“以管理员身份运行”)。为此,您需要 WTSQueryUserToken 返回的 token 的查询提升类型如果是 TokenElevationTypeLimited - 我们需要得到 linked token通过 GetTokenInformation调用 TokenLinkedToken .

这完全没有记录,但是 TOKEN_LINKED_TOKEN 中返回了哪个 token 结构依赖于 调用线程(或进程)有 SE_TCB_PRIVILEGE - 如果是 - TokenPrimary 被退回。否则 TokenImpersonation 返回 SECURITY_IMPERSONATION_LEVEL 设置为 SecurityIdentification (所以这个 token 只能用于查询)。因为在本地系统帐户下运行的服务有 SE_TCB_PRIVILEGE - 您获得了需要在 CreateProcessAsUser 中使用的主 token 按原样调用。所以你需要下一个功能:
ULONG GetElevatedUserToken(PHANDLE phToken)
{
union {
ULONG SessionId;
TOKEN_ELEVATION_TYPE tet;
TOKEN_LINKED_TOKEN tlt;
TOKEN_TYPE tt;
};

SessionId = WTSGetActiveConsoleSessionId();

if (SessionId == MAXDWORD)
{
return ERROR_NO_SUCH_LOGON_SESSION;
}

HANDLE hToken;

if (!WTSQueryUserToken(SessionId, &hToken))
{
return GetLastError();
}

ULONG len;

ULONG dwError = NOERROR;

if (GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &len))
{
if (tet == TokenElevationTypeLimited)
{
if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &len))
{
CloseHandle(hToken);
hToken = tlt.LinkedToken;
}
else
{
dwError = GetLastError();
}
}
}
else
{
dwError = GetLastError();
}

if (dwError == NOERROR)
{
if (GetTokenInformation(hToken, TokenType, &tt, sizeof(tt), &len))
{
if (tt != TokenPrimary)
{
dwError = ERROR_INVALID_HANDLE;
}
}
else
{
dwError = GetLastError();
}

if (dwError == NOERROR)
{
*phToken = hToken;
return NOERROR;
}

CloseHandle(hToken);
}

return dwError;
}

并使用下一个代码来启动 child
HANDLE hToken;

ULONG dwError = GetElevatedUserToken(&hToken);

if (dwError == NOERROR)
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
//***
if (CreateProcessAsUser(hToken, ***, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}

CloseHandle(hToken);
}

在这种情况下,您可能根本不需要在子进程中模拟 LocalSystem。但是,如果仍然需要 LocalSystem - 您可以在子进程中复制此类 token ,​​在这种情况下为 SetThreadtoken完全可以,因为子进程将具有模拟权限

关于子进程中的 C++ windows LocalSystem 模拟失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45613702/

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com