Files
metasploit-gs/lib/msf/core/db_export.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

549 lines
17 KiB
Ruby
Raw Normal View History

# -*- coding: binary -*-
module Msf
##
#
# This class provides export capabilities
#
##
2020-09-22 02:56:51 +01:00
class DBExport
attr_accessor :workspace
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
STATUS_START = "start"
STATUS_COMPLETE = "complete"
def initialize(workspace)
self.workspace = workspace
end
2013-08-30 16:28:33 -05:00
def myworkspace
self.workspace
2011-11-20 12:10:08 +11:00
end
2013-08-30 16:28:33 -05:00
def myusername
@username ||= (ENV['LOGNAME'] || ENV['USERNAME'] || ENV['USER'] || "unknown").to_s.strip.gsub(/[^A-Za-z0-9\x20]/n,"_")
end
2013-08-30 16:28:33 -05:00
# Hosts are always allowed. This is really just a stub.
def host_allowed?(arg)
true
end
2013-08-30 16:28:33 -05:00
2014-07-03 15:41:26 -05:00
# Performs an export of the workspace's `Metasploit::Credential::Login` objects in pwdump format
# @param path [String] the path on the local filesystem where the exported data will be written
2018-03-21 17:42:54 -05:00
# @return [String] The path to the location of the written file.
2014-07-03 15:41:26 -05:00
def to_pwdump_file(path, &block)
exporter = Metasploit::Credential::Exporter::Pwdump.new(workspace: workspace)
2013-08-30 16:28:33 -05:00
2018-03-06 15:15:27 -06:00
output_file = File.open(path, 'w') do |file|
2014-07-03 15:41:26 -05:00
file << exporter.rendered_output
end
2018-03-06 15:15:27 -06:00
output_file.path
end
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
# Performs an export of the workspace's `Metasploit::Credential::Login` objects in XML format
# @param path [String] the path on the local filesystem where the exported data will be written
# @return [String] The path to the location of the written file.
def to_xml_file(path, &block)
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
yield(:status, STATUS_START, "report") if block_given?
extract_target_entries
report_file = ::File.open(path, "wb")
2013-08-30 16:28:33 -05:00
report_file.write %Q|<?xml version="1.0" encoding="UTF-8"?>\n|
2014-07-08 12:03:32 -05:00
report_file.write %Q|<MetasploitV5>\n|
report_file.write %Q|<generated time="#{Time.now.utc}" user="#{myusername}" project="#{myworkspace.name.gsub(/[^A-Za-z0-9\x20]/n,"_")}" product="framework"/>\n|
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
yield(:status, STATUS_START, "hosts") if block_given?
report_file.write %Q|<hosts>\n|
report_file.flush
extract_host_info(report_file)
report_file.write %Q|</hosts>\n|
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
yield(:status, STATUS_START, "events") if block_given?
report_file.write %Q|<events>\n|
report_file.flush
extract_event_info(report_file)
report_file.write %Q|</events>\n|
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
yield(:status, STATUS_START, "services") if block_given?
report_file.write %Q|<services>\n|
report_file.flush
extract_service_info(report_file)
report_file.write %Q|</services>\n|
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
yield(:status, STATUS_START, "web sites") if block_given?
report_file.write %Q|<web_sites>\n|
report_file.flush
extract_web_site_info(report_file)
report_file.write %Q|</web_sites>\n|
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
yield(:status, STATUS_START, "web pages") if block_given?
report_file.write %Q|<web_pages>\n|
report_file.flush
extract_web_page_info(report_file)
report_file.write %Q|</web_pages>\n|
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
yield(:status, STATUS_START, "web forms") if block_given?
report_file.write %Q|<web_forms>\n|
report_file.flush
extract_web_form_info(report_file)
2011-11-20 12:10:08 +11:00
report_file.write %Q|</web_forms>\n|
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
yield(:status, STATUS_START, "web vulns") if block_given?
report_file.write %Q|<web_vulns>\n|
report_file.flush
extract_web_vuln_info(report_file)
report_file.write %Q|</web_vulns>\n|
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
yield(:status, STATUS_START, "module details") if block_given?
report_file.write %Q|<module_details>\n|
report_file.flush
extract_module_detail_info(report_file)
report_file.write %Q|</module_details>\n|
2013-08-30 16:28:33 -05:00
2014-07-08 12:03:32 -05:00
report_file.write %Q|</MetasploitV5>\n|
report_file.flush
report_file.close
2013-08-30 16:28:33 -05:00
2018-03-21 17:42:54 -05:00
yield(:status, STATUS_COMPLETE, "report") if block_given?
2013-08-30 16:28:33 -05:00
2018-03-06 15:15:27 -06:00
report_file.path
end
2013-08-30 16:28:33 -05:00
# A convenience function that bundles together host, event, and service extraction.
def extract_target_entries
extract_host_entries
extract_event_entries
extract_service_entries
extract_note_entries
extract_vuln_entries
extract_web_entries
end
2013-08-30 16:28:33 -05:00
# Extracts all the hosts from a project, storing them in @hosts and @owned_hosts
def extract_host_entries
@owned_hosts = []
@hosts = myworkspace.hosts
@hosts.each do |host|
2016-06-30 15:42:53 -05:00
if host.notes.where(ntype: 'pro.system.compromise').first
@owned_hosts << host
end
end
end
2013-08-30 16:28:33 -05:00
# Extracts all events from a project, storing them in @events
def extract_event_entries
@events = myworkspace.events.order('created_at ASC')
end
2013-08-30 16:28:33 -05:00
# Extracts all services from a project, storing them in @services
def extract_service_entries
@services = myworkspace.services
end
2013-08-30 16:28:33 -05:00
# Extracts all credentials from a project, storing them in @creds
def extract_credential_entries
2014-07-07 15:44:29 -05:00
@creds = Metasploit::Credential::Core.with_logins.with_public.with_private.workspace_id(myworkspace.id)
end
2013-08-30 16:28:33 -05:00
# Extracts all notes from a project, storing them in @notes
def extract_note_entries
@notes = myworkspace.notes
end
2013-08-30 16:28:33 -05:00
# Extracts all vulns from a project, storing them in @vulns
def extract_vuln_entries
@vulns = myworkspace.vulns
end
2013-08-30 16:28:33 -05:00
# Extract all web entries, storing them in instance variables
def extract_web_entries
@web_sites = myworkspace.web_sites
2011-11-20 12:10:08 +11:00
@web_pages = myworkspace.web_pages
@web_forms = myworkspace.web_forms
@web_vulns = myworkspace.web_vulns
end
2013-08-30 16:28:33 -05:00
# Simple marshalling, for now. Can I use ActiveRecord::ConnectionAdapters::Quoting#quote
# directly? Is it better to just marshal everything and destroy readability? Howabout
# XML safety?
def marshalize(obj)
case obj
when String
obj.strip
when TrueClass, FalseClass, Float, Integer, Time
obj.to_s.strip
when BigDecimal
obj.to_s("F")
when NilClass
"NULL"
else
[Marshal.dump(obj)].pack("m").gsub(/\s+/,"")
end
end
2013-08-30 16:28:33 -05:00
2014-07-07 15:44:29 -05:00
def create_xml_element(key,value,skip_encoding=false)
2018-09-14 10:51:51 -04:00
tag = key.tr("_","-")
el = REXML::Element.new(tag)
if value
2014-07-07 15:44:29 -05:00
unless skip_encoding
data = marshalize(value)
2020-09-22 02:56:51 +01:00
data.force_encoding(::Encoding::BINARY) if data.respond_to?('force_encoding')
2014-07-07 15:44:29 -05:00
data.gsub!(/([\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xFF])/n){ |x| "\\x%.2x" % x.unpack("C*")[0] }
2014-07-08 09:40:15 -05:00
el << REXML::Text.new(data)
else
el << value
2014-07-07 15:44:29 -05:00
end
end
return el
end
2013-08-30 16:28:33 -05:00
# @note there is no single root element output by
# {#extract_module_detail_info}, so if calling {#extract_module_detail_info}
# directly, it is the caller's responsibility to add an opening and closing
# tag to report_file around the call to {#extract_module_detail_info}.
#
2013-04-26 13:14:38 -05:00
# Writes a module_detail element to the report_file for each
# Mdm::Module::Detail.
#
# @param report_file [#write, #flush] IO stream to which to write the
# module_detail elements.
# @return [void]
def extract_module_detail_info(report_file)
2013-05-09 13:25:26 -05:00
Mdm::Module::Detail.all.each do |m|
report_file.write("<module_detail>\n")
#m_id = m.attributes["id"]
2013-08-30 16:28:33 -05:00
# Module attributes
m.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n") # Not checking types
end
2013-08-30 16:28:33 -05:00
# Authors sub-elements
# @todo https://www.pivotaltracker.com/story/show/48451001
report_file.write(" <module_authors>\n")
2015-01-20 17:06:51 -06:00
m.authors.each do |d|
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
2013-03-07 18:20:08 -06:00
end
end
report_file.write(" </module_authors>\n")
2013-08-30 16:28:33 -05:00
# Refs sub-elements
# @todo https://www.pivotaltracker.com/story/show/48451001
report_file.write(" <module_refs>\n")
2015-01-20 17:06:51 -06:00
m.refs.each do |d|
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
2013-03-07 18:20:08 -06:00
end
end
report_file.write(" </module_refs>\n")
2013-08-30 16:28:33 -05:00
# Archs sub-elements
# @todo https://www.pivotaltracker.com/story/show/48451001
report_file.write(" <module_archs>\n")
2015-01-20 17:06:51 -06:00
m.archs.each do |d|
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
2013-03-07 18:20:08 -06:00
end
end
report_file.write(" </module_archs>\n")
2013-08-30 16:28:33 -05:00
# Platforms sub-elements
# @todo https://www.pivotaltracker.com/story/show/48451001
report_file.write(" <module_platforms>\n")
2015-01-20 17:06:51 -06:00
m.platforms.each do |d|
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
2013-03-07 18:20:08 -06:00
end
end
report_file.write(" </module_platforms>\n")
2013-08-30 16:28:33 -05:00
# Targets sub-elements
# @todo https://www.pivotaltracker.com/story/show/48451001
report_file.write(" <module_targets>\n")
2015-01-20 17:06:51 -06:00
m.targets.each do |d|
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
2013-03-07 18:20:08 -06:00
end
end
report_file.write(" </module_targets>\n")
2013-08-30 16:28:33 -05:00
# Actions sub-elements
# @todo https://www.pivotaltracker.com/story/show/48451001
report_file.write(" <module_actions>\n")
2015-01-20 17:06:51 -06:00
m.actions.each do |d|
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
2013-03-07 18:20:08 -06:00
end
end
report_file.write(" </module_actions>\n")
2013-08-30 16:28:33 -05:00
# Mixins sub-elements
# @todo https://www.pivotaltracker.com/story/show/48451001
report_file.write(" <module_mixins>\n")
2015-01-20 17:06:51 -06:00
m.mixins.each do |d|
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
2013-03-07 18:20:08 -06:00
end
end
report_file.write(" </module_mixins>\n")
2013-08-30 16:28:33 -05:00
report_file.write("</module_detail>\n")
end
report_file.flush
end
2013-08-30 16:28:33 -05:00
# ActiveRecord's to_xml is easy and wrong. This isn't, on both counts.
def extract_host_info(report_file)
@hosts.each do |h|
report_file.write(" <host>\n")
host_id = h.attributes["id"]
2013-08-30 16:28:33 -05:00
# Host attributes
h.attributes.each_pair do |k,v|
# Convert IPAddr -> String
v = v.to_s if k == 'address'
el = create_xml_element(k,v)
report_file.write(" #{el}\n") # Not checking types
end
2013-08-30 16:28:33 -05:00
# Host details sub-elements
report_file.write(" <host_details>\n")
2015-01-20 17:06:51 -06:00
h.host_details.each do |d|
report_file.write(" <host_detail>\n")
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
2013-03-07 18:20:08 -06:00
report_file.write(" </host_detail>\n")
end
report_file.write(" </host_details>\n")
2013-08-30 16:28:33 -05:00
# Host exploit attempts sub-elements
report_file.write(" <exploit_attempts>\n")
2015-01-20 17:06:51 -06:00
h.exploit_attempts.each do |d|
report_file.write(" <exploit_attempt>\n")
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
2013-03-07 18:20:08 -06:00
end
report_file.write(" </exploit_attempt>\n")
end
report_file.write(" </exploit_attempts>\n")
2013-08-30 16:28:33 -05:00
# Service sub-elements
report_file.write(" <services>\n")
2015-01-15 15:07:18 -06:00
@services.where(host_id: host_id).each do |e|
report_file.write(" <service>\n")
e.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
report_file.write(" </service>\n")
end
report_file.write(" </services>\n")
2013-08-30 16:28:33 -05:00
# Notes sub-elements
report_file.write(" <notes>\n")
2015-01-15 15:07:18 -06:00
@notes.where(host_id: host_id).each do |e|
report_file.write(" <note>\n")
e.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
report_file.write(" </note>\n")
end
report_file.write(" </notes>\n")
2013-08-30 16:28:33 -05:00
# Vulns sub-elements
report_file.write(" <vulns>\n")
2015-01-15 15:07:18 -06:00
@vulns.where(host_id: host_id).each do |e|
report_file.write(" <vuln>\n")
e.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
2013-08-30 16:28:33 -05:00
2015-02-24 12:17:44 -06:00
# Notes attached to vulns instead of the host
report_file.write(" <notes>\n")
@notes.where(vuln_id: e.id).each do |note|
report_file.write(" <note>\n")
note.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
report_file.write(" </note>\n")
end
report_file.write(" </notes>\n")
# References
report_file.write(" <refs>\n")
e.refs.each do |ref|
el = create_xml_element("ref",ref.name)
report_file.write(" #{el}\n")
end
report_file.write(" </refs>\n")
2013-08-30 16:28:33 -05:00
# Vuln details sub-elements
report_file.write(" <vuln_details>\n")
2015-01-20 17:06:51 -06:00
e.vuln_details.each do |d|
report_file.write(" <vuln_detail>\n")
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
2013-03-07 18:20:08 -06:00
report_file.write(" </vuln_detail>\n")
end
report_file.write(" </vuln_details>\n")
2013-08-30 16:28:33 -05:00
# Vuln attempts sub-elements
report_file.write(" <vuln_attempts>\n")
2015-01-20 17:06:51 -06:00
e.vuln_attempts.each do |d|
report_file.write(" <vuln_attempt>\n")
d.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
2013-03-07 18:20:08 -06:00
report_file.write(" </vuln_attempt>\n")
end
report_file.write(" </vuln_attempts>\n")
2013-08-30 16:28:33 -05:00
report_file.write(" </vuln>\n")
end
report_file.write(" </vulns>\n")
2013-08-30 16:28:33 -05:00
report_file.write(" </host>\n")
end
2011-11-20 12:10:08 +11:00
report_file.flush
end
2013-08-30 16:28:33 -05:00
# Extract event data from @events
def extract_event_info(report_file)
@events.each do |e|
report_file.write(" <event>\n")
e.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
report_file.write(" </event>\n")
report_file.write("\n")
end
report_file.flush
end
2013-08-30 16:28:33 -05:00
# Extract service data from @services
def extract_service_info(report_file)
@services.each do |e|
report_file.write(" <service>\n")
e.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
report_file.write(" </service>\n")
report_file.write("\n")
end
report_file.flush
end
2013-08-30 16:28:33 -05:00
# Extract service data from @services
def extract_service_info(report_file)
@services.each do |e|
report_file.write(" <service>\n")
e.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
report_file.write(" </service>\n")
report_file.write("\n")
end
report_file.flush
end
2013-08-30 16:28:33 -05:00
# Extract web site data from @web_sites
def extract_web_site_info(report_file)
@web_sites.each do |e|
report_file.write(" <web_site>\n")
e.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
end
2013-08-30 16:28:33 -05:00
site = e
el = create_xml_element("host", site.service.host.address)
report_file.write(" #{el}\n")
2013-08-30 16:28:33 -05:00
2011-11-20 12:10:08 +11:00
el = create_xml_element("port", site.service.port)
report_file.write(" #{el}\n")
2013-08-30 16:28:33 -05:00
2011-11-20 12:10:08 +11:00
el = create_xml_element("ssl", site.service.name == "https")
report_file.write(" #{el}\n")
2013-08-30 16:28:33 -05:00
report_file.write(" </web_site>\n")
end
report_file.flush
end
2013-08-30 16:28:33 -05:00
2011-11-20 12:10:08 +11:00
# Extract web pages, forms, and vulns
def extract_web_info(report_file, tag, entries)
entries.each do |e|
report_file.write(" <#{tag}>\n")
e.attributes.each_pair do |k,v|
el = create_xml_element(k,v)
report_file.write(" #{el}\n")
2011-11-20 12:10:08 +11:00
end
2013-08-30 16:28:33 -05:00
site = e.web_site
el = create_xml_element("vhost", site.vhost)
report_file.write(" #{el}\n")
2013-08-30 16:28:33 -05:00
el = create_xml_element("host", site.service.host.address)
report_file.write(" #{el}\n")
2013-08-30 16:28:33 -05:00
2011-11-20 12:10:08 +11:00
el = create_xml_element("port", site.service.port)
report_file.write(" #{el}\n")
2013-08-30 16:28:33 -05:00
2011-11-20 12:10:08 +11:00
el = create_xml_element("ssl", site.service.name == "https")
report_file.write(" #{el}\n")
2013-08-30 16:28:33 -05:00
report_file.write(" </#{tag}>\n")
end
report_file.flush
end
2013-08-30 16:28:33 -05:00
# Extract web pages
def extract_web_page_info(report_file)
extract_web_info(report_file, "web_page", @web_pages)
end
2013-08-30 16:28:33 -05:00
# Extract web forms
def extract_web_form_info(report_file)
extract_web_info(report_file, "web_form", @web_forms)
end
2013-08-30 16:28:33 -05:00
# Extract web vulns
def extract_web_vuln_info(report_file)
extract_web_info(report_file, "web_vuln", @web_vulns)
2011-11-20 12:10:08 +11:00
end
end
end