Skip to content

Resolve generic payload arch to prevent hanging encoder - #21870

Open
sjanusz-r7 wants to merge 1 commit into
rapid7:masterfrom
sjanusz-r7:fix-generic-payload-encoding
Open

Resolve generic payload arch to prevent hanging encoder#21870
sjanusz-r7 wants to merge 1 commit into
rapid7:masterfrom
sjanusz-r7:fix-generic-payload-encoding

Conversation

@sjanusz-r7

@sjanusz-r7 sjanusz-r7 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Description

This PR fixes an issue/bug when trying to encode a generic payload, causing the console to hang and spin endlessly. This was present for exploit/windows/http/nowsms and exploit/bsdi/softcart/mercantec_softcart modules.

The issue is caused by the encoders trying to brute-force through encoding an incompatible payload, e.g. the riscv64le encoder being used for a generic payload.

Additional logs show the encoders being stuck trying to bruteforce:

> use exploit/windows/http/nowsms
No payload configured, defaulting to generic/shell_reverse_tcp
> run
...
[*] Attempting to encode payload with 1 iteration(s) of x64/xor
[*] Attempting to encode payload with 1 iteration(s) of sparc/longxor_tag
... STUCK HERE ...
Ctrl + C
...
[-] Exploit failed: generic/shell_reverse_tcp: All encoders failed to encode.

Additional logging diff:

diff --git a/lib/msf/core/encoded_payload.rb b/lib/msf/core/encoded_payload.rb
index 06dfba04ad..831506fb0f 100644
--- a/lib/msf/core/encoded_payload.rb
+++ b/lib/msf/core/encoded_payload.rb
@@ -227,6 +227,8 @@ class EncodedPayload
 
         next_encoder = false
 
+        emit_encoder_status("Attempting to encode payload with #{self.iterations} iteration(s) of #{encoder.refname}")
+
         # Try encoding with the current encoder
         #
         # NOTE: Using more than one iteration may cause successive iterations to switch
@@ -263,6 +265,7 @@ class EncodedPayload
 
         next if next_encoder
 
+        emit_encoder_status("Payload encoded with #{encoder.refname} (size is #{eout.length} bytes)")
         self.encoded = eout
         break
       }
@@ -559,6 +562,19 @@ protected
     false
   end
 
+  # Emits a verbose, user-visible status about encoder selection through the
+  # associated exploit (when one is present). This makes the encoder currently
+  # being attempted visible on screen with `set VERBOSE true`; previously the
+  # exploit path only wrote this to the framework log, so a slow or looping
+  # encoder gave no on-screen indication of which encoder was responsible.
+  #
+  # @param msg [String] the message to display
+  # @return [void]
+  def emit_encoder_status(msg)
+    exploit = reqs['Exploit']
+    exploit.vprint_status(msg) if exploit.respond_to?(:vprint_status)
+  end
+
   def compatible_encoders
     arch = reqs['Arch'] || pinst.arch
     platform = reqs['Platform'] || pinst.platform

Proof of Concept

Boot up metasploit framework, type irb then paste in the following commands:

def try_generate(framework, exploit_ref, payload_ref, timeout: 20)
  exp = framework.exploits.create(exploit_ref)
  exp.datastore['TARGET'] = 0

  pay = framework.payloads.create(payload_ref)
  pay.datastore.import_options_from_hash(exp.datastore)
  pay.assoc_exploit = exp

  started = Time.now
  worker  = Thread.new { exp.generate_payload(pay) }

  if worker.join(timeout)
    puts format('  %-28s -> encoded in %.2fs', payload_ref, Time.now - started)
  else
    puts format('  %-28s -> STILL ENCODING after %ds (hung)', payload_ref, timeout)
    puts '     blocked at: ' + (worker.backtrace || []).grep(/encoders/).first.to_s
    worker.kill
  end
end

exploit = 'bsdi/softcart/mercantec_softcart'
try_generate(framework, exploit, 'bsdi/x86/shell_bind_tcp') # This should complete pretty quickly
try_generate(framework, exploit, 'generic/shell_bind_tcp') # This will hang and never complete on 'master', will work with this PR.

This can also be tested with the following commands in the console (Ctrl + C once the console hangs):

use exploit/bsdi/softcart/mercantec_softcart
set PAYLOAD generic/shell_bind_tcp
set RHOSTS 192.0.2.1
run

Before

Generating payloads for bsdi/softcart/mercantec_softcart:
  bsdi/x86/shell_bind_tcp      -> encoded in 0.13s
  generic/shell_bind_tcp       -> STILL ENCODING after 20s (hung)

After

Generating payloads for bsdi/softcart/mercantec_softcart:
  bsdi/x86/shell_bind_tcp      -> encoded in 0.14s
  generic/shell_bind_tcp       -> encoded in 0.24s

Breaking Changes

None

Adding additional logging results in the following logs:

[*] Attempting to encode payload with 1 iteration(s) of x86/shikata_ga_nai
[*] Attempting to encode payload with 1 iteration(s) of x86/xor_poly
[*] Attempting to encode payload with 1 iteration(s) of x86/xor_dynamic
[*] Attempting to encode payload with 1 iteration(s) of x86/jmp_call_additive
[*] Attempting to encode payload with 1 iteration(s) of x86/fnstenv_mov
[*] Attempting to encode payload with 1 iteration(s) of x86/countdown
[*] Attempting to encode payload with 1 iteration(s) of x86/call4_dword_xor
[*] Attempting to encode payload with 1 iteration(s) of x86/nonupper
[*] Attempting to encode payload with 1 iteration(s) of x86/nonalpha
[*] Attempting to encode payload with 1 iteration(s) of x86/alpha_upper
[*] Payload encoded with x86/alpha_upper (size is 299 bytes)

So hopefully, things should continue to work.

Reviewer Notes

None

Verification Steps

    • Boot up Framework
    • type irb
    • execute the code snippet above
    • verify the generic payloads continue to work with an exploit in a real usecase against e.g. metasploitable 2

Test Evidence

Present above

AI Usage Disclosure

Debugged using Kiro

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant