Skip to content

Conversation

dledda-r7
Copy link
Contributor

@dledda-r7 dledda-r7 commented Mar 20, 2025

Framework side PR for rapid7/metasploit-payloads#744

This PR allows to load portion of stdapi by using namespace-specific dlls.

  • Make sure meterpreter with default AutoLoadExtension values works
  • Make sure meterpreter with empty AutoLoadExtension and load different stdapi_ extensions
  • Make sure linux meterpreter still works
  • Make sure the embedded extensions still works
msf6 payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > sessions -i -1
[*] Starting interaction with 1...

meterpreter > help

Core Commands
=============

    Command                   Description
    -------                   -----------
    ?                         Help menu
    background                Backgrounds the current session
    bg                        Alias for background
    bgkill                    Kills a background meterpreter script
    bglist                    Lists running background scripts
    bgrun                     Executes a meterpreter script as a background thread
    channel                   Displays information or control active channels
    close                     Closes a channel
    detach                    Detach the meterpreter session (for http/https)
    disable_unicode_encoding  Disables encoding of unicode strings
    enable_unicode_encoding   Enables encoding of unicode strings
    exit                      Terminate the meterpreter session
    get_timeouts              Get the current session timeout values
    guid                      Get the session GUID
    help                      Help menu
    info                      Displays information about a Post module
    irb                       Open an interactive Ruby shell on the current session
    load                      Load one or more meterpreter extensions
    machine_id                Get the MSF ID of the machine attached to the session
    migrate                   Migrate the server to another process
    pivot                     Manage pivot listeners
    pry                       Open the Pry debugger on the current session
    quit                      Terminate the meterpreter session
    read                      Reads data from a channel
    resource                  Run the commands stored in a file
    run                       Executes a meterpreter script or Post module
    secure                    (Re)Negotiate TLV packet encryption on the session
    sessions                  Quickly switch to another session
    set_timeouts              Set the current session timeout values
    sleep                     Force Meterpreter to go quiet, then re-establish session
    ssl_verify                Modify the SSL certificate verification setting
    transport                 Manage the transport mechanisms
    use                       Deprecated alias for "load"
    uuid                      Get the UUID for the current session
    write                     Writes data to a channel

For more info on a specific command, use <command> -h or help <command>.

meterpreter > ls
[-] The "ls" command requires the "stdapi_fs" extension to be loaded (run: `load stdapi_fs`)
meterpreter > load stdapi_fs
Loading extension stdapi_fs...WARNING: Local file /home/kali/Documents/github/metasploit-framework/data/meterpreter/ext_server_stdapi_fs.x64.dll is being used
Success.
meterpreter > help

Core Commands
=============

    Command                   Description
    -------                   -----------
    ?                         Help menu
    background                Backgrounds the current session
    bg                        Alias for background
    ...
    write                     Writes data to a channel


Stdapi: File system Commands
============================

    Command                   Description
    -------                   -----------
    cat                       Read the contents of a file to the screen
    cd                        Change directory
    checksum                  Retrieve the checksum of a file
    cp                        Copy source to destination
    del                       Delete the specified file
    dir                       List files (alias for ls)
    download                  Download a file or directory
    edit                      Edit a file
    getlwd                    Print local working directory (alias for lpwd)
    getwd                     Print working directory
    lcat                      Read the contents of a local file to the screen
    lcd                       Change local working directory
    ldir                      List local files (alias for lls)
    lls                       List local files
    lmkdir                    Create new directory on local machine
    lpwd                      Print local working directory
    ls                        List files
    mkdir                     Make directory
    mv                        Move source to destination
    pwd                       Print working directory
    rm                        Delete the specified file
    rmdir                     Remove directory
    search                    Search for files
    show_mount                List all mount points/logical drives
    upload                    Upload a file or directory

For more info on a specific command, use <command> -h or help <command>.

meterpreter > sysinfo
[-] The "sysinfo" command requires the "stdapi_sys" extension to be loaded (run: `load stdapi_sys`)
meterpreter > load stdapi_sys
Loading extension stdapi_sys...WARNING: Local file /home/kali/Documents/github/metasploit-framework/data/meterpreter/ext_server_stdapi_sys.x64.dll is being used
Success.
meterpreter > help

Core Commands
=============

    Command                   Description
    -------                   -----------
    ?                         Help menu
    background                Backgrounds the current session
    bg                        Alias for background
    ....
    write                     Writes data to a channel


Stdapi: File system Commands
============================

    Command                   Description
    -------                   -----------
    cat                       Read the contents of a file to the screen
    cd                        Change directory
    ...
    upload                    Upload a file or directory


