0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【C++】NT Authority\SYSTEM として外部プログラムを実行する

Last updated at Posted at 2023-03-22

目的

Windows のプロセスは、特定のユーザーとして実行できる。それは、ユーザーアカウントだけではなく、※サービスアカウントでも同じことだ。

※ ここでいうサービスアカウントとは、NT Authority\SYSTEM 等のアカウントを指します。
詳しくは、こちら をご覧ください。

これが何に役立つかといえば、ユーザー次第では、保護されたシステムファイルを操作するなどの、Administrator 以上の権限を必要とする場合だ。 (SeBackupPrivilege と SeRestorePrivilege 特権が有効化された管理者権限を持つプロセスなら、ACL を無視して操作ができる。)

NT Authority\SYSTEM はとても強力な権限を持ちます。
下手に扱うとコンピュータのセキュリティリスクを高めるだけなので、自己責任で行ってください。

使用環境

ソース

ソースファイル
wWinMain.cpp
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <TlHelp32.h>

#include <iostream>
#include <vector>
#include <memory>

namespace {
	constexpr LPCWSTR lpCommand = L"C:\\Windows\\System32\\cmd.exe";	// 実行したいファイル・コマンド

	constexpr LPCWSTR lpProcessImageName = L"Winlogon.exe";	// NT Authority\SYSTEM として実行されているプロセス ※注): 必ずプロセスが1つのみしか存在しないもの
}

::INT APIENTRY wWinMain(
	_In_		::HINSTANCE	hInst,
	_In_opt_	::HINSTANCE	/* hPrvInst = nullptr */,
	_In_		::LPWSTR	lpCmdLine,
	_In_		::INT		nCmdShow
) {
	::HANDLE hCurrentToken = nullptr;	// 自分のトークンハンドル
	
	/* プロセス(自分)のトークンハンドルを取得 */
	::OpenProcessToken(
		::OpenProcess(
			PROCESS_ALL_ACCESS,
			FALSE,
			::GetCurrentProcessId()
		),
		TOKEN_ALL_ACCESS,
		&hCurrentToken
	);

	if (!hCurrentToken) return -1;

	// 必要な特権
	std::vector<::LPCWSTR> PrivilegeNames{
		SE_DEBUG_NAME, SE_INCREASE_QUOTA_NAME, SE_ASSIGNPRIMARYTOKEN_NAME,
		SE_CREATE_TOKEN_NAME, SE_TCB_NAME, SE_DELEGATE_SESSION_USER_IMPERSONATE_NAME,
		SE_IMPERSONATE_NAME
	};

	::DWORD dwcbComputerNameLength = 256;
	auto szComputerName = std::make_unique<wchar_t[]>(dwcbComputerNameLength);

	// コンピュータの名前を取得
	if (!::GetComputerNameW(szComputerName.get(), &dwcbComputerNameLength)) {
		szComputerName = nullptr;
	}

	/* 必要な特権の有効化 */
	for (auto& lpPrivilegeName : PrivilegeNames) {
		::TOKEN_PRIVILEGES tp{};
		::LUID luId{};

		if (!::LookupPrivilegeValueW(
			szComputerName.get(),
			lpPrivilegeName,
			&luId
		)) return ::GetLastError();

		tp.PrivilegeCount = 1;
		tp.Privileges[0].Luid = luId;
		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

		if (!::AdjustTokenPrivileges(
			hCurrentToken,
			FALSE,
			&tp,
			sizeof(tp),
			nullptr,
			nullptr
		)) return ::GetLastError();
	}

	// lpProcessImageName が示すプロセスの PID
	::DWORD dwProcessImagePID = -1;

	/* lpProcessImageName が示すプロセスの PID を取得 */
	::HANDLE hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	::PROCESSENTRY32W ProcessEntry{ sizeof(::PROCESSENTRY32W) };

	if (!::Process32FirstW(hSnapshot, &ProcessEntry)) return ::GetLastError();

	do {
		if (!::_wcsicmp(::lpProcessImageName, ProcessEntry.szExeFile)) {
			::CloseHandle(hSnapshot);
			
			dwProcessImagePID = ProcessEntry.th32ProcessID;

			break;
		}
	} while (::Process32NextW(hSnapshot, &ProcessEntry));

	if (dwProcessImagePID == -1) return -1;

	// lpProcessImageName が示すプロセスのトークンハンドル
	::HANDLE hSystemProcessToken = nullptr;

	// lpProcessImageName が示すプロセスのトークンハンドルを取得 (NT Authority\SYSTEM として実行されているため)
	if (!::OpenProcessToken(
		::OpenProcess(
			PROCESS_QUERY_INFORMATION,
			FALSE, 
			dwProcessImagePID
		),
		TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_IMPERSONATE,
		&hSystemProcessToken
	)) return ::GetLastError();

	// NT Authority\SYSTEM を示すトークンハンドル
	::HANDLE hToken = nullptr;
	
	// トークンハンドルを複製
	if (!::DuplicateTokenEx(
		hSystemProcessToken,
		TOKEN_ALL_ACCESS,
		nullptr,
		::SECURITY_IMPERSONATION_LEVEL::SecurityIdentification,
		::TOKEN_TYPE::TokenPrimary,
		&hToken
	)) return ::GetLastError();

	// プロセスの作成に必要な情報
	::STARTUPINFOW stInf{};
	::RtlSecureZeroMemory(&stInf, sizeof(STARTUPINFOW));

	stInf.cb = sizeof(stInf);

	// 既定のデスクトップ名を明示的に指定 (ウィンドウを表示されるため)
	wchar_t szDesktopName[] = L"Winsta0\\default";
	stInf.lpDesktop = szDesktopName;
	
	// プロセスに関する情報
	::PROCESS_INFORMATION pcInf{};
	::RtlSecureZeroMemory(&pcInf, sizeof(PROCESS_INFORMATION));

	// 取得したトークンハンドルをもとに、プログラムを実行
	auto bRet = ::CreateProcessWithTokenW(
		hToken,
		LOGON_WITH_PROFILE,
		::lpCommand,
		nullptr,
		NULL,
		nullptr,
		nullptr,
		&stInf,
		&pcInf
	);
	if (!bRet) {
		return ::GetLastError();
	}

	// 不要なハンドルをクローズ、開放
	::CloseHandle(pcInf.hProcess);
	::CloseHandle(pcInf.hThread);

	// 正常終了
	return 0;
}

仕組み

このプログラムは以下のような処理を主に行い、NT Authority\SYSTEM として実行する。

関数 処理
OpenProcessToken 自分のプロセスのトークンハンドルを取得
AdjustTokenPrivileges 取得したトークンハンドルを用いて、必要な特権を有効化
Toolhelp32 系関数 プロセスを列挙し、NT Authority\SYSTEM として実行されているプロセスの PID を取得
OpenProcessToken 取得した PID から、トークンハンドルを取得
DuplicateTokenEx 取得したトークンハンドルを複製
CreateProcessWithToken 複製したトークンハンドルからプロセスを作成

※ 処理の順番は上から下に

つまり、NT Authority\SYSTEM として実行されているプロセスから実行しているユーザー情報を抜き取って、それをもとに新しいプロセスを作成する という処理を行っているのだ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?