From 9a7ddd9ea6860a02e29cf93118d2505e839d0d23 Mon Sep 17 00:00:00 2001 From: vinicius-batistella Date: Tue, 18 Aug 2026 21:05:16 -0300 Subject: [PATCH 1/2] Add AArch64 host detection to run_hidden_psh [IntPtr]::Size cannot tell an ARM64 process apart from an x64 one (both have 8-byte pointers), so the existing wrapper picked the wrong powershell.exe on Windows-on-ARM whenever the payload architecture mattered. Metasploit's psexec module currently keeps AArch64 payloads off the PowerShell delivery path for this reason. Prepend two branches to the emitted architecture-detection block: - PROCESSOR_ARCHITECTURE == ARM64 for native ARM64 processes. An 'aarch64' payload targets the current binary (System32 already resolves to the ARM64 powershell.exe on WoA); an 'x86' payload hops to SysWOW64 as before. - PROCESSOR_ARCHITEW6432 == ARM64 for a 32-bit process on a WoA host. An 'aarch64' payload escapes WOW64 filesystem redirection via sysnative to reach the native ARM64 powershell.exe. The existing IntPtr::Size branches are preserved and continue to handle every non-WoA case. The 'aarch64' string is documented on both run_hidden_psh and cmd_psh_payload. No template changes: the reflection and dotnet templates already transition memory PAGE_READWRITE -> PAGE_EXECUTE_READ via VirtualProtect, which implicitly invalidates the ARM64 icache for the range, so injected AArch64 shellcode is coherent by the time CreateThread executes it. Verified on Windows 11 26200 (ARM64, Qualcomm) with a minimal 'mov x0,0; ret' probe. Bumps the gem to 0.1.105 for consumers. Co-authored-by: Cursor --- lib/rex/powershell/command.rb | 47 ++++++++++++++++++++++++++--- lib/rex/powershell/version.rb | 2 +- spec/rex/powershell/command_spec.rb | 33 ++++++++++++++++++++ 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/lib/rex/powershell/command.rb b/lib/rex/powershell/command.rb index 0e9bffa..93015a9 100644 --- a/lib/rex/powershell/command.rb +++ b/lib/rex/powershell/command.rb @@ -208,8 +208,14 @@ def self.generate_psh_args(opts) # detect the execution environment and spawn the appropriate # powershell executable for the payload architecture. # + # ARM64 note: [IntPtr]::Size cannot tell an ARM64 process apart from an x64 + # one, so PROCESSOR_ARCHITECTURE (and PROCESSOR_ARCHITEW6432 for a 32-bit + # process on Windows-on-ARM) is consulted first. A payload_arch of + # 'aarch64' targets the native ARM64 powershell.exe under System32 + # (reached via sysnative when the current process is 32-bit). + # # @param ps_code [String] Powershell code - # @param payload_arch [String] The payload architecture 'x86'/'x86_64' + # @param payload_arch [String] The payload architecture 'x86'/'x86_64'/'aarch64' # @param encoded [Boolean] Indicates whether ps_code is encoded or not # @param opts [Hash] The options for generate_psh_args # @@ -240,11 +246,42 @@ def self.run_hidden_psh(ps_code, payload_arch, encoded, opts={}) EOS process_start_info.gsub!("\n", ';') + # Path helpers keep the emitted PowerShell readable and single-quoted so no + # further escaping is required at the target. + native_ps = "$b='powershell.exe'" + syswow64_ps = "$b=$env:windir+'\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe'" + sysnative_ps = "$b=$env:windir+'\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe'" + + # On Windows-on-ARM the native host is the ARM64 powershell.exe; the + # 32-bit x86 host still lives under SysWOW64. When we're already inside a + # 32-bit process on WoA, PROCESSOR_ARCHITECTURE reports 'x86' and + # PROCESSOR_ARCHITEW6432 reports 'ARM64', so we escape to native via + # sysnative. x86_64 payloads on WoA fall through to native and rely on + # the OS x64 emulator, which is best-effort. + arm64_native_branch = case payload_arch + when 'aarch64' then native_ps + when 'x86' then syswow64_ps + else native_ps + end + + arm64_wow64_branch = case payload_arch + when 'aarch64' then sysnative_ps + when 'x86' then native_ps + else sysnative_ps + end + + intptr4_branch = payload_arch == 'x86' ? native_ps : sysnative_ps + intptr8_branch = payload_arch == 'x86' ? syswow64_ps : native_ps + archictecure_detection = < Date: Wed, 19 Aug 2026 13:48:33 -0300 Subject: [PATCH 2/2] Fix architecture_detection typo in run_hidden_psh Rename the pre-existing archictecure_detection local so the generated-code wrapper is easier to search. Behavior is unchanged. Co-authored-by: Cursor --- lib/rex/powershell/command.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rex/powershell/command.rb b/lib/rex/powershell/command.rb index 93015a9..1191109 100644 --- a/lib/rex/powershell/command.rb +++ b/lib/rex/powershell/command.rb @@ -273,7 +273,7 @@ def self.run_hidden_psh(ps_code, payload_arch, encoded, opts={}) intptr4_branch = payload_arch == 'x86' ? native_ps : sysnative_ps intptr8_branch = payload_arch == 'x86' ? syswow64_ps : native_ps - archictecure_detection = <