Stdapi: System Commands
=======================

    Command                   Description
    -------                   -----------
    clearev                   Clear the event log
    drop_token                Relinquishes any active impersonation token.
    execute                   Execute a command
    getenv                    Get one or more environment variable values
    getpid                    Get the current process identifier
    getprivs                  Attempt to enable all privileges available to the current process
    getsid                    Get the SID of the user that the server is running as
    getuid                    Get the user that the server is running as
    kill                      Terminate a process
    localtime                 Displays the target system local date and time
    pgrep                     Filter processes by name
    pkill                     Terminate processes by name
    ps                        List running processes
    reboot                    Reboots the remote computer
    reg                       Modify and interact with the remote registry
    rev2self                  Calls RevertToSelf() on the remote machine
    shell                     Drop into a system command shell
    shutdown                  Shuts down the remote computer
    steal_token               Attempts to steal an impersonation token from the target process
    suspend                   Suspends or resumes a list of processes
    sysinfo                   Gets information about the remote system, such as OS

For more info on a specific command, use <command> -h or help <command>.

meterpreter > arp
[-] The "arp" command requires the "stdapi_net" extension to be loaded (run: `load stdapi_net`)
meterpreter > load stdapi_net
Loading extension stdapi_net...WARNING: Local file /home/kali/Documents/github/metasploit-framework/data/meterpreter/ext_server_stdapi_net.x64.dll is being used
Success.
meterpreter > help

Core Commands
=============

    Command                   Description
    -------                   -----------
    ?                         Help menu
    background                Backgrounds the current session
    bg                        Alias for background
    ...
    write                     Writes data to a channel


Stdapi: File system Commands
============================

    Command                   Description
    -------                   -----------
    cat                       Read the contents of a file to the screen
    cd                        Change directory
    ...
    upload                    Upload a file or directory


Stdapi: System Commands
=======================

    Command                   Description
    -------                   -----------
    clearev                   Clear the event log
    drop_token                Relinquishes any active impersonation token.
    ...
    sysinfo                   Gets information about the remote system, such as OS


Stdapi: Networking Commands
===========================

    Command                   Description
    -------                   -----------
    arp                       Display the host ARP cache
    getproxy                  Display the current proxy configuration
    ifconfig                  Display interfaces
    ipconfig                  Display interfaces
    netstat                   Display the network connections
    portfwd                   Forward a local port to a remote service
    resolve                   Resolve a set of host names on the target
    route                     View and modify the routing table

For more info on a specific command, use <command> -h or help <command>.

meterpreter > ls
Listing: C:\Users\User
======================

Mode              Size     Type  Last modified              Name
----              ----     ----  -------------              ----
040777/rwxrwxrwx  0        dir   2024-09-10 04:48:08 -0400  .chocolatey
100666/rw-rw-rw-  20       fil   2025-03-11 10:59:44 -0400  .lesshst
040777/rwxrwxrwx  0        dir   2024-09-23 06:11:45 -0400  .ssh
040777/rwxrwxrwx  0        dir   2024-09-19 09:00:41 -0400  .vscode
040777/rwxrwxrwx  0        dir   2024-09-06 09:11:30 -0400  AppData
040777/rwxrwxrwx  0        dir   2024-09-06 09:11:30 -0400  Application Data
040555/r-xr-xr-x  0        dir   2024-09-06 09:22:22 -0400  Contacts
040777/rwxrwxrwx  0        dir   2024-09-06 09:11:30 -0400  Cookies
040555/r-xr-xr-x  4096     dir   2025-02-20 09:09:11 -0500  Desktop
040555/r-xr-xr-x  4096     dir   2024-09-24 08:33:33 -0400  Documents
040555/r-xr-xr-x  4096     dir   2025-02-18 06:38:23 -0500  Downloads
040555/r-xr-xr-x  0        dir   2024-09-06 09:22:22 -0400  Favorites
040555/r-xr-xr-x  0        dir   2024-09-06 09:22:22 -0400  Links
040777/rwxrwxrwx  0        dir   2024-09-06 09:11:30 -0400  Local Settings
040555/r-xr-xr-x  0        dir   2024-09-06 09:22:22 -0400  Music
040777/rwxrwxrwx  0        dir   2024-09-06 09:11:30 -0400  My Documents
.....
040777/rwxrwxrwx  0        dir   2024-09-11 08:13:29 -0400  source

meterpreter > sysinfo
Computer        : WINVM01
OS              : Windows 11 (10.0 Build 22631).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows
meterpreter > arp

