Last updated at Tue, 16 Jan 2024 16:39:52 GMT

The Metasploit RPC API provides a straightforward, programmatic way to accomplish basic tasks with your Metasploit Pro instance. Two of the key capabilities are export generation to backup your data and report generation to summarize and share your findings. The RPC API docs are currently undergoing a major overhaul and are a bit out of date for reports and export generation. This post will provide all the examples and configuration options you need to get running.

Setting up a client to make the API calls is simple:

# This class is defined under pro/api-example  
require_relative 'metasploit_rpc_client'  
client = MetasploitRPCClient.new(host:host, token:api_token, ssl:false, port:50505)  

Note that there are example scripts shipped with Metasploit Pro that show these examples and more. They can be found inside the install directory (on *nix systems /opt/metasploit) under apps/pro/api-example. They are simple wrappers that allow you to pass in required arguments, so good for getting a feel for things. In addition to the API calling code, however you implement that, you need to have the Metasploit Pro instance running and you need to generate an API key. This can be done from Administration -> Global Settings.

Reports

Listing existing reports

report_list displays all reports that have been generated in the workspace you specify:

report_list = client.call('pro.report_list', workspace_name)  
puts "Existing Reports: #{report_list}"  

Sample output:

Existing Reports: {7=>[{"id"=>6, "report_id"=>7, "file_path"=>"/Users/shuckins/rapid7/pro/reports/artifacts/CredentialMetaModule-20140912105153.pdf",   
"created_at"=>1410537159, "updated_at"=>1410537159, "accessed_at"=>nil, "workspace_id"=>2, "created_by"=>"shuckins", "report_type"=>"mm_auth", "file_size"=>34409}],  

The keys of the Hash are the report IDs, needed for download as will be seen below. The value Array contains all the artifacts that were generated. An artifact is simply a particular file in a particular format. For example, when you generate an Audit report and select file formats PDF, HTML, and Doc, this results in a single report with three child artifacts.

Getting information on available reports to generate

type_list = client.call('pro.list_report_types')  
puts "Allowed Report types: #{type_list}"  

Sample output (snipped, full output includes every report type):

Allowed Report types:  
{"activity"=>{"required_data"=>"tasks", "file_formats"=>"pdf, html, rtf", "options"=>"include_task_logs",  
"sections"=>"cover, project_summary, task_details", "report_directory"=>"/Users/shuckins/rapid7/pro/reports/activity/", "parent_template_file"=>"/Users/shuckins/rapid7/pro/reports/activity/main.jrxml"},  

Downloading a report (all child artifacts)

report_id = 1 # Get this from report_list call  
report = client.call('pro.report_download', report_id)  
report['report_artifacts'].each_with_index do |a, i|  
    tmp_path = "/tmp/report_test_#{i}_#{Time.now.to_i}#{File.extname(a['file_path'])}"  
    File.open(tmp_path, 'w') {|c| c.write a['data']}  
    puts "Wrote report artifact #{report_id} to #{tmp_path}"  
end  

This will download every artifact related to this report generation (1-4 files depending on format selection).

Downloading particular report artifacts

If you only want a particular artifact file under a report, you can download that using the artifact ID provided from the report_list call.

report_artifact_id = 1  
artifact = client.call('pro.report_artifact_download', report_artifact_id)  
tmp_path = "/tmp/report_#{report_artifact_id}#{File.extname(artifact['file_path'])}"  
File.open(tmp_path, 'w') {|c| c.write artifact['data']}  
puts "Wrote report artifact #{report_artifact_id} to #{tmp_path}"  

Generating a report

There are a number of options available for this call, detailed below. This basic version generates a single PDF artifact of the Audit report:

report_hash = {workspace: workspace_name,  
               name: "SuperTest_#{Time.now.to_i}",  
               report_type: :audit,  
               created_by: 'whoareyou',  
               file_formats: [:pdf]  
}  
report_creation = client.call('pro.start_report', report_hash)  
puts "Created report: #{report_creation}"  

There's currently no API call to provide report (or export) generation status. The time required depends entirely on your data size and complexity. One place to check for status is the reports.log file.

Configuration options

These are placed in the hash passed to the start_report call.

Required:

  • name: String, the name for the report shown in the web UI and in the file path; used in forming the filenames of the artifacts generated
  • report_type: String, must be one of those listed by list_report_types, e.g.: activity, audit, credentials, collected_evidence, compromised_hosts, custom, fisma, mm_auth, mm_pnd, mm_segment, pci, services, social_engineering, webapp_assessment
  • report_template: if type custom this can be set, String, full file path to custom Jasper jrxml template. If not a custom report, do not use this.
  • workspace_name: String, name of the workspace to which the report will be scoped
  • created_by: String, username to which the report should be attributed
  • file_formats: Array, the file format(s) of the artifacts to be generated. Must specify at least one. Available types vary slightly per report, 'pdf' is present for all. See list_report_types for formats per type.

Optional:

  • email_recipients: String, addresses to which the report artifact(s) should be emailed. Addresses can be separated with comma, semicolon, newlines, or spaces.
  • mask_credentials: Boolean, whether credentials shown in report artifacts should be scrubbed (replaced with MASKED)
  • included_addresses: String, space-separated addresses to include in the report. Can include wildcards, ranges, CIDR.
  • excluded_addresses: String, space-separated addresses to exclude from the report. Can include wildcards, ranges, CIDR. If included and excluded are both specified, they are both expanded and the address set used is included - excluded.
  • logo_path: String, full path to image file to use on cover page of report artifacts. If not specified, the Rapid7 logo is used. Must be of type: gif, png, jpg, or jpeg
  • options: sub hash of additional configuration options:
    • include_sessions: Boolean, whether information on sessions should be included in the report if applicable
    • include_charts: Boolean, whether graphs should be included in the report if applicable
    • include_page_code: Boolean, whether HTML code of pages in SE campaigns should be included in the report versus just an image preview of the rendered page
  • se_campaign_id: Integer, the ID of the SE campaign the report should cover. Only applied to SE report.
  • sections: Array, specific sections of the report to include. If this is specified only the specified sections will be included. If not specified all sections will be included. For section names, see list_report_types.
  • usernames_reported: String, comma-separated list of users to be included as active in the report. This is usually shown in the Executive summary section.

Exports

Export coverage is nearly identical to reports.

Listing existing exports

export_list = client.call('pro.export_list', workspace_name)  
puts "Existing Exports: #{export_list}"  

Generating an export

export_types = ['zip_workspace','xml','replay_scripts','pwdump’]  
# NOTE: If you are not on the latest update of 4.10 (4.10.0-2014092401) this requires workspace_id with integer value of ID.  
# If you've updated to this point you can use workspace with a string value of name as below.  
export_config = {created_by: 'whoareyou',  
                           export_type: export_types[1],  
                           workspace: 'ThePlace'}  
export_creation = client.call('pro.start_export', export_config)  
puts "Created export: #{export_creation}"  

Downloading a generated export

export_id = 1  
export = client.call('pro.export_download', export_id)  
tmp_path = "/tmp/export_test_#{export_id}#{File.extname(export['file_path'])}"  
File.open(tmp_path, 'w') {|c| c.write export['data']}  
puts "Wrote export #{export_id} to #{tmp_path}"  

Configuration options

Required:

  • created_by: String, username to which the export should be attributed
  • export_type: String, must be one of: zip_workspace, xml, replay_scripts, pwdump
  • workspace: String, name of the workspace to which export will be scoped

Optional:

  • name: String, the name for the export shown in the web UI and in the file path; unacceptable characters are changed to underscores or removed
  • mask_credentials: Boolean, whether credentials shown in XML and other files should be scrubbed (replaced with MASKED)
  • included_addresses: String, space-separated addresses to include in the export. Can include wildcards, ranges, CIDR.
  • excluded_addresses: String, space-separated addresses to exclude from the export. Can include wildcards, ranges, CIDR. If included and excluded are both specified, they are both expanded and the address set used is included – excluded.