我正在尝试设置类似于chromium的沙箱。特别是,我试图复制他们使用低权限令牌创建休眠进程的技巧,然后在运行它之前临时设置高权限令牌。我们的想法是让进程在高权限模式下进行所有初始化,然后在运行任何不安全的代码之前立即恢复为低权限令牌。
到目前为止,我正在努力进行基本的测试并运行。这是我的代码:
#include "stdafx.h"
#include <atlbase.h>
#include <iostream>
#include <cassert>
#include <vector>
#include <string>
#include <AccCtrl.h>
#include <aclapi.h>
#define VERIFY(x) { bool r = x; assert(r); }
uint8_t* GetTokenInfo(const HANDLE& token, TOKEN_INFORMATION_CLASS info_class, DWORD* error)
{
// Get the required buffer size.
DWORD size = 0;
::GetTokenInformation(token, info_class, NULL, 0, &size);
if (!size)
{
*error = ::GetLastError();
return nullptr;
}
uint8_t* buffer = new uint8_t[size];
if (!::GetTokenInformation(token, info_class, buffer, size, &size))
{
*error = ::GetLastError();
return nullptr;
}
*error = ERROR_SUCCESS;
return buffer;
}
int main()
{
// Open the current token
CHandle processToken;
VERIFY(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &processToken.m_h));
// Create an impersonation token without restrictions
HANDLE impersonationToken;
VERIFY(DuplicateToken(processToken, SecurityImpersonation, &impersonationToken));
// Build the list of the deny only group SIDs
DWORD error;
uint8_t* buffer = GetTokenInfo(processToken, TokenGroups, &error);
if (!buffer) return error;
TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer);
std::vector<SID*> sids_for_deny_only;
for (unsigned int i = 0; i < token_groups->GroupCount; ++i)
{
if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 &&
(token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0)
{
sids_for_deny_only.push_back(reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
}
}
{
DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
uint8_t* buffer = new uint8_t[size];
TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(buffer);
BOOL result = ::GetTokenInformation(processToken, TokenUser, token_user, size, &size);
if (!result) return ::GetLastError();
sids_for_deny_only.push_back(reinterpret_cast<SID*>(token_user->User.Sid));
}
size_t deny_size = sids_for_deny_only.size();
SID_AND_ATTRIBUTES *deny_only_array = NULL;
if (deny_size)
{
deny_only_array = new SID_AND_ATTRIBUTES[deny_size];
for (unsigned int i = 0; i < sids_for_deny_only.size(); ++i)
{
deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY;
deny_only_array[i].Sid = const_cast<SID*>(sids_for_deny_only[i]);
}
}
// Create restricted sids
DWORD size_sid = SECURITY_MAX_SID_SIZE;
BYTE sid_[SECURITY_MAX_SID_SIZE];
VERIFY(::CreateWellKnownSid(WinNullSid, NULL, sid_, &size_sid));
SID_AND_ATTRIBUTES sidsToRestrict[] =
{
reinterpret_cast<SID*>(const_cast<BYTE*>(sid_)),
0
};
// Create the restricted token
HANDLE restrictedToken;
VERIFY(::CreateRestrictedToken(processToken,
0, // flags
deny_size,
deny_only_array,
0,
0,
_countof(sidsToRestrict), // number of SIDs to restrict,
sidsToRestrict, // no SIDs to restrict,
&restrictedToken));
VERIFY(::IsTokenRestricted(restrictedToken));
// Create a process using the restricted token (but keep it suspended)
STARTUPINFO startupInfo = { 0 };
PROCESS_INFORMATION processInfo;
VERIFY(::CreateProcessAsUser(restrictedToken,
L"C:\\Dev\\Projects\\SandboxTest\\Debug\\Naughty.exe",
0, // cmd line
0, // process attributes
0, // thread attributes
FALSE, // don't inherit handles
CREATE_SUSPENDED | DETACHED_PROCESS, // flags
0, // inherit environment
0, // inherit current directory
&startupInfo,
&processInfo));
// Set impersonation token with more rights
{
HANDLE temp_thread = processInfo.hThread;
if (!::SetThreadToken(&temp_thread, impersonationToken))
{
return 1;
}
}
// Run the process
if (!::ResumeThread(processInfo.hThread)) // Other process crashes immediately when this is run
{
return 1;
}
std::cout << "Done!" << std::endl;
return 0;
}
还不确定拒绝列表和限制列表,但如果我理解正确,那应该是无关紧要的。我在运行线程之前使用我的无限制令牌调用SetThreadToken,所以我认为对于restrictedToken我使用的设置无关紧要。然而,这种情况并非如此;新进程崩溃,错误代码为0xc00000a5。如果我在CreateProcessAsUser中使用processToken而不是restrictedToken,则代码运行得很好。这就像SetThreadToken没有做任何事情。
我现在在naughty.exe上做的不多,只是开始无限循环。
任何人都知道我在这里做错了吗?
编辑1: 根据{{3}}页面,0xc00000a5表示“STATUS_BAD_IMPERSONATION_LEVEL”。不确定,但我认为我错过了SeImpersonatePrivilege,导致事情失败。仍在调查选择......
编辑2: 好吧,似乎我必须减少模拟令牌的权限才能将其与其他进程一起使用。不知道为什么,但不能在没有管理员权限的情况下运行程序。
虽然仍然出现错误:/现在它是“STATUS_DLL_NOT_FOUND”。检查Process Monitor日志的最佳线索是“C:\ Windows \ SysWOW64 \ ucrtbased.dll”上的ACCESS DENIED。奇怪的是它似乎偶尔会起作用(即产生的过程有时运行得很好)。回到挖掘...