ARP cache
=========

    IP address       MAC address        Interface
    ----------       -----------        ---------
    224.0.0.22       00:00:00:00:00:00  Software Loopback Interface 1
    239.255.255.250  00:00:00:00:00:00  Software Loopback Interface 1
    ....
    255.255.255.255  ff:ff:ff:ff:ff:ff  Microsoft Hyper-V Network Adapter

meterpreter >

@dledda-r7 dledda-r7 added payload rn-payload-enhancement release notes for enhanced payloads blocked Blocked by one or more additional tasks labels Apr 9, 2025
@dledda-r7 dledda-r7 moved this from Todo to In Progress in Metasploit Kanban Apr 9, 2025
@dledda-r7 dledda-r7 self-assigned this Apr 9, 2025
@dledda-r7 dledda-r7 marked this pull request as ready for review June 16, 2025 09:15
@dledda-r7 dledda-r7 changed the title [WIP] Split Stdapi Split Stdapi Jun 16, 2025
@dledda-r7 dledda-r7 removed the blocked Blocked by one or more additional tasks label Aug 11, 2025
@dledda-r7 dledda-r7 removed their assignment Aug 14, 2025
@bwatters-r7
Copy link
Contributor

I checked out 744 and built everything. The libraries are present, and I moved them over, but it looks like stdapi gets loaded like normal?

image

@dledda-r7
Copy link
Contributor Author

dledda-r7 commented Aug 26, 2025

I checked out 744 and built everything. The libraries are present, and I moved them over, but it looks like stdapi gets loaded like normal?

image

Yeah, that is the expected default behavior. In order to change it you should:

set AutoLoadStdapi false
set AutoLoadExtensions ''
to_handler

Then you can load one or more namespaces.

@bwatters-r7
Copy link
Contributor

I get a validation failure for AutoloadExtensions when it is clear?

msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > show options

Module options (payload/cmd/windows/http/x64/meterpreter/reverse_tcp):

   Name                Current Setting  Required  Description
   ----                ---------------  --------  -----------
   EXITFUNC            process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   FETCH_COMMAND       CERTUTIL         yes       Command to fetch payload (Accepted: CURL, TFTP, CERTUTIL)
   FETCH_DELETE        false            yes       Attempt to delete the binary after execution
   FETCH_FILENAME      EdgqRKNn         no        Name to use on remote system when storing payload; cannot contain spaces or slash
                                                  es
   FETCH_SRVHOST                        no        Local IP to use for serving payload
   FETCH_SRVPORT       8080             yes       Local port to use for serving payload
   FETCH_URIPATH                        no        Local URI to use for serving payload
   FETCH_WRITABLE_DIR  %TEMP%           yes       Remote writable dir to store payload; cannot contain spaces.
   LHOST               10.5.135.201     yes       The listen address (an interface may be specified)
   LPORT               4565             yes       The listen port


   When FETCH_COMMAND is one of CURL:

   Name        Current Setting  Required  Description
   ----        ---------------  --------  -----------
   FETCH_PIPE  false            yes       Host both the binary payload and the command so it can be piped directly to the shell.


View the full module info with the info, or info -d command.

msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > git log | head -n 5
[*] exec: git log | head -n 5

commit 53ce08ef6e90c8d97cfed0927e33668f8c77cb00
Author: dledda-r7 <dledda-r7@github>
Date:   Sun Jun 15 09:55:09 2025 -0400

    feat: add stdapi_webcam extension
msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > set AutoloadStdapi false
AutoloadStdapi => false
msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > set --clear AutoloadExtensions
AutoloadExtensions => 
msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > to_handler
[-] Msf::OptionValidateError One or more options failed to validate: AutoLoadExtensions.
msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > 

@dledda-r7
Copy link
Contributor Author

I get a validation failure for AutoloadExtensions when it is clear?

msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > show options

Module options (payload/cmd/windows/http/x64/meterpreter/reverse_tcp):

   Name                Current Setting  Required  Description
   ----                ---------------  --------  -----------
   EXITFUNC            process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   FETCH_COMMAND       CERTUTIL         yes       Command to fetch payload (Accepted: CURL, TFTP, CERTUTIL)
   FETCH_DELETE        false            yes       Attempt to delete the binary after execution
   FETCH_FILENAME      EdgqRKNn         no        Name to use on remote system when storing payload; cannot contain spaces or slash
                                                  es
   FETCH_SRVHOST                        no        Local IP to use for serving payload
   FETCH_SRVPORT       8080             yes       Local port to use for serving payload
   FETCH_URIPATH                        no        Local URI to use for serving payload
   FETCH_WRITABLE_DIR  %TEMP%           yes       Remote writable dir to store payload; cannot contain spaces.
   LHOST               10.5.135.201     yes       The listen address (an interface may be specified)
   LPORT               4565             yes       The listen port


   When FETCH_COMMAND is one of CURL:

   Name        Current Setting  Required  Description
   ----        ---------------  --------  -----------
   FETCH_PIPE  false            yes       Host both the binary payload and the command so it can be piped directly to the shell.


