PAYLOAD value from datastore is not populated via RPC
Background
Some modules / exploits define the preferred payload in their 'default_options', example: [https://github.com/rapid7/metasploit-framework/commit/2b90d33aefe0b01d52bce71bfd85f266e430f08a]
This information is not being sent our via the RPC interface, neither via the datastore (which isn't sent out) nor via the default_options variable that is created (via the module internal mechanism)
How to recreate:
Gather the options of the module: use exploit/linux/http/opennms_horizon_authenticated_rce via RPC
Note that compatible_payloads is empty (array is len 0), while if you use the msfconsole the PAYLOAD value of DefaultOptions is used, allowing you to know which payload to use
The problem is not specific to this module, all modules that use a DefaultOptions Payload will have a similar outcome
Potential solution in lib/msf/core/rpc/v10/rpc_module.rb add, under def rpc_info(mtype, mname), a check that verifies if PAYLOAD inside the datastore is present, if it is, pass it out
There is possible other values in the datastore that we may want to send back via the RPC to make it more accurate and useful.
Unfortunately, using rpc['datastore'] = m.datastore doesn't work (probably due to its size?)
Metasploit version
Framework: 6.4.1-dev- Console : 6.4.1-dev-
It seems that if you add to the above function rpc_info a simple call for:
res['PAYLOAD'] = m.datastore['PAYLOAD']
To cause the PAYLOAD to show up in the info of the module
Maybe an option is to add to lib/msf/core/exploit.rb:
if info.key? 'DefaultOptions'
print "DefaultOptions found in info\n"
self.default_options = info['DefaultOptions']
end
Inside the def initialize - and then we can reference this
I've also observed this issue for many modules; it would be really useful to have solved. For example, https://github.com/rapid7/metasploit-framework/blob/master//modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb.
The wfsdelay is 2 and the payload is null if the options are queried for this module through the RPC API.
Its not specific to the exploit I mentioned, just there it is evident if you compare RPC results and msfconsole results - due to lack of the payload values not being passed
I've confirmed there's some nuanced differences in the RPC layer in how options are handled versus a module's default options that are used to populate the datastore.
Just to confirm all the datapoints - which RPC calls are you making here in particular? Just rpc_info or others as well?
I am using pymetasploit3 (just for context) and the use for ExploitModule class
What it does, is call module.info, pulls all the _info information and places them in dicts
It then tries to get default_target for use with payload picking.
Subsequently, via the payloads function you can make a call to module.target_compatible_payloads which should return the relevant payloads for this module.
Some modules like exploit/linux/http/opennms_horizon_authenticated_rce return an empty array (empty payloads), see this small python snippet:
exploit_path = "linux/http/opennms_horizon_authenticated_rce"
client = MsfRpcClient(
metasploit_msfrpcd_password, port=55553, ssl=True
)
res = client.modules.use("exploit", exploit_path)
Hope this clarifies the problem I am seeing
Proposed fix: https://github.com/rapid7/metasploit-framework/pull/19086
I used via pymetasploit3 code that use all available exploits to confirm this has no effect beyond exposing the information (i.e. no crash)
from pymetasploit3.msfrpc import MsfRpcClient
client = MsfRpcClient(
metasploit_msfrpcd_password, port=55544 ssl=True
)
exploits = client.modules.search("type:exploit")
for exploit in exploits:
res = client.modules.use("exploit", exploit)
Merged