Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
47 changes: 42 additions & 5 deletions lib/rex/powershell/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand Down Expand Up @@ -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 = <<EOS

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done in 0608202.

if([IntPtr]::Size -eq 4){
#{payload_arch == 'x86' ? "$b='powershell.exe'" : "$b=$env:windir+'\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe'"}
if($env:PROCESSOR_ARCHITECTURE -eq 'ARM64'){
#{arm64_native_branch}
}elseif($env:PROCESSOR_ARCHITEW6432 -eq 'ARM64'){
#{arm64_wow64_branch}
}elseif([IntPtr]::Size -eq 4){
#{intptr4_branch}
}else{
#{payload_arch == 'x86' ? "$b=$env:windir+'\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe'" : "$b='powershell.exe'"}
#{intptr8_branch}
};
EOS

Expand All @@ -264,7 +301,7 @@ def self.run_hidden_psh(ps_code, payload_arch, encoded, opts={})
# run_hidden_psh, generate_psh_command_line and generate_psh_args
#
# @param pay [String] The payload shellcode
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'/'aarch64'
# @param opts [Hash] The options to generate the command
# @option opts [Boolean] :persist Loop the payload to cause
# re-execution if the shellcode finishes
Expand Down
2 changes: 1 addition & 1 deletion lib/rex/powershell/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Rex
module Powershell
VERSION = "0.1.104"
VERSION = "0.1.105"
end
end
33 changes: 33 additions & 0 deletions spec/rex/powershell/command_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,39 @@ def decompress(code)
end
end

context 'when aarch64 payload' do
let(:code) { subject.run_hidden_psh(payload, 'aarch64', encoded) }

it 'branches on PROCESSOR_ARCHITECTURE (ARM64) instead of [IntPtr]::Size' do
expect(code).to include("$env:PROCESSOR_ARCHITECTURE -eq 'ARM64'")
expect(code).to include("$env:PROCESSOR_ARCHITEW6432 -eq 'ARM64'")
end

it 'targets the native powershell.exe on a native ARM64 host' do
arm64_branch = code[/PROCESSOR_ARCHITECTURE -eq 'ARM64'\)\{([^}]*)\}/, 1]
expect(arm64_branch).to include("$b='powershell.exe'")
end

it 'escapes WOW64 filesystem redirection via sysnative when running 32-bit on WoA' do
wow64_branch = code[/PROCESSOR_ARCHITEW6432 -eq 'ARM64'\)\{([^}]*)\}/, 1]
expect(wow64_branch).to include('sysnative')
end
end

context 'when x86 payload on a WoA host' do
let(:code) { subject.run_hidden_psh(payload, 'x86', encoded) }

it 'still routes 32-bit payloads through SysWOW64 on native ARM64 hosts' do
arm64_branch = code[/PROCESSOR_ARCHITECTURE -eq 'ARM64'\)\{([^}]*)\}/, 1]
expect(arm64_branch).to include('syswow64')
end

it 'keeps the current process when already 32-bit on WoA' do
wow64_branch = code[/PROCESSOR_ARCHITEW6432 -eq 'ARM64'\)\{([^}]*)\}/, 1]
expect(wow64_branch).to include("$b='powershell.exe'")
end
end

context 'when encoded' do
it 'should generate a code including an encoded command' do
code = subject.run_hidden_psh(payload, arch, true)
Expand Down