View the full module info with the info, or info -d command.

msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > git log | head -n 5
[*] exec: git log | head -n 5

commit 53ce08ef6e90c8d97cfed0927e33668f8c77cb00
Author: dledda-r7 <dledda-r7@github>
Date:   Sun Jun 15 09:55:09 2025 -0400

    feat: add stdapi_webcam extension
msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > set AutoloadStdapi false
AutoloadStdapi => false
msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > set --clear AutoloadExtensions
AutoloadExtensions => 
msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > to_handler
[-] Msf::OptionValidateError One or more options failed to validate: AutoLoadExtensions.
msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > 

My bad, in metasploit required strings cannot be set as 'nil'

instead of
set --clear AutoLoadExtensions
we need to do:
set AutoLoadExtensions ''

@bwatters-r7 bwatters-r7 self-assigned this Aug 28, 2025
@cdelafuente-r7
Copy link
Contributor

Regarding your suggestions for the "Already Loaded Extensions", I think the first solution is the safest and avoid breaking changes. Why should it be considered as a short term solution? What are the drawbacks for this solution?

Also, I'm not super familiar with the Meterpreter bootstrap process, but is there a way the payload can advertise itself which extensions are loaded in the session instead of having a routine that will test every extensions? I'm thinking about reducing the footprint and limiting the number of back-and-forth between the client and the payload.

@dledda-r7
Copy link
Contributor Author

Regarding your suggestions for the "Already Loaded Extensions", I think the first solution is the safest and avoid breaking changes. Why should it be considered as a short term solution? What are the drawbacks for this solution?

Also, I'm not super familiar with the Meterpreter bootstrap process, but is there a way the payload can advertise itself which extensions are loaded in the session instead of having a routine that will test every extensions? I'm thinking about reducing the footprint and limiting the number of back-and-forth between the client and the payload.

Thanks a lot Christophe for your review, I will consider the first solution as cheap and short-term for the following reasons:

  • I generally don't like introducing edge cases, this will create edge-case in the config.rb of meterpreter, because we need to specifically check for split_ and overall we need to accomodate this in the other section of the code (specifically looking for split_ in the extension name and check the platform).
  • I would like the user to be able to include stdapi_fs as embedded extension, and delivering the new extensions that doesn't have the 100% of support of the older extension seems a bit off to me.

Regarding your question to the meterpreter bootstrap process, meterpreter per-se have a different granularity compared to framework. Meterpreter think in commands, when we load an extension, inside the init of the extension, we register commands to the core array for request dispatch. The command ids registered should carry a mask, if i have an extension with id 0x2000, the commands should look like 0x2000 + unique_integer. We are not strickly enforcing this, for example stdapi register core reserved commands for file channel opening to guarantee a more smooth execution. Ideally if we move from a flat command_id array to a extension-based command array we could have more control over it. Also in this case we should be very carefull in modification on framework side because we can endup needing modify the other 4 meterpreters

@smcintyre-r7
Copy link
Contributor

Already Loaded Extension

There is an issue in how we "manage" the already loaded extension, the default way works as the following:

We start a meterpreter session, with for example stdapi already loaded (this is the case of mettle, or for example a stageless meterpreter with the EXTENSION option set to stdapi). if we perform load stdapi it will check if we have the extension registered in the client side, if not it will use the use defined inside client core, the use function will send an ENUMEXTCMD request passing the extension id (used as "start" range), this will get result of registered commands for that extension. if the commands.length are != 0, it will assume we assume the extension is already loaded and we just register the client side of the extension. easy win.

With the introduction of split-stdapi we are breaking the assumption that if the commands registered are != 0 the extension is already loaded because the different stdapi namespaces share the same extension id (0x1000).

Now, since we cannot determine run-time if the already loaded extension is the full stdapi or one of namespace we have two possible solution.

Solution 1 - Prevent embedding split-stdapi in meterpreter (short term solution, easy to implement)

The first modification required is to create a function inside the client core called load_embedded_extensions. this should be called only once inside the meterpreter session bootstrap right before loading the AutoLoadExtensions. The goal of this function is to iterate through all the possible extension and determinate which one are already present inside the session, if they are, the client side of the extension is loaded, at this point we proceed with the normal meterpreter bootstrap flow.

