The RPC API enables you to programmatically drive the Metasploit Framework and commercial products using HTTP-based remote procedure call (RPC) services. An RPC service is a collection of message types and remote methods that provide a structured way for external applications to interact with web applications. You can use the RPC interface to locally or remotely execute Metasploit commands to perform basic tasks like running modules, communicating with the database, interacting with sessions, exporting data, and generating reports.
The Metasploit products are written primarily in Ruby, which is the easiest way to use the remote API. However, in addition to Ruby, any language with support for HTTPS and MessagePack, such as Python, Java, and C, can be used to take advantage of the RPC API.
There are currently two implementations of Metasploit’s RPC:
- HTTP and messagepack - covered by a separate guide
- HTTP and JSON - covered by this guide
Note that both the messagepack and JSON RPC services provide very similar operations, and it is worth reviewing both documents.
Starting the JSON API Server
The pre-requisite to running the JSON API Server is to run your Metasploit database. This can be initialized with msfdb
. Note that msfdb
will ask if you wish to run the JSON RPC web service - but it is not required for this guide which shows how to run the JSON service directly with thin or Puma:
First run the Metasploit database:
msfdb init
After configuring the database the JSON RPC service can be initialized with the thin Ruby web server:
bundle exec thin --rackup msf-json-rpc.ru --address 0.0.0.0 --port 8081 --environment production --tag msf-json-rpc start
Or with Puma:
bundle exec puma msf-json-rpc.ru --port 8081 --environment production --tag msf-json-rpc start
Development
If you are wanting to develop or debug the Ruby implementation of the JSON RPC service - it can be useful to run the Metasploit API synchronously in the foreground. This allows for console logs to appear directly in the terminal, as well as being able to interact with breakpoints via require 'pry-byebug'; binding.pry
:
It is possible to debug Msfconsole’s webservice component too:
bundle exec ruby ./msfdb reinit
bundle exec ruby ./msfdb --component webservice stop
bundle exec ruby ./msfdb --component webservice --no-daemon start
RPC Logging
You can configure the RPC service logging with the MSF_WS_DATA_SERVICE_LOGGER
environment variable.
The list of supported loggers is viewable with msfconsole --help
. The list at the time of writing is:
- Stdout / Stderr / StdoutWithoutTimestamps - Write logs to stdout/stderr
- Flatfile / TimestampColorlessFlatfile - Write logs to
~/.msf4/logs
Example usage:
$ MSF_WS_DATA_SERVICE_LOGGER=Stdout bundle exec thin --rackup msf-json-rpc.ru --address localhost --port 8081 --environment production --tag msf-json-rpc start
[11/25/2020 17:34:53] [e(0)] core: Dependency for windows/encrypted_shell_reverse_tcp is not supported
[11/25/2020 17:34:53] [e(0)] core: Dependency for windows/x64/encrypted_shell_reverse_tcp is not supported
[11/25/2020 17:34:53] [e(0)] core: Dependency for windows/encrypted_reverse_tcp is not supported
[11/25/2020 17:34:53] [e(0)] core: Dependency for windows/x64/encrypted_reverse_tcp is not supported
[11/25/2020 17:34:54] [e(0)] core: Unable to load module /Users/adfoster/Documents/code/metasploit-framework/modules/auxiliary/gather/office365userenum.py - LoadError Try running file manually to check for errors or dependency issues.
Thin web server (v1.7.2 codename Bachmanity)
Maximum connections set to 1024
Listening on localhost:8081, CTRL+C to stop
[11/25/2020 17:35:17] [d(0)] core: Already established connection to postgresql, so reusing active connection.
[11/25/2020 17:35:17] [e(0)] core: DB.connect threw an exception - ActiveRecord::AdapterNotSpecified database configuration does not specify adapter
[11/25/2020 17:35:17] [e(0)] core: Failed to connect to the database: database configuration does not specify adapter```
Concepts
The Metasploit RPC aims to follow the jsonrpc specification. Therefore:
- Each JSON RPC request should provide a unique message ID which the client and server can use to correlate requests and responses
- Metasploit may return the following error codes.
Examples
First ensure you are running the Metasploit database, and are running the JSON service before running these examples
Querying
Query DB status
Request:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "db.status",
"id": 1,
"params": []
}'
Response:
{
"jsonrpc": "2.0",
"result": {
"driver": "postgresql",
"db": "msf"
},
"id": 1
}
Query workspaces
Request:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "db.workspaces",
"id": 1,
"params": []
}'
Response:
{
"jsonrpc": "2.0",
"result": {
"workspaces": [
{
"id": 1,
"name": "default",
"created_at": 1673368954,
"updated_at": 1673368954
}
]
},
"id": 1
}
Modules workflow
Search for modules
Request:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'content-type: application/json' \
--data '{ "jsonrpc": "2.0", "method": "module.search", "id": 1, "params": ["psexec author:egypt arch:x64"] }'
Response:
{
"jsonrpc": "2.0",
"result": [
{
"type": "exploit",
"name": "PsExec via Current User Token",
"fullname": "exploit/windows/local/current_user_psexec",
"rank": "excellent",
"disclosuredate": "1999-01-01"
}
],
"id": 1
}
Run module check methods
Metasploit modules support running check
methods which can be used to identify the success of an exploit module, or to run an auxiliary module against a target. For instance, with an Auxiliary module check request:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "module.check",
"id": 1,
"params": [
"auxiliary",
"auxiliary/scanner/ssl/openssl_heartbleed",
{
"RHOST": "192.168.123.13"
}
]
}'
Or an Exploit module check request:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'content-type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "module.check",
"id": 1,
"params": [
"exploit",
"exploit/windows/smb/ms17_010_eternalblue",
{
"RHOST": "192.168.123.13"
}
]
}'
The response will contain an identifier which can be used to query for updates:
{
"jsonrpc": "2.0",
"result": {
"job_id": 0,
"uuid": "1MIqJ5lViZHSOuaWf1Zz1lpR"
},
"id": 1
}
query all running stats
Request:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "module.running_stats",
"id": 1,
"params": []
}'
The response will include the following keys:
- waiting - modules that are queued up, but have not started to run yet
- running - currently running modules
- results - the module has completed or failed, and the results can be retrieved and acknowledged
Response:
{
"jsonrpc": "2.0",
"result": {
"waiting": [
"NkJvf4kp4JxcuFCz7rjSuHL1",
"wRnMQuJ8gzMTp5CaHu18bHdV"
],
"running": [
"b7hIX6G4ZtwvRVRDOXk5ylSx",
"gx9xTEi6KlH5LJHauyhrHTBn",
],
"results": [
"1MIqJ5lViZHSOuaWf1Zz1lpR",
"IN5PwYXrjqKfuekQt8cyCENK",
"Spd1xfgsCZXQABNh7UA3uB58",
"nRQw0bEvhFcXF0AxtVYOpQku"
]
},
"id": 1
}
retrieve module results
It is possible to poll for module results using the id returned when running a module.
Request:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "module.results",
"id": 1,
"params": ["0L37lfcIQqyRK9aBTIVJB4H3"]
}'
Example response when the module is has not yet complete:
{
"jsonrpc": "2.0",
"result": {
"status": "running"
},
"id": 1
}
Example error response:
{
"jsonrpc": "2.0",
"result": {
"status": "errored",
"error": "The connection with (192.168.123.13:443) timed out."
},
"id": 1
}
Example success response:
{
"jsonrpc": "2.0",
"result": {
"status": "completed",
"result": {
"code": "vulnerable",
"message": "The target is vulnerable.",
"reason": null,
"details": {
"os": "Windows 7 Enterprise 7601 Service Pack 1",
"arch": "x64"
}
}
},
"id": 1
}
acknowledge module results
This command will also allow Metasploit to remove the result resources from memory. Not acknowledging module results will lead to a memory leak, but the memory is limited to 35mb as the memory datastore used is implemented by ActiveSupport::Cache::MemoryStore
Request:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "module.ack",
"id": 1,
"params": ["nRQw0bEvhFcXF0AxtVYOpQku"]
}'
Response:
{
"jsonrpc": "2.0",
"result": {
"success": true
},
"id": 1
}
Analyzing hosts workflow
Metasploit supports an analyze
command which suggests modules to run based on what a user has already learned and stored about a host. First report a host:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "db.report_host",
"id": 1,
"params": [
{
"workspace": "default",
"host": "10.0.0.1",
"state": "alive",
"os_name": "Windows",
"os_flavor": "Enterprize",
"os_sp": "SP2",
"os_lang": "English",
"arch": "ARCH_X86",
"mac": "97-42-51-F2-A7-A7",
"scope": "eth2",
"virtual_host": "VMWare"
}
]
}'
# response: {"jsonrpc":"2.0","result":{"result":"success"},"id":1}
Report the host vulnerabilities:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "db.report_vuln",
"id": 1,
"params": [
{
"workspace": "default",
"host": "10.0.0.1",
"name": "Exploit Name",
"info": "Human readable description of the vuln",
"refs": [
"CVE-2017-0143",
"CVE-2017-0144",
"CVE-2017-0145",
"CVE-2017-0146",
"CVE-2017-0147",
"CVE-2017-0148"
]
}
]
}'
# response: {"jsonrpc":"2.0","result":{"result":"success"},"id":1}
Run the analyze command:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "db.analyze_host",
"id": 1,
"params": [
{
"workspace": "default",
"host": "10.0.0.1"
}
]
}'
Response:
{
"jsonrpc": "2.0",
"result": {
"host": {
"address": "10.0.0.1",
"modules": [
{
"mtype": "exploit",
"mname": "exploit/windows/smb/ms17_010_eternalblue",
"state": "READY_FOR_TEST",
"description": "ready for testing",
"options": {
"invalid": [],
"missing": []
}
}
]
}
},
"id": 1
}
When analyzing a host, it is also possible to specify payload requirements for additional granularity:
curl --request POST \
--url http://localhost:8081/api/v1/json-rpc \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "db.analyze_host",
"id": 1,
"params": [
{
"workspace": "default",
"host": "10.0.0.1",
"payload": "payload/cmd/unix/reverse_bash"
}
]
}'