To implement this solution we should modify the config generation logic for meterpreter to raise an error when the user tries to embedd a stdapi_ extension (a namespaced stdapi), in this way, when we are inside load_embedded_extensions we know we have the full stdapi already in memory if we find any command registered.

then we need to modify the cmd_load and the use (function inside the client core), to ensure we can load only stdapi_ extensions along side each-others and not load stdapi and later on stdapi_ and vice-versa.

Solution 2 - Change commands ID for stdapi (breaking-change solution, more elegant, long term)

Instead of running around the issue of the shared EXTENSION ID, we modify the stdapi command id convetion introducing a namespace id, something like

#define STDAPI_EXTENSION_ID 0x1000
#define STDAPI_EXTENSION_FS_ID 0x0100
#define STDAPI_EXTENSION_NET_ID 0x0200
...

#define STDAPI_CMD_FS_LS STDAPI_EXTENSION_ID | STDAPI_EXTENSION_FS_ID + 1
...
#define STDAPI_CMD_NET_IFCONFIG STDAPI_EXTENSION_ID | STDAPI_EXTENSION_NET_ID + 1

In this way we can run ENUMEXTCMD to start with (STDAPI_EXTENION_ID + NAMESOACE_ID) with a length of (0x0100) and enumerate the namespace specific functions. we can also define a STDAPI_CMD_IS_FULL_EXTENSION that is present only in the complete version of stdapi. this changes require modify all the other meterpreters.

I would recommend the second option if we wanted to switch this to the 6.5 branch as a breaking change. It would keep things better organized and be a better change long term. You'll still have an issue figuring out what extensions are loaded though, the grouping of the IDs will only simply the logic so far.

Take the following pseudo code for consideration as an example

def get_loaded_extensions
  extensions = [] # populate with the others as we do now, extapi, espia, etc.

  extensions.delete('stdapi') # process this as a special case
  
  stdapi_subextensions_for_platform = get_stdapi_subextensions_for_platform
  if stdapi_subextensions_for_platform.empty?
    # if there are no subextensions, then we can simply assume that stdapi is loaded if there is *any* overlap
    extensions << 'stdapi' unless (command_ids.to_set & STDAPI_EXTENSION_ID.upto(STDAPI_EXTENSION_ID + EXTENSION_RANGE - 1).to_set).empty?
  else
    stdapi_subextensions_for_platform.each do |subextension_name|
      # add the subextension if the commands overlap with any of it's commands
      extensions << subextension_name unless (command_ids.to_set & get_subextension_ids(subextension_name)).empty?
    end
    

    if (extensions.to_set & stdapi_subextensions_for_platform.to_set) == stdapi_subextensions_for_platform.to_set
       # if *all* subextensions are loaded, remove them and declare that 'stdapi' is loaded
       extensions -= stdapi_subextensions_for_platform
       extensions << 'stdapi'
    end
  end
end

def get_subextension_ids(subextension_name)
  # this abstraction allows the IDs to either be hardcoded as they are now so we can maintain backwards compatibility
  return HARDCODED_SUBIDS[subextension_name]

  # or use the range if they're grouped together
  range_start = { 'stdapi-fs' => STDAPI_FS_EXTENSION_ID }.fetch(subextension_name)
  return range_start.upto(range_start + RANGE - 1).to_a
end

def get_stdapi_subextensions_for_platform
  # return the subextensions based on the platform
  return [] if platform != 'windows'

  return ['fs', 'registry', 'railgun', etc.] # enumerate this from the files we have on disk with the `Metasploit::Payloads` gem
end

With that in place you should have all of the logic for all of the Meterpreters to identify when one of the subextensions is loaded, stdapi isn't loaded at all, all of the subextensions are loaded or the combined stdapi is loaded. This does assume however that if all of the subextensions are loaded, that it's functionally equivalent to having the main stdapi extension loaded.

1 similar comment
@smcintyre-r7
Copy link
Contributor

Already Loaded Extension

There is an issue in how we "manage" the already loaded extension, the default way works as the following:

We start a meterpreter session, with for example stdapi already loaded (this is the case of mettle, or for example a stageless meterpreter with the EXTENSION option set to stdapi). if we perform load stdapi it will check if we have the extension registered in the client side, if not it will use the use defined inside client core, the use function will send an ENUMEXTCMD request passing the extension id (used as "start" range), this will get result of registered commands for that extension. if the commands.length are != 0, it will assume we assume the extension is already loaded and we just register the client side of the extension. easy win.

With the introduction of split-stdapi we are breaking the assumption that if the commands registered are != 0 the extension is already loaded because the different stdapi namespaces share the same extension id (0x1000).

Now, since we cannot determine run-time if the already loaded extension is the full stdapi or one of namespace we have two possible solution.

Solution 1 - Prevent embedding split-stdapi in meterpreter (short term solution, easy to implement)

The first modification required is to create a function inside the client core called load_embedded_extensions. this should be called only once inside the meterpreter session bootstrap right before loading the AutoLoadExtensions. The goal of this function is to iterate through all the possible extension and determinate which one are already present inside the session, if they are, the client side of the extension is loaded, at this point we proceed with the normal meterpreter bootstrap flow.

To implement this solution we should modify the config generation logic for meterpreter to raise an error when the user tries to embedd a stdapi_ extension (a namespaced stdapi), in this way, when we are inside load_embedded_extensions we know we have the full stdapi already in memory if we find any command registered.

then we need to modify the cmd_load and the use (function inside the client core), to ensure we can load only stdapi_ extensions along side each-others and not load stdapi and later on stdapi_ and vice-versa.

Solution 2 - Change commands ID for stdapi (breaking-change solution, more elegant, long term)

Instead of running around the issue of the shared EXTENSION ID, we modify the stdapi command id convetion introducing a namespace id, something like

#define STDAPI_EXTENSION_ID 0x1000
#define STDAPI_EXTENSION_FS_ID 0x0100
#define STDAPI_EXTENSION_NET_ID 0x0200
...

#define STDAPI_CMD_FS_LS STDAPI_EXTENSION_ID | STDAPI_EXTENSION_FS_ID + 1
...
#define STDAPI_CMD_NET_IFCONFIG STDAPI_EXTENSION_ID | STDAPI_EXTENSION_NET_ID + 1

In this way we can run ENUMEXTCMD to start with (STDAPI_EXTENION_ID + NAMESOACE_ID) with a length of (0x0100) and enumerate the namespace specific functions. we can also define a STDAPI_CMD_IS_FULL_EXTENSION that is present only in the complete version of stdapi. this changes require modify all the other meterpreters.

I would recommend the second option if we wanted to switch this to the 6.5 branch as a breaking change. It would keep things better organized and be a better change long term. You'll still have an issue figuring out what extensions are loaded though, the grouping of the IDs will only simply the logic so far.

Take the following pseudo code for consideration as an example

def get_loaded_extensions
  extensions = [] # populate with the others as we do now, extapi, espia, etc.

  extensions.delete('stdapi') # process this as a special case
  
  stdapi_subextensions_for_platform = get_stdapi_subextensions_for_platform
  if stdapi_subextensions_for_platform.empty?
    # if there are no subextensions, then we can simply assume that stdapi is loaded if there is *any* overlap
    extensions << 'stdapi' unless (command_ids.to_set & STDAPI_EXTENSION_ID.upto(STDAPI_EXTENSION_ID + EXTENSION_RANGE - 1).to_set).empty?
  else
    stdapi_subextensions_for_platform.each do |subextension_name|
      # add the subextension if the commands overlap with any of it's commands
      extensions << subextension_name unless (command_ids.to_set & get_subextension_ids(subextension_name)).empty?
    end
    

    if (extensions.to_set & stdapi_subextensions_for_platform.to_set) == stdapi_subextensions_for_platform.to_set
       # if *all* subextensions are loaded, remove them and declare that 'stdapi' is loaded
       extensions -= stdapi_subextensions_for_platform
       extensions << 'stdapi'
    end
  end
end

def get_subextension_ids(subextension_name)
  # this abstraction allows the IDs to either be hardcoded as they are now so we can maintain backwards compatibility
  return HARDCODED_SUBIDS[subextension_name]

  # or use the range if they're grouped together
  range_start = { 'stdapi-fs' => STDAPI_FS_EXTENSION_ID }.fetch(subextension_name)
  return range_start.upto(range_start + RANGE - 1).to_a
end

def get_stdapi_subextensions_for_platform
  # return the subextensions based on the platform
  return [] if platform != 'windows'

  return ['fs', 'registry', 'railgun', etc.] # enumerate this from the files we have on disk with the `Metasploit::Payloads` gem
end

With that in place you should have all of the logic for all of the Meterpreters to identify when one of the subextensions is loaded, stdapi isn't loaded at all, all of the subextensions are loaded or the combined stdapi is loaded. This does assume however that if all of the subextensions are loaded, that it's functionally equivalent to having the main stdapi extension loaded.

@bwatters-r7
Copy link
Contributor

[*] Sending payload to 10.5.132.134 (curl/7.55.1)
[*] Sending stage (230982 bytes) to 10.5.132.134
[*] Meterpreter session 1 opened (10.5.135.201:4564 -> 10.5.132.134:50443) at 2025-10-06 14:23:13 -0500

msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > sessions -i -1
[*] Starting interaction with 1...

meterpreter > sysinfo
[-] The "sysinfo" command requires the "stdapi_ui" extension to be loaded (run: `load stdapi_ui`)
meterpreter > stdapi_ui
[-] Unknown command: stdapi_ui. Run the help command for more details.
meterpreter > load stdapi_ui
Loading extension stdapi_ui...Success.
meterpreter > sysinfo
Computer        : WIN10_2004_8D28
OS              : Windows 10 2004 (10.0 Build 19041).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows
meterpreter > ifconfig
[-] The "ifconfig" command is not supported by this Meterpreter type (x64/windows)
meterpreter > 

I cannot help but notice that sysinfo is now in stdapi_ui and not stdapi_sys, and apparently, ifconfig is no longer supported?

@dledda-r7
Copy link
Contributor Author

dledda-r7 commented Oct 7, 2025

The suggestions are not correct in general. probably is better to remove stdapi_ extension from the suggestion. The issue we have probably can be fixed with some breaking change that will require some work for 6.5. I will make it in a way to allow only stdapi to be suggested. at least until we change some IDs, the ifconfig is part of the stdapi_net namespace so I am not sure why is suggesting ui....

msf payload(windows/x64/meterpreter_reverse_http) > generate -f exe -o ~/Public/metsrv.x64.exe
[*] Writing 238592 bytes to ~/Public/metsrv.x64.exe...
msf payload(windows/x64/meterpreter_reverse_http) > 
[!] http://192.168.136.136:8080 handling request from 192.168.136.138; (UUID: 2txhzvcy) Without a database connected that payload UUID tracking will not work!
[*] http://192.168.136.136:8080 handling request from 192.168.136.138; (UUID: 2txhzvcy) Redirecting stageless connection from /rzwNN4p02sjw6PHqmAwlegUPfjNTnlB2kqoLdg_Afsrpv195-u4lIti27BHOXDIRPA_e with UA 'Mozilla/5.0 (iPad; CPU OS 17_7_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Mobile/15E148 Safari/604.1'
[!] http://192.168.136.136:8080 handling request from 192.168.136.138; (UUID: 2txhzvcy) Without a database connected that payload UUID tracking will not work!
[*] http://192.168.136.136:8080 handling request from 192.168.136.138; (UUID: 2txhzvcy) Attaching orphaned/stageless session...
[!] http://192.168.136.136:8080 handling request from 192.168.136.138; (UUID: 2txhzvcy) Without a database connected that payload UUID tracking will not work!
[*] Meterpreter session 1 opened (192.168.136.136:8080 -> 192.168.136.138:56830) at 2025-10-07 04:55:46 -0400

msf payload(windows/x64/meterpreter_reverse_http) > sessions -i -1
[*] Starting interaction with 1...

meterpreter > ls
[-] The "ls" command requires the "stdapi" extension to be loaded (run: `load stdapi`)
meterpreter > ifconfig
[-] The "ifconfig" command requires the "stdapi" extension to be loaded (run: `load stdapi`)
meterpreter > load stdapi_fs
Loading extension stdapi_fs...Success.
meterpreter > ls
Listing: C:\Users\User\Desktop
==============================

Mode              Size    Type  Last modified              Name
----              ----    ----  -------------              ----
100666/rw-rw-rw-  282     fil   2025-06-04 14:58:16 -0400  desktop.ini
100777/rwxrwxrwx  238592  fil   2025-10-06 11:38:20 -0400  m.exe
100666/rw-rw-rw-  1345    fil   2025-08-22 09:09:56 -0400  shellcode.c
100666/rw-rw-rw-  1756    fil   2025-07-10 12:26:26 -0400  x32dbg.lnk
100666/rw-rw-rw-  1756    fil   2025-07-10 12:26:26 -0400  x64dbg.lnk

meterpreter > ifconfig
[-] The "ifconfig" command requires the "stdapi" extension to be loaded (run: `load stdapi`)
meterpreter > load stdapi_net
Loading extension stdapi_net...Success.
meterpreter > ifconfig

Interface  1
============
Name         : Software Loopback Interface 1
Hardware MAC : 00:00:00:00:00:00
MTU          : 4294967295
IPv4 Address : 127.0.0.1
IPv4 Netmask : 255.0.0.0
IPv6 Address : ::1
IPv6 Netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff


Interface 11
============
Name         : Intel(R) 82574L Gigabit Network Connection
Hardware MAC : 00:0c:29:48:c8:ba
MTU          : 1500
IPv4 Address : 192.168.136.138
IPv4 Netmask : 255.255.255.0
IPv6 Address : fe80::c04f:4b12:84b4:92f7
IPv6 Netmask : ffff:ffff:ffff:ffff::

meterpreter > enumdesktops
[-] The "enumdesktops" command requires the "stdapi" extension to be loaded (run: `load stdapi`)
meterpreter > load stdapi_ui
Loading extension stdapi_ui...Success.
meterpreter > enumdesktops 
Enumerating all accessible desktops

Desktops
========

    Session  Station             Name
    -------  -------             ----
    1        WinSta0             Default
    1        Service-0x0-16e7d$  sbox_alternate_desktop_0x203C

meterpreter > 

@bwatters-r7
Copy link
Contributor

msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > sessions -i -1
[*] Starting interaction with 1...

meterpreter > sysinfo
[-] The "sysinfo" command requires the stdapi extension to be loaded or the relative subcomponent (run: `load stdapi` or `load stdapi_audio/_fs/_net/_sys/_railgun/_ui/_webcam`)
meterpreter > load stdapi_sys
Loading extension stdapi_sys...Success.
meterpreter > sysinfo
Computer        : WIN10_22H2_7FD2
OS              : Windows 10 22H2+ (10.0 Build 19045).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows
meterpreter > ifconfig
[-] The "ifconfig" command requires the stdapi extension to be loaded or the relative subcomponent (run: `load stdapi` or `load stdapi_audio/_fs/_net/_sys/_railgun/_ui/_webcam`)
meterpreter > load stdapi_net
Loading extension stdapi_net...Success.
meterpreter > ifconfig

Interface  1
============
Name         : Software Loopback Interface 1
Hardware MAC : 00:00:00:00:00:00
MTU          : 4294967295
IPv4 Address : 127.0.0.1
IPv4 Netmask : 255.0.0.0
IPv6 Address : ::1
IPv6 Netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff


Interface  4
============
Name         : Microsoft Hyper-V Network Adapter
Hardware MAC : 00:15:5d:87:6c:84
MTU          : 1500
IPv4 Address : 10.5.132.122
IPv4 Netmask : 255.255.255.0
IPv6 Address : fe80::9f82:eee0:e81e:199a
IPv6 Netmask : ffff:ffff:ffff:ffff::

meterpreter > 


@bwatters-r7
Copy link
Contributor

image

Bump Payloads version again
@bwatters-r7
Copy link
Contributor

msf payload(cmd/windows/http/x64/meterpreter/reverse_tcp) > sessions -i -1
[*] Starting interaction with 1...

meterpreter > sysinfo
[-] The "sysinfo" command requires the stdapi extension to be loaded or the relative subcomponent (run: `load stdapi` or `load stdapi_audio/_fs/_net/_sys/_railgun/_ui/_webcam`)
meterpreter > load stdapi_sys 
Loading extension stdapi_sys...Success.
meterpreter > sysinfo
Computer        : WIN10_22H2_7FD2
OS              : Windows 10 22H2+ (10.0 Build 19045).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows
meterpreter > ifconfig
[-] The "ifconfig" command requires the stdapi extension to be loaded or the relative subcomponent (run: `load stdapi` or `load stdapi_audio/_fs/_net/_sys/_railgun/_ui/_webcam`)
meterpreter > load stdapi
[-] Partial extension of stdapi has already been loaded.
meterpreter > load stdapi_net
Loading extension stdapi_net...Success.
meterpreter > ifconfig

Interface  1
============
Name         : Software Loopback Interface 1
Hardware MAC : 00:00:00:00:00:00
MTU          : 4294967295
IPv4 Address : 127.0.0.1
IPv4 Netmask : 255.0.0.0
IPv6 Address : ::1
IPv6 Netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff


Interface  4
============
Name         : Microsoft Hyper-V Network Adapter
Hardware MAC : 00:15:5d:87:6c:84
MTU          : 1500
IPv4 Address : 10.5.132.122
IPv4 Netmask : 255.255.255.0
IPv6 Address : fe80::9f82:eee0:e81e:199a
IPv6 Netmask : ffff:ffff:ffff:ffff::

meterpreter > 

@bwatters-r7 bwatters-r7 merged commit 10d1f53 into rapid7:master Oct 8, 2025
61 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in Metasploit Kanban Oct 8, 2025
@bwatters-r7
Copy link
Contributor

Release Notes

Splits the stdapi extension into subcomponents that can be loaded individually to lessen the footprint of the Windows Meterpreter Payload in memory. The original stdapi extension can still be loaded as well, and the default behavior to autoload stdapi when the Windows Meterpreter payload is loaded remains unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
payload rn-payload-enhancement release notes for enhanced payloads
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

5 participants